diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-06 00:53:35 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-06 00:53:35 +0000 |
commit | 69c6a41ffb878ef98c9378ed4b1634a404cfaa7f (patch) | |
tree | b2a4f704565d62fbb129ab9dc3b35977c50e6e7f /src | |
parent | Initial commit. (diff) | |
download | knot-69c6a41ffb878ef98c9378ed4b1634a404cfaa7f.tar.xz knot-69c6a41ffb878ef98c9378ed4b1634a404cfaa7f.zip |
Adding upstream version 2.7.6.upstream/2.7.6upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
478 files changed, 221722 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..247cea9 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,21 @@ +AM_CPPFLAGS = \ + -include $(top_builddir)/src/config.h \ + -DCONFIG_DIR='"${config_dir}"' \ + -DSTORAGE_DIR='"${storage_dir}"' \ + -DRUN_DIR='"${run_dir}"' \ + -DMODULE_DIR='"${module_dir}"' \ + -DMODULE_INSTDIR='"${module_instdir}"' + +EXTRA_DIST = +CLEANFILES = +BUILT_SOURCES = +lib_LTLIBRARIES = +noinst_LTLIBRARIES = +pkgconfig_DATA = + +include $(srcdir)/contrib/Makefile.inc +include $(srcdir)/libdnssec/Makefile.inc +include $(srcdir)/libknot/Makefile.inc +include $(srcdir)/libzscanner/Makefile.inc +include $(srcdir)/knot/Makefile.inc +include $(srcdir)/utils/Makefile.inc diff --git a/src/Makefile.in b/src/Makefile.in new file mode 100644 index 0000000..88f9f97 --- /dev/null +++ b/src/Makefile.in @@ -0,0 +1,5499 @@ +# Makefile.in generated by automake 1.15 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2014 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + + + + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +@HAVE_LMDB_FALSE@am__append_1 = $(pthread_LIBS) +@HAVE_LMDB_FALSE@am__append_2 = \ +@HAVE_LMDB_FALSE@ contrib/lmdb/lmdb.h \ +@HAVE_LMDB_FALSE@ contrib/lmdb/mdb.c \ +@HAVE_LMDB_FALSE@ contrib/lmdb/midl.c \ +@HAVE_LMDB_FALSE@ contrib/lmdb/midl.h + +@HAVE_LIBDNSTAP_TRUE@am__append_3 = libdnstap.la +@HAVE_LIBDNSTAP_TRUE@am__append_4 = $(nodist_libdnstap_la_SOURCES) +@HAVE_LIBDNSTAP_TRUE@am__append_5 = $(nodist_libdnstap_la_SOURCES) +@EXCLUDE_LIBS_LIBDNSSEC_TRUE@am__append_6 = $(LDFLAG_EXCLUDE_LIBS) +@ENABLE_PKCS11_TRUE@am__append_7 = $(pthread_LIBS) +@HAVE_DAEMON_TRUE@am__append_8 = libknotd.la +@HAVE_DAEMON_TRUE@am__append_9 = knotd.pc +@STATIC_MODULE_cookies_TRUE@am__append_10 = $(knot_modules_cookies_la_SOURCES) +@SHARED_MODULE_cookies_TRUE@am__append_11 = knot/modules/cookies.la +@STATIC_MODULE_dnsproxy_TRUE@am__append_12 = $(knot_modules_dnsproxy_la_SOURCES) +@SHARED_MODULE_dnsproxy_TRUE@am__append_13 = knot/modules/dnsproxy.la +@STATIC_MODULE_dnstap_TRUE@am__append_14 = $(knot_modules_dnstap_la_SOURCES) +@STATIC_MODULE_dnstap_TRUE@am__append_15 = $(DNSTAP_CFLAGS) +@STATIC_MODULE_dnstap_TRUE@am__append_16 = $(DNSTAP_LIBS) libdnstap.la +@SHARED_MODULE_dnstap_TRUE@am__append_17 = knot/modules/dnstap.la +@STATIC_MODULE_geoip_TRUE@am__append_18 = $(knot_modules_geoip_la_SOURCES) +@STATIC_MODULE_geoip_TRUE@am__append_19 = $(libmaxminddb_CFLAGS) +@STATIC_MODULE_geoip_TRUE@am__append_20 = $(libmaxminddb_LIBS) +@SHARED_MODULE_geoip_TRUE@am__append_21 = knot/modules/geoip.la +@STATIC_MODULE_noudp_TRUE@am__append_22 = $(knot_modules_noudp_la_SOURCES) +@SHARED_MODULE_noudp_TRUE@am__append_23 = knot/modules/noudp.la +@STATIC_MODULE_onlinesign_TRUE@am__append_24 = $(knot_modules_onlinesign_la_SOURCES) +@SHARED_MODULE_onlinesign_TRUE@am__append_25 = knot/modules/onlinesign.la +@STATIC_MODULE_queryacl_TRUE@am__append_26 = $(knot_modules_queryacl_la_SOURCES) +@SHARED_MODULE_queryacl_TRUE@am__append_27 = knot/modules/queryacl.la +@STATIC_MODULE_rrl_TRUE@am__append_28 = $(knot_modules_rrl_la_SOURCES) +@SHARED_MODULE_rrl_TRUE@am__append_29 = knot/modules/rrl.la +@STATIC_MODULE_stats_TRUE@am__append_30 = $(knot_modules_stats_la_SOURCES) +@SHARED_MODULE_stats_TRUE@am__append_31 = knot/modules/stats.la +@STATIC_MODULE_synthrecord_TRUE@am__append_32 = $(knot_modules_synthrecord_la_SOURCES) +@SHARED_MODULE_synthrecord_TRUE@am__append_33 = knot/modules/synthrecord.la +@STATIC_MODULE_whoami_TRUE@am__append_34 = $(knot_modules_whoami_la_SOURCES) +@SHARED_MODULE_whoami_TRUE@am__append_35 = knot/modules/whoami.la +@HAVE_LIBUTILS_TRUE@am__append_36 = libknotus.la +@HAVE_UTILS_TRUE@bin_PROGRAMS = kdig$(EXEEXT) khost$(EXEEXT) \ +@HAVE_UTILS_TRUE@ knsec3hash$(EXEEXT) knsupdate$(EXEEXT) \ +@HAVE_UTILS_TRUE@ $(am__EXEEXT_1) +@HAVE_DNSTAP_TRUE@@HAVE_UTILS_TRUE@am__append_37 = $(DNSTAP_LIBS) libdnstap.la +@HAVE_DNSTAP_TRUE@@HAVE_UTILS_TRUE@am__append_38 = $(DNSTAP_LIBS) libdnstap.la +@HAVE_DNSTAP_TRUE@@HAVE_UTILS_TRUE@am__append_39 = $(DNSTAP_CFLAGS) +@HAVE_DNSTAP_TRUE@@HAVE_UTILS_TRUE@am__append_40 = $(DNSTAP_CFLAGS) +@HAVE_DAEMON_TRUE@sbin_PROGRAMS = knotc$(EXEEXT) knotd$(EXEEXT) \ +@HAVE_DAEMON_TRUE@ $(am__EXEEXT_2) +@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@am__append_41 = kzonecheck +@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@am__append_42 = keymgr kjournalprint +subdir = src +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_cc_clang.m4 \ + $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_check_link_flag.m4 \ + $(top_srcdir)/m4/ax_compare_version.m4 \ + $(top_srcdir)/m4/code-coverage.m4 \ + $(top_srcdir)/m4/knot-lib-version.m4 \ + $(top_srcdir)/m4/knot-module.m4 $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/m4/sanitizer.m4 $(top_srcdir)/m4/visibility.m4 \ + $(top_srcdir)/m4/knot-version.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(include_libdnssec_HEADERS) \ + $(include_libknotd_HEADERS) $(include_libzscanner_HEADERS) \ + $(nobase_include_libknot_HEADERS) $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = config.h +CONFIG_CLEAN_FILES = knotd.pc libknot.pc libdnssec.pc libzscanner.pc +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)$(pkglibdir)" \ + "$(DESTDIR)$(bindir)" "$(DESTDIR)$(sbindir)" \ + "$(DESTDIR)$(pkgconfigdir)" \ + "$(DESTDIR)$(include_libdnssecdir)" \ + "$(DESTDIR)$(include_libknotddir)" \ + "$(DESTDIR)$(include_libzscannerdir)" \ + "$(DESTDIR)$(include_libknotdir)" +LTLIBRARIES = $(lib_LTLIBRARIES) $(noinst_LTLIBRARIES) \ + $(pkglib_LTLIBRARIES) +@SHARED_MODULE_cookies_TRUE@knot_modules_cookies_la_DEPENDENCIES = \ +@SHARED_MODULE_cookies_TRUE@ libcontrib.la +am__dirstamp = $(am__leading_dot)dirstamp +am_knot_modules_cookies_la_OBJECTS = \ + knot/modules/cookies/knot_modules_cookies_la-cookies.lo +knot_modules_cookies_la_OBJECTS = \ + $(am_knot_modules_cookies_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 = +knot_modules_cookies_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(knot_modules_cookies_la_LDFLAGS) \ + $(LDFLAGS) -o $@ +@SHARED_MODULE_cookies_TRUE@am_knot_modules_cookies_la_rpath = -rpath \ +@SHARED_MODULE_cookies_TRUE@ $(pkglibdir) +@SHARED_MODULE_dnsproxy_TRUE@knot_modules_dnsproxy_la_DEPENDENCIES = \ +@SHARED_MODULE_dnsproxy_TRUE@ libcontrib.la +am_knot_modules_dnsproxy_la_OBJECTS = \ + knot/modules/dnsproxy/knot_modules_dnsproxy_la-dnsproxy.lo +knot_modules_dnsproxy_la_OBJECTS = \ + $(am_knot_modules_dnsproxy_la_OBJECTS) +knot_modules_dnsproxy_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(knot_modules_dnsproxy_la_LDFLAGS) \ + $(LDFLAGS) -o $@ +@SHARED_MODULE_dnsproxy_TRUE@am_knot_modules_dnsproxy_la_rpath = \ +@SHARED_MODULE_dnsproxy_TRUE@ -rpath $(pkglibdir) +am__DEPENDENCIES_1 = +@SHARED_MODULE_dnstap_TRUE@knot_modules_dnstap_la_DEPENDENCIES = \ +@SHARED_MODULE_dnstap_TRUE@ $(am__DEPENDENCIES_1) libdnstap.la +am_knot_modules_dnstap_la_OBJECTS = \ + knot/modules/dnstap/knot_modules_dnstap_la-dnstap.lo +knot_modules_dnstap_la_OBJECTS = $(am_knot_modules_dnstap_la_OBJECTS) +knot_modules_dnstap_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(knot_modules_dnstap_la_LDFLAGS) \ + $(LDFLAGS) -o $@ +@SHARED_MODULE_dnstap_TRUE@am_knot_modules_dnstap_la_rpath = -rpath \ +@SHARED_MODULE_dnstap_TRUE@ $(pkglibdir) +knot_modules_geoip_la_LIBADD = +am_knot_modules_geoip_la_OBJECTS = \ + knot/modules/geoip/knot_modules_geoip_la-geoip.lo \ + knot/modules/geoip/knot_modules_geoip_la-geodb.lo +knot_modules_geoip_la_OBJECTS = $(am_knot_modules_geoip_la_OBJECTS) +knot_modules_geoip_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(knot_modules_geoip_la_LDFLAGS) \ + $(LDFLAGS) -o $@ +@SHARED_MODULE_geoip_TRUE@am_knot_modules_geoip_la_rpath = -rpath \ +@SHARED_MODULE_geoip_TRUE@ $(pkglibdir) +knot_modules_noudp_la_LIBADD = +am_knot_modules_noudp_la_OBJECTS = \ + knot/modules/noudp/knot_modules_noudp_la-noudp.lo +knot_modules_noudp_la_OBJECTS = $(am_knot_modules_noudp_la_OBJECTS) +knot_modules_noudp_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(knot_modules_noudp_la_LDFLAGS) \ + $(LDFLAGS) -o $@ +@SHARED_MODULE_noudp_TRUE@am_knot_modules_noudp_la_rpath = -rpath \ +@SHARED_MODULE_noudp_TRUE@ $(pkglibdir) +@SHARED_MODULE_onlinesign_TRUE@knot_modules_onlinesign_la_DEPENDENCIES = \ +@SHARED_MODULE_onlinesign_TRUE@ libcontrib.la +am_knot_modules_onlinesign_la_OBJECTS = knot/modules/onlinesign/knot_modules_onlinesign_la-onlinesign.lo \ + knot/modules/onlinesign/knot_modules_onlinesign_la-nsec_next.lo +knot_modules_onlinesign_la_OBJECTS = \ + $(am_knot_modules_onlinesign_la_OBJECTS) +knot_modules_onlinesign_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(knot_modules_onlinesign_la_LDFLAGS) \ + $(LDFLAGS) -o $@ +@SHARED_MODULE_onlinesign_TRUE@am_knot_modules_onlinesign_la_rpath = \ +@SHARED_MODULE_onlinesign_TRUE@ -rpath $(pkglibdir) +knot_modules_queryacl_la_LIBADD = +am_knot_modules_queryacl_la_OBJECTS = \ + knot/modules/queryacl/knot_modules_queryacl_la-queryacl.lo +knot_modules_queryacl_la_OBJECTS = \ + $(am_knot_modules_queryacl_la_OBJECTS) +knot_modules_queryacl_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(knot_modules_queryacl_la_LDFLAGS) \ + $(LDFLAGS) -o $@ +@SHARED_MODULE_queryacl_TRUE@am_knot_modules_queryacl_la_rpath = \ +@SHARED_MODULE_queryacl_TRUE@ -rpath $(pkglibdir) +@SHARED_MODULE_rrl_TRUE@knot_modules_rrl_la_DEPENDENCIES = \ +@SHARED_MODULE_rrl_TRUE@ libcontrib.la +am_knot_modules_rrl_la_OBJECTS = \ + knot/modules/rrl/knot_modules_rrl_la-rrl.lo \ + knot/modules/rrl/knot_modules_rrl_la-functions.lo +knot_modules_rrl_la_OBJECTS = $(am_knot_modules_rrl_la_OBJECTS) +knot_modules_rrl_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(knot_modules_rrl_la_LDFLAGS) \ + $(LDFLAGS) -o $@ +@SHARED_MODULE_rrl_TRUE@am_knot_modules_rrl_la_rpath = -rpath \ +@SHARED_MODULE_rrl_TRUE@ $(pkglibdir) +knot_modules_stats_la_LIBADD = +am_knot_modules_stats_la_OBJECTS = \ + knot/modules/stats/knot_modules_stats_la-stats.lo +knot_modules_stats_la_OBJECTS = $(am_knot_modules_stats_la_OBJECTS) +knot_modules_stats_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(knot_modules_stats_la_LDFLAGS) \ + $(LDFLAGS) -o $@ +@SHARED_MODULE_stats_TRUE@am_knot_modules_stats_la_rpath = -rpath \ +@SHARED_MODULE_stats_TRUE@ $(pkglibdir) +@SHARED_MODULE_synthrecord_TRUE@knot_modules_synthrecord_la_DEPENDENCIES = \ +@SHARED_MODULE_synthrecord_TRUE@ libcontrib.la +am_knot_modules_synthrecord_la_OBJECTS = knot/modules/synthrecord/knot_modules_synthrecord_la-synthrecord.lo +knot_modules_synthrecord_la_OBJECTS = \ + $(am_knot_modules_synthrecord_la_OBJECTS) +knot_modules_synthrecord_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(knot_modules_synthrecord_la_LDFLAGS) \ + $(LDFLAGS) -o $@ +@SHARED_MODULE_synthrecord_TRUE@am_knot_modules_synthrecord_la_rpath = \ +@SHARED_MODULE_synthrecord_TRUE@ -rpath $(pkglibdir) +knot_modules_whoami_la_LIBADD = +am_knot_modules_whoami_la_OBJECTS = \ + knot/modules/whoami/knot_modules_whoami_la-whoami.lo +knot_modules_whoami_la_OBJECTS = $(am_knot_modules_whoami_la_OBJECTS) +knot_modules_whoami_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(knot_modules_whoami_la_LDFLAGS) \ + $(LDFLAGS) -o $@ +@SHARED_MODULE_whoami_TRUE@am_knot_modules_whoami_la_rpath = -rpath \ +@SHARED_MODULE_whoami_TRUE@ $(pkglibdir) +libcontrib_la_LIBADD = +am__libcontrib_la_SOURCES_DIST = contrib/asan.h contrib/base32hex.c \ + contrib/base32hex.h contrib/base64.c contrib/base64.h \ + contrib/ctype.h contrib/dynarray.h contrib/files.c \ + contrib/files.h contrib/getline.c contrib/getline.h \ + contrib/macros.h contrib/mempattern.c contrib/mempattern.h \ + contrib/net.c contrib/net.h contrib/qp-trie/trie.c \ + contrib/qp-trie/trie.h contrib/sockaddr.c contrib/sockaddr.h \ + contrib/string.c contrib/string.h contrib/strtonum.h \ + contrib/time.c contrib/time.h contrib/tolower.h contrib/trim.h \ + contrib/wire_ctx.h contrib/openbsd/siphash.c \ + contrib/openbsd/siphash.h contrib/openbsd/strlcat.c \ + contrib/openbsd/strlcat.h contrib/openbsd/strlcpy.c \ + contrib/openbsd/strlcpy.h contrib/ucw/array-sort.h \ + contrib/ucw/binsearch.h contrib/ucw/heap.c contrib/ucw/heap.h \ + contrib/ucw/lists.c contrib/ucw/lists.h contrib/ucw/mempool.c \ + contrib/ucw/mempool.h contrib/lmdb/lmdb.h contrib/lmdb/mdb.c \ + contrib/lmdb/midl.c contrib/lmdb/midl.h +@HAVE_LMDB_FALSE@am__objects_1 = contrib/lmdb/libcontrib_la-mdb.lo \ +@HAVE_LMDB_FALSE@ contrib/lmdb/libcontrib_la-midl.lo +am_libcontrib_la_OBJECTS = contrib/libcontrib_la-base32hex.lo \ + contrib/libcontrib_la-base64.lo contrib/libcontrib_la-files.lo \ + contrib/libcontrib_la-getline.lo \ + contrib/libcontrib_la-mempattern.lo \ + contrib/libcontrib_la-net.lo \ + contrib/qp-trie/libcontrib_la-trie.lo \ + contrib/libcontrib_la-sockaddr.lo \ + contrib/libcontrib_la-string.lo contrib/libcontrib_la-time.lo \ + contrib/openbsd/libcontrib_la-siphash.lo \ + contrib/openbsd/libcontrib_la-strlcat.lo \ + contrib/openbsd/libcontrib_la-strlcpy.lo \ + contrib/ucw/libcontrib_la-heap.lo \ + contrib/ucw/libcontrib_la-lists.lo \ + contrib/ucw/libcontrib_la-mempool.lo $(am__objects_1) +libcontrib_la_OBJECTS = $(am_libcontrib_la_OBJECTS) +libcontrib_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(libcontrib_la_LDFLAGS) $(LDFLAGS) -o $@ +@ENABLE_PKCS11_TRUE@am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1) +libdnssec_la_DEPENDENCIES = libshared.la $(am__DEPENDENCIES_2) +am_libdnssec_la_OBJECTS = libdnssec/contrib/libdnssec_la-vpool.lo \ + libdnssec/libdnssec_la-binary.lo \ + libdnssec/libdnssec_la-crypto.lo \ + libdnssec/libdnssec_la-error.lo \ + libdnssec/key/libdnssec_la-algorithm.lo \ + libdnssec/key/libdnssec_la-convert.lo \ + libdnssec/key/libdnssec_la-dnskey.lo \ + libdnssec/key/libdnssec_la-ds.lo \ + libdnssec/key/libdnssec_la-key.lo \ + libdnssec/key/libdnssec_la-keytag.lo \ + libdnssec/key/libdnssec_la-privkey.lo \ + libdnssec/key/libdnssec_la-simple.lo \ + libdnssec/libdnssec_la-keyid.lo \ + libdnssec/keystore/libdnssec_la-keystore.lo \ + libdnssec/keystore/libdnssec_la-pkcs11.lo \ + libdnssec/keystore/libdnssec_la-pkcs8.lo \ + libdnssec/keystore/libdnssec_la-pkcs8_dir.lo \ + libdnssec/list/libdnssec_la-list.lo \ + libdnssec/nsec/libdnssec_la-bitmap.lo \ + libdnssec/nsec/libdnssec_la-hash.lo \ + libdnssec/nsec/libdnssec_la-nsec.lo \ + libdnssec/p11/libdnssec_la-p11.lo \ + libdnssec/libdnssec_la-random.lo \ + libdnssec/sign/libdnssec_la-der.lo \ + libdnssec/sign/libdnssec_la-sign.lo \ + libdnssec/libdnssec_la-tsig.lo +libdnssec_la_OBJECTS = $(am_libdnssec_la_OBJECTS) +libdnssec_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(libdnssec_la_LDFLAGS) $(LDFLAGS) -o $@ +libdnstap_la_LIBADD = +am__libdnstap_la_SOURCES_DIST = contrib/dnstap/convert.c \ + contrib/dnstap/convert.h contrib/dnstap/dnstap.c \ + contrib/dnstap/dnstap.h contrib/dnstap/message.c \ + contrib/dnstap/message.h contrib/dnstap/reader.c \ + contrib/dnstap/reader.h contrib/dnstap/writer.c \ + contrib/dnstap/writer.h +@HAVE_LIBDNSTAP_TRUE@am_libdnstap_la_OBJECTS = \ +@HAVE_LIBDNSTAP_TRUE@ contrib/dnstap/libdnstap_la-convert.lo \ +@HAVE_LIBDNSTAP_TRUE@ contrib/dnstap/libdnstap_la-dnstap.lo \ +@HAVE_LIBDNSTAP_TRUE@ contrib/dnstap/libdnstap_la-message.lo \ +@HAVE_LIBDNSTAP_TRUE@ contrib/dnstap/libdnstap_la-reader.lo \ +@HAVE_LIBDNSTAP_TRUE@ contrib/dnstap/libdnstap_la-writer.lo +@HAVE_LIBDNSTAP_TRUE@nodist_libdnstap_la_OBJECTS = contrib/dnstap/libdnstap_la-dnstap.pb-c.lo +libdnstap_la_OBJECTS = $(am_libdnstap_la_OBJECTS) \ + $(nodist_libdnstap_la_OBJECTS) +libdnstap_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(libdnstap_la_LDFLAGS) $(LDFLAGS) -o $@ +@HAVE_LIBDNSTAP_TRUE@am_libdnstap_la_rpath = +libknot_la_DEPENDENCIES = libcontrib.la libdnssec.la \ + $(am__DEPENDENCIES_1) +am_libknot_la_OBJECTS = libknot/libknot_la-codes.lo \ + libknot/control/libknot_la-control.lo \ + libknot/libknot_la-cookies.lo libknot/libknot_la-descriptor.lo \ + libknot/libknot_la-dname.lo libknot/libknot_la-error.lo \ + libknot/db/libknot_la-db_lmdb.lo \ + libknot/db/libknot_la-db_trie.lo \ + libknot/packet/libknot_la-pkt.lo \ + libknot/packet/libknot_la-rrset-wire.lo \ + libknot/libknot_la-rdataset.lo \ + libknot/libknot_la-rrset-dump.lo libknot/libknot_la-rrset.lo \ + libknot/rrtype/libknot_la-naptr.lo \ + libknot/rrtype/libknot_la-opt.lo \ + libknot/rrtype/libknot_la-tsig.lo \ + libknot/libknot_la-tsig-op.lo libknot/libknot_la-tsig.lo \ + libknot/yparser/libknot_la-yparser.lo \ + libknot/yparser/libknot_la-ypbody.lo \ + libknot/yparser/libknot_la-ypformat.lo \ + libknot/yparser/libknot_la-ypschema.lo \ + libknot/yparser/libknot_la-yptrafo.lo +libknot_la_OBJECTS = $(am_libknot_la_OBJECTS) +libknot_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(libknot_la_LDFLAGS) $(LDFLAGS) -o $@ +@STATIC_MODULE_dnstap_TRUE@am__DEPENDENCIES_3 = $(am__DEPENDENCIES_1) \ +@STATIC_MODULE_dnstap_TRUE@ libdnstap.la +@STATIC_MODULE_geoip_TRUE@am__DEPENDENCIES_4 = $(am__DEPENDENCIES_1) +libknotd_la_DEPENDENCIES = libcontrib.la libknot.la libzscanner.la \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_3) $(am__DEPENDENCIES_4) +am__libknotd_la_SOURCES_DIST = knot/conf/base.c knot/conf/base.h \ + knot/conf/conf.c knot/conf/conf.h knot/conf/confdb.c \ + knot/conf/confdb.h knot/conf/confio.c knot/conf/confio.h \ + knot/conf/migration.c knot/conf/migration.h knot/conf/module.h \ + knot/conf/module.c knot/conf/schema.c knot/conf/schema.h \ + knot/conf/tools.c knot/conf/tools.h knot/ctl/commands.c \ + knot/ctl/commands.h knot/ctl/process.c knot/ctl/process.h \ + knot/dnssec/context.c knot/dnssec/context.h \ + knot/dnssec/ds_query.c knot/dnssec/ds_query.h \ + knot/dnssec/kasp/kasp_db.c knot/dnssec/kasp/kasp_db.h \ + knot/dnssec/kasp/kasp_zone.c knot/dnssec/kasp/kasp_zone.h \ + knot/dnssec/kasp/keystate.c knot/dnssec/kasp/keystate.h \ + knot/dnssec/kasp/keystore.c knot/dnssec/kasp/keystore.h \ + knot/dnssec/kasp/policy.h knot/dnssec/key-events.c \ + knot/dnssec/key-events.h knot/dnssec/nsec-chain.c \ + knot/dnssec/nsec-chain.h knot/dnssec/nsec3-chain.c \ + knot/dnssec/nsec3-chain.h knot/dnssec/policy.c \ + knot/dnssec/policy.h knot/dnssec/rrset-sign.c \ + knot/dnssec/rrset-sign.h knot/dnssec/zone-events.c \ + knot/dnssec/zone-events.h knot/dnssec/zone-keys.c \ + knot/dnssec/zone-keys.h knot/dnssec/zone-nsec.c \ + knot/dnssec/zone-nsec.h knot/dnssec/zone-sign.c \ + knot/dnssec/zone-sign.h knot/events/events.c \ + knot/events/events.h knot/events/handlers.h \ + knot/events/handlers/dnssec.c knot/events/handlers/expire.c \ + knot/events/handlers/flush.c \ + knot/events/handlers/freeze_thaw.c knot/events/handlers/load.c \ + knot/events/handlers/notify.c \ + knot/events/handlers/nsec3resalt.c \ + knot/events/handlers/refresh.c knot/events/handlers/update.c \ + knot/events/handlers/parent_ds_query.c knot/events/replan.c \ + knot/events/replan.h knot/nameserver/axfr.c \ + knot/nameserver/axfr.h knot/nameserver/chaos.c \ + knot/nameserver/chaos.h knot/nameserver/internet.c \ + knot/nameserver/internet.h knot/nameserver/ixfr.c \ + knot/nameserver/ixfr.h knot/nameserver/log.h \ + knot/nameserver/notify.c knot/nameserver/notify.h \ + knot/nameserver/nsec_proofs.c knot/nameserver/nsec_proofs.h \ + knot/nameserver/process_query.c \ + knot/nameserver/process_query.h knot/nameserver/query_module.c \ + knot/nameserver/query_module.h knot/nameserver/tsig_ctx.c \ + knot/nameserver/tsig_ctx.h knot/nameserver/update.c \ + knot/nameserver/update.h knot/nameserver/xfr.c \ + knot/nameserver/xfr.h knot/query/capture.c \ + knot/query/capture.h knot/query/layer.h knot/query/query.c \ + knot/query/query.h knot/query/requestor.c \ + knot/query/requestor.h knot/common/evsched.c \ + knot/common/evsched.h knot/common/fdset.c knot/common/fdset.h \ + knot/common/log.c knot/common/log.h knot/common/process.c \ + knot/common/process.h knot/common/ref.c knot/common/ref.h \ + knot/common/stats.c knot/common/stats.h knot/server/dthreads.c \ + knot/server/dthreads.h knot/journal/chgset_ctx.c \ + knot/journal/chgset_ctx.h knot/journal/journal.c \ + knot/journal/journal.h knot/journal/serialization.c \ + knot/journal/serialization.h knot/server/server.c \ + knot/server/server.h knot/server/tcp-handler.c \ + knot/server/tcp-handler.h knot/server/udp-handler.c \ + knot/server/udp-handler.h knot/updates/acl.c \ + knot/updates/acl.h knot/updates/apply.c knot/updates/apply.h \ + knot/updates/changesets.c knot/updates/changesets.h \ + knot/updates/ddns.c knot/updates/ddns.h \ + knot/updates/zone-update.c knot/updates/zone-update.h \ + knot/worker/pool.c knot/worker/pool.h knot/worker/queue.c \ + knot/worker/queue.h knot/zone/contents.c knot/zone/contents.h \ + knot/zone/node.c knot/zone/node.h knot/zone/semantic-check.c \ + knot/zone/semantic-check.h knot/zone/serial.c \ + knot/zone/serial.h knot/zone/timers.c knot/zone/timers.h \ + knot/zone/zone-diff.c knot/zone/zone-diff.h \ + knot/zone/zone-dump.c knot/zone/zone-dump.h \ + knot/zone/zone-load.c knot/zone/zone-load.h \ + knot/zone/zone-tree.c knot/zone/zone-tree.h knot/zone/zone.c \ + knot/zone/zone.h knot/zone/zonedb-load.c \ + knot/zone/zonedb-load.h knot/zone/zonedb.c knot/zone/zonedb.h \ + knot/zone/zonefile.c knot/zone/zonefile.h \ + knot/modules/cookies/cookies.c \ + knot/modules/dnsproxy/dnsproxy.c knot/modules/dnstap/dnstap.c \ + knot/modules/geoip/geoip.c knot/modules/geoip/geodb.c \ + knot/modules/geoip/geodb.h knot/modules/noudp/noudp.c \ + knot/modules/onlinesign/onlinesign.c \ + knot/modules/onlinesign/nsec_next.c \ + knot/modules/onlinesign/nsec_next.h \ + knot/modules/queryacl/queryacl.c knot/modules/rrl/rrl.c \ + knot/modules/rrl/functions.c knot/modules/rrl/functions.h \ + knot/modules/stats/stats.c \ + knot/modules/synthrecord/synthrecord.c \ + knot/modules/whoami/whoami.c +am__objects_2 = knot/modules/cookies/libknotd_la-cookies.lo +@STATIC_MODULE_cookies_TRUE@am__objects_3 = $(am__objects_2) +am__objects_4 = knot/modules/dnsproxy/libknotd_la-dnsproxy.lo +@STATIC_MODULE_dnsproxy_TRUE@am__objects_5 = $(am__objects_4) +am__objects_6 = knot/modules/dnstap/libknotd_la-dnstap.lo +@STATIC_MODULE_dnstap_TRUE@am__objects_7 = $(am__objects_6) +am__objects_8 = knot/modules/geoip/libknotd_la-geoip.lo \ + knot/modules/geoip/libknotd_la-geodb.lo +@STATIC_MODULE_geoip_TRUE@am__objects_9 = $(am__objects_8) +am__objects_10 = knot/modules/noudp/libknotd_la-noudp.lo +@STATIC_MODULE_noudp_TRUE@am__objects_11 = $(am__objects_10) +am__objects_12 = knot/modules/onlinesign/libknotd_la-onlinesign.lo \ + knot/modules/onlinesign/libknotd_la-nsec_next.lo +@STATIC_MODULE_onlinesign_TRUE@am__objects_13 = $(am__objects_12) +am__objects_14 = knot/modules/queryacl/libknotd_la-queryacl.lo +@STATIC_MODULE_queryacl_TRUE@am__objects_15 = $(am__objects_14) +am__objects_16 = knot/modules/rrl/libknotd_la-rrl.lo \ + knot/modules/rrl/libknotd_la-functions.lo +@STATIC_MODULE_rrl_TRUE@am__objects_17 = $(am__objects_16) +am__objects_18 = knot/modules/stats/libknotd_la-stats.lo +@STATIC_MODULE_stats_TRUE@am__objects_19 = $(am__objects_18) +am__objects_20 = knot/modules/synthrecord/libknotd_la-synthrecord.lo +@STATIC_MODULE_synthrecord_TRUE@am__objects_21 = $(am__objects_20) +am__objects_22 = knot/modules/whoami/libknotd_la-whoami.lo +@STATIC_MODULE_whoami_TRUE@am__objects_23 = $(am__objects_22) +am_libknotd_la_OBJECTS = knot/conf/libknotd_la-base.lo \ + knot/conf/libknotd_la-conf.lo knot/conf/libknotd_la-confdb.lo \ + knot/conf/libknotd_la-confio.lo \ + knot/conf/libknotd_la-migration.lo \ + knot/conf/libknotd_la-module.lo \ + knot/conf/libknotd_la-schema.lo knot/conf/libknotd_la-tools.lo \ + knot/ctl/libknotd_la-commands.lo \ + knot/ctl/libknotd_la-process.lo \ + knot/dnssec/libknotd_la-context.lo \ + knot/dnssec/libknotd_la-ds_query.lo \ + knot/dnssec/kasp/libknotd_la-kasp_db.lo \ + knot/dnssec/kasp/libknotd_la-kasp_zone.lo \ + knot/dnssec/kasp/libknotd_la-keystate.lo \ + knot/dnssec/kasp/libknotd_la-keystore.lo \ + knot/dnssec/libknotd_la-key-events.lo \ + knot/dnssec/libknotd_la-nsec-chain.lo \ + knot/dnssec/libknotd_la-nsec3-chain.lo \ + knot/dnssec/libknotd_la-policy.lo \ + knot/dnssec/libknotd_la-rrset-sign.lo \ + knot/dnssec/libknotd_la-zone-events.lo \ + knot/dnssec/libknotd_la-zone-keys.lo \ + knot/dnssec/libknotd_la-zone-nsec.lo \ + knot/dnssec/libknotd_la-zone-sign.lo \ + knot/events/libknotd_la-events.lo \ + knot/events/handlers/libknotd_la-dnssec.lo \ + knot/events/handlers/libknotd_la-expire.lo \ + knot/events/handlers/libknotd_la-flush.lo \ + knot/events/handlers/libknotd_la-freeze_thaw.lo \ + knot/events/handlers/libknotd_la-load.lo \ + knot/events/handlers/libknotd_la-notify.lo \ + knot/events/handlers/libknotd_la-nsec3resalt.lo \ + knot/events/handlers/libknotd_la-refresh.lo \ + knot/events/handlers/libknotd_la-update.lo \ + knot/events/handlers/libknotd_la-parent_ds_query.lo \ + knot/events/libknotd_la-replan.lo \ + knot/nameserver/libknotd_la-axfr.lo \ + knot/nameserver/libknotd_la-chaos.lo \ + knot/nameserver/libknotd_la-internet.lo \ + knot/nameserver/libknotd_la-ixfr.lo \ + knot/nameserver/libknotd_la-notify.lo \ + knot/nameserver/libknotd_la-nsec_proofs.lo \ + knot/nameserver/libknotd_la-process_query.lo \ + knot/nameserver/libknotd_la-query_module.lo \ + knot/nameserver/libknotd_la-tsig_ctx.lo \ + knot/nameserver/libknotd_la-update.lo \ + knot/nameserver/libknotd_la-xfr.lo \ + knot/query/libknotd_la-capture.lo \ + knot/query/libknotd_la-query.lo \ + knot/query/libknotd_la-requestor.lo \ + knot/common/libknotd_la-evsched.lo \ + knot/common/libknotd_la-fdset.lo \ + knot/common/libknotd_la-log.lo \ + knot/common/libknotd_la-process.lo \ + knot/common/libknotd_la-ref.lo \ + knot/common/libknotd_la-stats.lo \ + knot/server/libknotd_la-dthreads.lo \ + knot/journal/libknotd_la-chgset_ctx.lo \ + knot/journal/libknotd_la-journal.lo \ + knot/journal/libknotd_la-serialization.lo \ + knot/server/libknotd_la-server.lo \ + knot/server/libknotd_la-tcp-handler.lo \ + knot/server/libknotd_la-udp-handler.lo \ + knot/updates/libknotd_la-acl.lo \ + knot/updates/libknotd_la-apply.lo \ + knot/updates/libknotd_la-changesets.lo \ + knot/updates/libknotd_la-ddns.lo \ + knot/updates/libknotd_la-zone-update.lo \ + knot/worker/libknotd_la-pool.lo \ + knot/worker/libknotd_la-queue.lo \ + knot/zone/libknotd_la-contents.lo \ + knot/zone/libknotd_la-node.lo \ + knot/zone/libknotd_la-semantic-check.lo \ + knot/zone/libknotd_la-serial.lo \ + knot/zone/libknotd_la-timers.lo \ + knot/zone/libknotd_la-zone-diff.lo \ + knot/zone/libknotd_la-zone-dump.lo \ + knot/zone/libknotd_la-zone-load.lo \ + knot/zone/libknotd_la-zone-tree.lo \ + knot/zone/libknotd_la-zone.lo \ + knot/zone/libknotd_la-zonedb-load.lo \ + knot/zone/libknotd_la-zonedb.lo \ + knot/zone/libknotd_la-zonefile.lo $(am__objects_3) \ + $(am__objects_5) $(am__objects_7) $(am__objects_9) \ + $(am__objects_11) $(am__objects_13) $(am__objects_15) \ + $(am__objects_17) $(am__objects_19) $(am__objects_21) \ + $(am__objects_23) +libknotd_la_OBJECTS = $(am_libknotd_la_OBJECTS) +libknotd_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(libknotd_la_LDFLAGS) $(LDFLAGS) -o $@ +@HAVE_DAEMON_TRUE@am_libknotd_la_rpath = +@HAVE_LIBUTILS_TRUE@libknotus_la_DEPENDENCIES = libcontrib.la \ +@HAVE_LIBUTILS_TRUE@ libknot.la $(am__DEPENDENCIES_1) \ +@HAVE_LIBUTILS_TRUE@ $(am__DEPENDENCIES_1) \ +@HAVE_LIBUTILS_TRUE@ $(am__DEPENDENCIES_1) \ +@HAVE_LIBUTILS_TRUE@ $(am__DEPENDENCIES_1) +am__libknotus_la_SOURCES_DIST = utils/common/cert.c \ + utils/common/cert.h utils/common/exec.c utils/common/exec.h \ + utils/common/hex.c utils/common/hex.h utils/common/lookup.c \ + utils/common/lookup.h utils/common/msg.c utils/common/msg.h \ + utils/common/netio.c utils/common/netio.h \ + utils/common/params.c utils/common/params.h \ + utils/common/resolv.c utils/common/resolv.h \ + utils/common/sign.c utils/common/sign.h utils/common/tls.c \ + utils/common/tls.h utils/common/token.c utils/common/token.h +@HAVE_LIBUTILS_TRUE@am_libknotus_la_OBJECTS = \ +@HAVE_LIBUTILS_TRUE@ utils/common/libknotus_la-cert.lo \ +@HAVE_LIBUTILS_TRUE@ utils/common/libknotus_la-exec.lo \ +@HAVE_LIBUTILS_TRUE@ utils/common/libknotus_la-hex.lo \ +@HAVE_LIBUTILS_TRUE@ utils/common/libknotus_la-lookup.lo \ +@HAVE_LIBUTILS_TRUE@ utils/common/libknotus_la-msg.lo \ +@HAVE_LIBUTILS_TRUE@ utils/common/libknotus_la-netio.lo \ +@HAVE_LIBUTILS_TRUE@ utils/common/libknotus_la-params.lo \ +@HAVE_LIBUTILS_TRUE@ utils/common/libknotus_la-resolv.lo \ +@HAVE_LIBUTILS_TRUE@ utils/common/libknotus_la-sign.lo \ +@HAVE_LIBUTILS_TRUE@ utils/common/libknotus_la-tls.lo \ +@HAVE_LIBUTILS_TRUE@ utils/common/libknotus_la-token.lo +libknotus_la_OBJECTS = $(am_libknotus_la_OBJECTS) +libknotus_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(libknotus_la_LDFLAGS) $(LDFLAGS) -o $@ +@HAVE_LIBUTILS_TRUE@am_libknotus_la_rpath = +libshared_la_LIBADD = +am_libshared_la_OBJECTS = libdnssec/shared/libshared_la-bignum.lo \ + libdnssec/shared/libshared_la-dname.lo \ + libdnssec/shared/libshared_la-fs.lo \ + libdnssec/shared/libshared_la-hex.lo \ + libdnssec/shared/libshared_la-keyid_gnutls.lo \ + libdnssec/shared/libshared_la-pem.lo +libshared_la_OBJECTS = $(am_libshared_la_OBJECTS) +libzscanner_la_DEPENDENCIES = $(am__DEPENDENCIES_1) +am__objects_24 = +am_libzscanner_la_OBJECTS = libzscanner/libzscanner_la-error.lo \ + libzscanner/libzscanner_la-functions.lo $(am__objects_24) +nodist_libzscanner_la_OBJECTS = libzscanner/libzscanner_la-scanner.lo +libzscanner_la_OBJECTS = $(am_libzscanner_la_OBJECTS) \ + $(nodist_libzscanner_la_OBJECTS) +libzscanner_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(libzscanner_la_LDFLAGS) $(LDFLAGS) -o \ + $@ +@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@am__EXEEXT_1 = kzonecheck$(EXEEXT) +@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@am__EXEEXT_2 = keymgr$(EXEEXT) \ +@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ kjournalprint$(EXEEXT) +PROGRAMS = $(bin_PROGRAMS) $(sbin_PROGRAMS) +am__kdig_SOURCES_DIST = utils/kdig/kdig_exec.c utils/kdig/kdig_exec.h \ + utils/kdig/kdig_main.c utils/kdig/kdig_params.c \ + utils/kdig/kdig_params.h +@HAVE_UTILS_TRUE@am_kdig_OBJECTS = \ +@HAVE_UTILS_TRUE@ utils/kdig/kdig-kdig_exec.$(OBJEXT) \ +@HAVE_UTILS_TRUE@ utils/kdig/kdig-kdig_main.$(OBJEXT) \ +@HAVE_UTILS_TRUE@ utils/kdig/kdig-kdig_params.$(OBJEXT) +kdig_OBJECTS = $(am_kdig_OBJECTS) +@HAVE_DNSTAP_TRUE@@HAVE_UTILS_TRUE@am__DEPENDENCIES_5 = \ +@HAVE_DNSTAP_TRUE@@HAVE_UTILS_TRUE@ $(am__DEPENDENCIES_1) \ +@HAVE_DNSTAP_TRUE@@HAVE_UTILS_TRUE@ libdnstap.la +@HAVE_UTILS_TRUE@kdig_DEPENDENCIES = libknotus.la \ +@HAVE_UTILS_TRUE@ $(am__DEPENDENCIES_5) +am__keymgr_SOURCES_DIST = utils/keymgr/bind_privkey.c \ + utils/keymgr/bind_privkey.h utils/keymgr/functions.c \ + utils/keymgr/functions.h utils/keymgr/main.c +@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@am_keymgr_OBJECTS = utils/keymgr/keymgr-bind_privkey.$(OBJEXT) \ +@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ utils/keymgr/keymgr-functions.$(OBJEXT) \ +@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ utils/keymgr/keymgr-main.$(OBJEXT) +keymgr_OBJECTS = $(am_keymgr_OBJECTS) +@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@keymgr_DEPENDENCIES = \ +@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ libcontrib.la libknotd.la \ +@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ libknotus.la libdnssec.la \ +@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ libshared.la libzscanner.la +am__khost_SOURCES_DIST = utils/kdig/kdig_exec.c utils/kdig/kdig_exec.h \ + utils/kdig/kdig_params.c utils/kdig/kdig_params.h \ + utils/khost/khost_main.c utils/khost/khost_params.c \ + utils/khost/khost_params.h +@HAVE_UTILS_TRUE@am_khost_OBJECTS = \ +@HAVE_UTILS_TRUE@ utils/kdig/khost-kdig_exec.$(OBJEXT) \ +@HAVE_UTILS_TRUE@ utils/kdig/khost-kdig_params.$(OBJEXT) \ +@HAVE_UTILS_TRUE@ utils/khost/khost-khost_main.$(OBJEXT) \ +@HAVE_UTILS_TRUE@ utils/khost/khost-khost_params.$(OBJEXT) +khost_OBJECTS = $(am_khost_OBJECTS) +@HAVE_UTILS_TRUE@khost_DEPENDENCIES = libknotus.la \ +@HAVE_UTILS_TRUE@ $(am__DEPENDENCIES_5) +am__kjournalprint_SOURCES_DIST = utils/kjournalprint/main.c +@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@am_kjournalprint_OBJECTS = utils/kjournalprint/kjournalprint-main.$(OBJEXT) +kjournalprint_OBJECTS = $(am_kjournalprint_OBJECTS) +@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@kjournalprint_DEPENDENCIES = \ +@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ libcontrib.la libknotd.la +am__knotc_SOURCES_DIST = utils/knotc/commands.c utils/knotc/commands.h \ + utils/knotc/estimator.c utils/knotc/estimator.h \ + utils/knotc/interactive.c utils/knotc/interactive.h \ + utils/knotc/process.c utils/knotc/process.h utils/knotc/main.c +@HAVE_DAEMON_TRUE@am_knotc_OBJECTS = \ +@HAVE_DAEMON_TRUE@ utils/knotc/knotc-commands.$(OBJEXT) \ +@HAVE_DAEMON_TRUE@ utils/knotc/knotc-estimator.$(OBJEXT) \ +@HAVE_DAEMON_TRUE@ utils/knotc/knotc-interactive.$(OBJEXT) \ +@HAVE_DAEMON_TRUE@ utils/knotc/knotc-process.$(OBJEXT) \ +@HAVE_DAEMON_TRUE@ utils/knotc/knotc-main.$(OBJEXT) +knotc_OBJECTS = $(am_knotc_OBJECTS) +@HAVE_DAEMON_TRUE@knotc_DEPENDENCIES = libcontrib.la libknotd.la \ +@HAVE_DAEMON_TRUE@ libknotus.la $(am__DEPENDENCIES_1) +knotc_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(knotc_LDFLAGS) $(LDFLAGS) -o $@ +am__knotd_SOURCES_DIST = utils/knotd/main.c +@HAVE_DAEMON_TRUE@am_knotd_OBJECTS = utils/knotd/knotd-main.$(OBJEXT) +knotd_OBJECTS = $(am_knotd_OBJECTS) +@HAVE_DAEMON_TRUE@knotd_DEPENDENCIES = $(am__DEPENDENCIES_1) \ +@HAVE_DAEMON_TRUE@ libcontrib.la libknotd.la \ +@HAVE_DAEMON_TRUE@ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) +knotd_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(knotd_LDFLAGS) $(LDFLAGS) -o $@ +am__knsec3hash_SOURCES_DIST = utils/knsec3hash/knsec3hash.c +@HAVE_UTILS_TRUE@am_knsec3hash_OBJECTS = utils/knsec3hash/knsec3hash-knsec3hash.$(OBJEXT) +knsec3hash_OBJECTS = $(am_knsec3hash_OBJECTS) +@HAVE_UTILS_TRUE@knsec3hash_DEPENDENCIES = libcontrib.la libdnssec.la \ +@HAVE_UTILS_TRUE@ libknot.la libshared.la +am__knsupdate_SOURCES_DIST = utils/knsupdate/knsupdate_exec.c \ + utils/knsupdate/knsupdate_exec.h \ + utils/knsupdate/knsupdate_main.c \ + utils/knsupdate/knsupdate_params.c \ + utils/knsupdate/knsupdate_params.h +@HAVE_UTILS_TRUE@am_knsupdate_OBJECTS = utils/knsupdate/knsupdate-knsupdate_exec.$(OBJEXT) \ +@HAVE_UTILS_TRUE@ utils/knsupdate/knsupdate-knsupdate_main.$(OBJEXT) \ +@HAVE_UTILS_TRUE@ utils/knsupdate/knsupdate-knsupdate_params.$(OBJEXT) +knsupdate_OBJECTS = $(am_knsupdate_OBJECTS) +@HAVE_UTILS_TRUE@knsupdate_DEPENDENCIES = libknotus.la libzscanner.la +am__kzonecheck_SOURCES_DIST = utils/kzonecheck/main.c \ + utils/kzonecheck/zone_check.c utils/kzonecheck/zone_check.h +@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@am_kzonecheck_OBJECTS = utils/kzonecheck/kzonecheck-main.$(OBJEXT) \ +@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ utils/kzonecheck/kzonecheck-zone_check.$(OBJEXT) +kzonecheck_OBJECTS = $(am_kzonecheck_OBJECTS) +@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@kzonecheck_DEPENDENCIES = \ +@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ libcontrib.la libknotd.la +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@ +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(knot_modules_cookies_la_SOURCES) \ + $(knot_modules_dnsproxy_la_SOURCES) \ + $(knot_modules_dnstap_la_SOURCES) \ + $(knot_modules_geoip_la_SOURCES) \ + $(knot_modules_noudp_la_SOURCES) \ + $(knot_modules_onlinesign_la_SOURCES) \ + $(knot_modules_queryacl_la_SOURCES) \ + $(knot_modules_rrl_la_SOURCES) \ + $(knot_modules_stats_la_SOURCES) \ + $(knot_modules_synthrecord_la_SOURCES) \ + $(knot_modules_whoami_la_SOURCES) $(libcontrib_la_SOURCES) \ + $(libdnssec_la_SOURCES) $(libdnstap_la_SOURCES) \ + $(nodist_libdnstap_la_SOURCES) $(libknot_la_SOURCES) \ + $(libknotd_la_SOURCES) $(libknotus_la_SOURCES) \ + $(libshared_la_SOURCES) $(libzscanner_la_SOURCES) \ + $(nodist_libzscanner_la_SOURCES) $(kdig_SOURCES) \ + $(keymgr_SOURCES) $(khost_SOURCES) $(kjournalprint_SOURCES) \ + $(knotc_SOURCES) $(knotd_SOURCES) $(knsec3hash_SOURCES) \ + $(knsupdate_SOURCES) $(kzonecheck_SOURCES) +DIST_SOURCES = $(knot_modules_cookies_la_SOURCES) \ + $(knot_modules_dnsproxy_la_SOURCES) \ + $(knot_modules_dnstap_la_SOURCES) \ + $(knot_modules_geoip_la_SOURCES) \ + $(knot_modules_noudp_la_SOURCES) \ + $(knot_modules_onlinesign_la_SOURCES) \ + $(knot_modules_queryacl_la_SOURCES) \ + $(knot_modules_rrl_la_SOURCES) \ + $(knot_modules_stats_la_SOURCES) \ + $(knot_modules_synthrecord_la_SOURCES) \ + $(knot_modules_whoami_la_SOURCES) \ + $(am__libcontrib_la_SOURCES_DIST) $(libdnssec_la_SOURCES) \ + $(am__libdnstap_la_SOURCES_DIST) $(libknot_la_SOURCES) \ + $(am__libknotd_la_SOURCES_DIST) \ + $(am__libknotus_la_SOURCES_DIST) $(libshared_la_SOURCES) \ + $(libzscanner_la_SOURCES) $(am__kdig_SOURCES_DIST) \ + $(am__keymgr_SOURCES_DIST) $(am__khost_SOURCES_DIST) \ + $(am__kjournalprint_SOURCES_DIST) $(am__knotc_SOURCES_DIST) \ + $(am__knotd_SOURCES_DIST) $(am__knsec3hash_SOURCES_DIST) \ + $(am__knsupdate_SOURCES_DIST) $(am__kzonecheck_SOURCES_DIST) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +DATA = $(pkgconfig_DATA) +HEADERS = $(include_libdnssec_HEADERS) $(include_libknotd_HEADERS) \ + $(include_libzscanner_HEADERS) \ + $(nobase_include_libknot_HEADERS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) \ + $(LISP)config.h.in +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/config.h.in \ + $(srcdir)/contrib/Makefile.inc $(srcdir)/knot/Makefile.inc \ + $(srcdir)/knot/modules/cookies/Makefile.inc \ + $(srcdir)/knot/modules/dnsproxy/Makefile.inc \ + $(srcdir)/knot/modules/dnstap/Makefile.inc \ + $(srcdir)/knot/modules/geoip/Makefile.inc \ + $(srcdir)/knot/modules/noudp/Makefile.inc \ + $(srcdir)/knot/modules/onlinesign/Makefile.inc \ + $(srcdir)/knot/modules/queryacl/Makefile.inc \ + $(srcdir)/knot/modules/rrl/Makefile.inc \ + $(srcdir)/knot/modules/stats/Makefile.inc \ + $(srcdir)/knot/modules/synthrecord/Makefile.inc \ + $(srcdir)/knot/modules/whoami/Makefile.inc \ + $(srcdir)/knotd.pc.in $(srcdir)/libdnssec.pc.in \ + $(srcdir)/libdnssec/Makefile.inc $(srcdir)/libknot.pc.in \ + $(srcdir)/libknot/Makefile.inc $(srcdir)/libzscanner.pc.in \ + $(srcdir)/libzscanner/Makefile.inc \ + $(srcdir)/utils/Makefile.inc $(top_srcdir)/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +pkglibdir = $(module_instdir) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CC_CLANG_VERSION = @CC_CLANG_VERSION@ +CFLAGS = @CFLAGS@ +CFLAG_VISIBILITY = @CFLAG_VISIBILITY@ +CODE_COVERAGE_ENABLED = @CODE_COVERAGE_ENABLED@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DNSTAP_CFLAGS = @DNSTAP_CFLAGS@ +DNSTAP_LIBS = @DNSTAP_LIBS@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GENHTML = @GENHTML@ +GREP = @GREP@ +HAVE_VISIBILITY = @HAVE_VISIBILITY@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +KNOT_VERSION_MAJOR = @KNOT_VERSION_MAJOR@ +KNOT_VERSION_MINOR = @KNOT_VERSION_MINOR@ +KNOT_VERSION_PATCH = @KNOT_VERSION_PATCH@ +LCOV = @LCOV@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LDFLAG_EXCLUDE_LIBS = @LDFLAG_EXCLUDE_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PDFLATEX = @PDFLATEX@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PROTOC_C = @PROTOC_C@ +RANLIB = @RANLIB@ +RELEASE_DATE = @RELEASE_DATE@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SPHINXBUILD = @SPHINXBUILD@ +STRIP = @STRIP@ +VERSION = @VERSION@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +cap_ng_CFLAGS = @cap_ng_CFLAGS@ +cap_ng_LIBS = @cap_ng_LIBS@ +conf_mapsize = @conf_mapsize@ +config_dir = @config_dir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +dlopen_LIBS = @dlopen_LIBS@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +external_lmdb_LIBS = @external_lmdb_LIBS@ +fuzzer_CFLAGS = @fuzzer_CFLAGS@ +fuzzer_LDFLAGS = @fuzzer_LDFLAGS@ +gnutls_CFLAGS = @gnutls_CFLAGS@ +gnutls_LIBS = @gnutls_LIBS@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libdnssec_SONAME = @libdnssec_SONAME@ +libdnssec_SOVERSION = @libdnssec_SOVERSION@ +libdnssec_VERSION_INFO = @libdnssec_VERSION_INFO@ +libedit_CFLAGS = @libedit_CFLAGS@ +libedit_LIBS = @libedit_LIBS@ +libexecdir = @libexecdir@ +libfstrm_CFLAGS = @libfstrm_CFLAGS@ +libfstrm_LIBS = @libfstrm_LIBS@ +libidn2_CFLAGS = @libidn2_CFLAGS@ +libidn2_LIBS = @libidn2_LIBS@ +libidn_CFLAGS = @libidn_CFLAGS@ +libidn_LIBS = @libidn_LIBS@ +libknot_SONAME = @libknot_SONAME@ +libknot_SOVERSION = @libknot_SOVERSION@ +libknot_VERSION_INFO = @libknot_VERSION_INFO@ +libmaxminddb_CFLAGS = @libmaxminddb_CFLAGS@ +libmaxminddb_LIBS = @libmaxminddb_LIBS@ +libprotobuf_c_CFLAGS = @libprotobuf_c_CFLAGS@ +libprotobuf_c_LIBS = @libprotobuf_c_LIBS@ +liburcu_CFLAGS = @liburcu_CFLAGS@ +liburcu_LIBS = @liburcu_LIBS@ +liburcu_PKGCONFIG = @liburcu_PKGCONFIG@ +libzscanner_SONAME = @libzscanner_SONAME@ +libzscanner_SOVERSION = @libzscanner_SOVERSION@ +libzscanner_VERSION_INFO = @libzscanner_VERSION_INFO@ +lmdb_CFLAGS = @lmdb_CFLAGS@ +lmdb_LIBS = @lmdb_LIBS@ +localedir = @localedir@ +localstatedir = @localstatedir@ +malloc_LIBS = @malloc_LIBS@ +mandir = @mandir@ +math_LIBS = @math_LIBS@ +mkdir_p = @mkdir_p@ +module_dir = @module_dir@ +module_instdir = @module_instdir@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgconfigdir = @pkgconfigdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pthread_LIBS = @pthread_LIBS@ +run_dir = @run_dir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +storage_dir = @storage_dir@ +sysconfdir = @sysconfdir@ +systemd_CFLAGS = @systemd_CFLAGS@ +systemd_LIBS = @systemd_LIBS@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +AM_CPPFLAGS = \ + -include $(top_builddir)/src/config.h \ + -DCONFIG_DIR='"${config_dir}"' \ + -DSTORAGE_DIR='"${storage_dir}"' \ + -DRUN_DIR='"${run_dir}"' \ + -DMODULE_DIR='"${module_dir}"' \ + -DMODULE_INSTDIR='"${module_instdir}"' + +EXTRA_DIST = contrib/licenses/0BSD contrib/licenses/BSD-3-Clause \ + contrib/licenses/LGPL-2.0 contrib/licenses/OLDAP-2.8 \ + contrib/lmdb/LICENSE contrib/openbsd/LICENSE \ + contrib/ucw/LICENSE contrib/dnstap/dnstap.proto \ + libzscanner/scanner.rl libzscanner/scanner_body.rl \ + libzscanner/scanner.c.g2 libzscanner/scanner.c.t0 \ + knot/modules/cookies/cookies.rst \ + knot/modules/dnsproxy/dnsproxy.rst \ + knot/modules/dnstap/dnstap.rst knot/modules/geoip/geoip.rst \ + knot/modules/noudp/noudp.rst \ + knot/modules/onlinesign/onlinesign.rst \ + knot/modules/queryacl/queryacl.rst knot/modules/rrl/rrl.rst \ + knot/modules/stats/stats.rst \ + knot/modules/synthrecord/synthrecord.rst \ + knot/modules/whoami/whoami.rst +CLEANFILES = $(am__append_5) libzscanner/scanner.c +BUILT_SOURCES = $(am__append_4) libzscanner/scanner.c +lib_LTLIBRARIES = libdnssec.la libknot.la libzscanner.la +noinst_LTLIBRARIES = libcontrib.la $(am__append_3) libshared.la \ + $(am__append_8) $(am__append_36) +pkgconfig_DATA = libdnssec.pc libknot.pc libzscanner.pc \ + $(am__append_9) +libcontrib_la_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAG_VISIBILITY) +libcontrib_la_LDFLAGS = $(AM_LDFLAGS) $(LDFLAG_EXCLUDE_LIBS) \ + $(am__append_1) +libcontrib_la_SOURCES = contrib/asan.h contrib/base32hex.c \ + contrib/base32hex.h contrib/base64.c contrib/base64.h \ + contrib/ctype.h contrib/dynarray.h contrib/files.c \ + contrib/files.h contrib/getline.c contrib/getline.h \ + contrib/macros.h contrib/mempattern.c contrib/mempattern.h \ + contrib/net.c contrib/net.h contrib/qp-trie/trie.c \ + contrib/qp-trie/trie.h contrib/sockaddr.c contrib/sockaddr.h \ + contrib/string.c contrib/string.h contrib/strtonum.h \ + contrib/time.c contrib/time.h contrib/tolower.h contrib/trim.h \ + contrib/wire_ctx.h contrib/openbsd/siphash.c \ + contrib/openbsd/siphash.h contrib/openbsd/strlcat.c \ + contrib/openbsd/strlcat.h contrib/openbsd/strlcpy.c \ + contrib/openbsd/strlcpy.h contrib/ucw/array-sort.h \ + contrib/ucw/binsearch.h contrib/ucw/heap.c contrib/ucw/heap.h \ + contrib/ucw/lists.c contrib/ucw/lists.h contrib/ucw/mempool.c \ + contrib/ucw/mempool.h $(am__append_2) +@HAVE_LIBDNSTAP_TRUE@libdnstap_la_CPPFLAGS = $(AM_CPPFLAGS) $(DNSTAP_CFLAGS) +@HAVE_LIBDNSTAP_TRUE@libdnstap_la_LDFLAGS = $(AM_LDFLAGS) $(DNSTAP_LIBS) +@HAVE_LIBDNSTAP_TRUE@SUFFIXES = .proto .pb-c.c .pb-c.h +@HAVE_LIBDNSTAP_TRUE@libdnstap_la_SOURCES = \ +@HAVE_LIBDNSTAP_TRUE@ contrib/dnstap/convert.c \ +@HAVE_LIBDNSTAP_TRUE@ contrib/dnstap/convert.h \ +@HAVE_LIBDNSTAP_TRUE@ contrib/dnstap/dnstap.c \ +@HAVE_LIBDNSTAP_TRUE@ contrib/dnstap/dnstap.h \ +@HAVE_LIBDNSTAP_TRUE@ contrib/dnstap/message.c \ +@HAVE_LIBDNSTAP_TRUE@ contrib/dnstap/message.h \ +@HAVE_LIBDNSTAP_TRUE@ contrib/dnstap/reader.c \ +@HAVE_LIBDNSTAP_TRUE@ contrib/dnstap/reader.h \ +@HAVE_LIBDNSTAP_TRUE@ contrib/dnstap/writer.c \ +@HAVE_LIBDNSTAP_TRUE@ contrib/dnstap/writer.h + +@HAVE_LIBDNSTAP_TRUE@nodist_libdnstap_la_SOURCES = \ +@HAVE_LIBDNSTAP_TRUE@ contrib/dnstap/dnstap.pb-c.c \ +@HAVE_LIBDNSTAP_TRUE@ contrib/dnstap/dnstap.pb-c.h + +libshared_la_CPPFLAGS = $(AM_CPPFLAGS) $(gnutls_CFLAGS) +libshared_la_SOURCES = \ + libdnssec/shared/bignum.c \ + libdnssec/shared/bignum.h \ + libdnssec/shared/binary_wire.h \ + libdnssec/shared/dname.c \ + libdnssec/shared/dname.h \ + libdnssec/shared/fs.c \ + libdnssec/shared/fs.h \ + libdnssec/shared/hex.c \ + libdnssec/shared/hex.h \ + libdnssec/shared/keyid_gnutls.c \ + libdnssec/shared/keyid_gnutls.h \ + libdnssec/shared/pem.c \ + libdnssec/shared/pem.h \ + libdnssec/shared/shared.h + +libdnssec_la_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAG_VISIBILITY) $(gnutls_CFLAGS) +libdnssec_la_LDFLAGS = $(AM_LDFLAGS) $(libdnssec_VERSION_INFO) \ + $(gnutls_LIBS) $(am__append_6) +libdnssec_la_LIBADD = libshared.la $(am__append_7) +include_libdnssecdir = $(includedir)/libdnssec +include_libdnssec_HEADERS = \ + libdnssec/binary.h \ + libdnssec/crypto.h \ + libdnssec/dnssec.h \ + libdnssec/error.h \ + libdnssec/key.h \ + libdnssec/keyid.h \ + libdnssec/keystore.h \ + libdnssec/keytag.h \ + libdnssec/list.h \ + libdnssec/nsec.h \ + libdnssec/random.h \ + libdnssec/sign.h \ + libdnssec/tsig.h \ + libdnssec/version.h + +libdnssec_la_SOURCES = \ + libdnssec/contrib/vpool.c \ + libdnssec/contrib/vpool.h \ + libdnssec/binary.c \ + libdnssec/crypto.c \ + libdnssec/error.c \ + libdnssec/key/algorithm.c \ + libdnssec/key/algorithm.h \ + libdnssec/key/convert.c \ + libdnssec/key/convert.h \ + libdnssec/key/dnskey.c \ + libdnssec/key/dnskey.h \ + libdnssec/key/ds.c \ + libdnssec/key/internal.h \ + libdnssec/key/key.c \ + libdnssec/key/keytag.c \ + libdnssec/key/privkey.c \ + libdnssec/key/privkey.h \ + libdnssec/key/simple.c \ + libdnssec/keyid.c \ + libdnssec/keystore/internal.h \ + libdnssec/keystore/keystore.c \ + libdnssec/keystore/pkcs11.c \ + libdnssec/keystore/pkcs8.c \ + libdnssec/keystore/pkcs8_dir.c \ + libdnssec/list/list.c \ + libdnssec/list/ucw_clists.h \ + libdnssec/nsec/bitmap.c \ + libdnssec/nsec/hash.c \ + libdnssec/nsec/nsec.c \ + libdnssec/p11/p11.c \ + libdnssec/p11/p11.h \ + libdnssec/random.c \ + libdnssec/sign/der.c \ + libdnssec/sign/der.h \ + libdnssec/sign/sign.c \ + libdnssec/tsig.c + +libknot_la_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAG_VISIBILITY) $(lmdb_CFLAGS) +libknot_la_LDFLAGS = $(AM_LDFLAGS) $(libknot_VERSION_INFO) $(lmdb_LIBS) \ + $(LDFLAG_EXCLUDE_LIBS) + +libknot_la_LIBADD = libcontrib.la libdnssec.la $(math_LIBS) +include_libknotdir = $(includedir) +nobase_include_libknot_HEADERS = \ + libknot/attribute.h \ + libknot/codes.h \ + libknot/consts.h \ + libknot/control/control.h \ + libknot/cookies.h \ + libknot/descriptor.h \ + libknot/dname.h \ + libknot/endian.h \ + libknot/errcode.h \ + libknot/error.h \ + libknot/libknot.h \ + libknot/lookup.h \ + libknot/mm_ctx.h \ + libknot/db/db.h \ + libknot/db/db_lmdb.h \ + libknot/db/db_trie.h \ + libknot/packet/compr.h \ + libknot/packet/pkt.h \ + libknot/packet/rrset-wire.h \ + libknot/packet/wire.h \ + libknot/rdata.h \ + libknot/rdataset.h \ + libknot/rrset-dump.h \ + libknot/rrset.h \ + libknot/rrtype/dnskey.h \ + libknot/rrtype/ds.h \ + libknot/rrtype/naptr.h \ + libknot/rrtype/nsec.h \ + libknot/rrtype/nsec3.h \ + libknot/rrtype/nsec3param.h \ + libknot/rrtype/opt.h \ + libknot/rrtype/rdname.h \ + libknot/rrtype/rrsig.h \ + libknot/rrtype/soa.h \ + libknot/rrtype/tsig.h \ + libknot/tsig-op.h \ + libknot/tsig.h \ + libknot/wire.h \ + libknot/yparser/yparser.h \ + libknot/yparser/ypformat.h \ + libknot/yparser/ypschema.h \ + libknot/yparser/yptrafo.h \ + libknot/version.h + +libknot_la_SOURCES = \ + libknot/codes.c \ + libknot/control/control.c \ + libknot/cookies.c \ + libknot/descriptor.c \ + libknot/dname.c \ + libknot/error.c \ + libknot/db/db_lmdb.c \ + libknot/db/db_trie.c \ + libknot/packet/pkt.c \ + libknot/packet/rrset-wire.c \ + libknot/rdataset.c \ + libknot/rrset-dump.c \ + libknot/rrset.c \ + libknot/rrtype/naptr.c \ + libknot/rrtype/opt.c \ + libknot/rrtype/tsig.c \ + libknot/tsig-op.c \ + libknot/tsig.c \ + libknot/yparser/yparser.c \ + libknot/yparser/ypbody.c \ + libknot/yparser/ypformat.c \ + libknot/yparser/ypschema.c \ + libknot/yparser/yptrafo.c + +libzscanner_la_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAG_VISIBILITY) +libzscanner_la_LDFLAGS = $(AM_LDFLAGS) $(libzscanner_VERSION_INFO) $(LDFLAG_EXCLUDE_LIBS) +libzscanner_la_LIBADD = $(math_LIBS) +include_libzscannerdir = $(includedir)/libzscanner +include_libzscanner_HEADERS = \ + libzscanner/error.h \ + libzscanner/scanner.h \ + libzscanner/version.h + +libzscanner_la_SOURCES = \ + libzscanner/error.c \ + libzscanner/functions.h \ + libzscanner/functions.c \ + $(include_libzscanner_HEADERS) + +nodist_libzscanner_la_SOURCES = \ + libzscanner/scanner.c + +libknotd_la_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAG_VISIBILITY) \ + $(systemd_CFLAGS) $(liburcu_CFLAGS) -DKNOTD_MOD_STATIC \ + $(am__append_15) $(am__append_19) +libknotd_la_LDFLAGS = $(AM_LDFLAGS) -export-symbols-regex '^knotd_' +libknotd_la_LIBADD = libcontrib.la libknot.la libzscanner.la \ + $(systemd_LIBS) $(liburcu_LIBS) $(pthread_LIBS) $(dlopen_LIBS) \ + $(am__append_16) $(am__append_20) +include_libknotddir = $(includedir)/knot +include_libknotd_HEADERS = \ + knot/include/module.h + +libknotd_la_SOURCES = knot/conf/base.c knot/conf/base.h \ + knot/conf/conf.c knot/conf/conf.h knot/conf/confdb.c \ + knot/conf/confdb.h knot/conf/confio.c knot/conf/confio.h \ + knot/conf/migration.c knot/conf/migration.h knot/conf/module.h \ + knot/conf/module.c knot/conf/schema.c knot/conf/schema.h \ + knot/conf/tools.c knot/conf/tools.h knot/ctl/commands.c \ + knot/ctl/commands.h knot/ctl/process.c knot/ctl/process.h \ + knot/dnssec/context.c knot/dnssec/context.h \ + knot/dnssec/ds_query.c knot/dnssec/ds_query.h \ + knot/dnssec/kasp/kasp_db.c knot/dnssec/kasp/kasp_db.h \ + knot/dnssec/kasp/kasp_zone.c knot/dnssec/kasp/kasp_zone.h \ + knot/dnssec/kasp/keystate.c knot/dnssec/kasp/keystate.h \ + knot/dnssec/kasp/keystore.c knot/dnssec/kasp/keystore.h \ + knot/dnssec/kasp/policy.h knot/dnssec/key-events.c \ + knot/dnssec/key-events.h knot/dnssec/nsec-chain.c \ + knot/dnssec/nsec-chain.h knot/dnssec/nsec3-chain.c \ + knot/dnssec/nsec3-chain.h knot/dnssec/policy.c \ + knot/dnssec/policy.h knot/dnssec/rrset-sign.c \ + knot/dnssec/rrset-sign.h knot/dnssec/zone-events.c \ + knot/dnssec/zone-events.h knot/dnssec/zone-keys.c \ + knot/dnssec/zone-keys.h knot/dnssec/zone-nsec.c \ + knot/dnssec/zone-nsec.h knot/dnssec/zone-sign.c \ + knot/dnssec/zone-sign.h knot/events/events.c \ + knot/events/events.h knot/events/handlers.h \ + knot/events/handlers/dnssec.c knot/events/handlers/expire.c \ + knot/events/handlers/flush.c \ + knot/events/handlers/freeze_thaw.c knot/events/handlers/load.c \ + knot/events/handlers/notify.c \ + knot/events/handlers/nsec3resalt.c \ + knot/events/handlers/refresh.c knot/events/handlers/update.c \ + knot/events/handlers/parent_ds_query.c knot/events/replan.c \ + knot/events/replan.h knot/nameserver/axfr.c \ + knot/nameserver/axfr.h knot/nameserver/chaos.c \ + knot/nameserver/chaos.h knot/nameserver/internet.c \ + knot/nameserver/internet.h knot/nameserver/ixfr.c \ + knot/nameserver/ixfr.h knot/nameserver/log.h \ + knot/nameserver/notify.c knot/nameserver/notify.h \ + knot/nameserver/nsec_proofs.c knot/nameserver/nsec_proofs.h \ + knot/nameserver/process_query.c \ + knot/nameserver/process_query.h knot/nameserver/query_module.c \ + knot/nameserver/query_module.h knot/nameserver/tsig_ctx.c \ + knot/nameserver/tsig_ctx.h knot/nameserver/update.c \ + knot/nameserver/update.h knot/nameserver/xfr.c \ + knot/nameserver/xfr.h knot/query/capture.c \ + knot/query/capture.h knot/query/layer.h knot/query/query.c \ + knot/query/query.h knot/query/requestor.c \ + knot/query/requestor.h knot/common/evsched.c \ + knot/common/evsched.h knot/common/fdset.c knot/common/fdset.h \ + knot/common/log.c knot/common/log.h knot/common/process.c \ + knot/common/process.h knot/common/ref.c knot/common/ref.h \ + knot/common/stats.c knot/common/stats.h knot/server/dthreads.c \ + knot/server/dthreads.h knot/journal/chgset_ctx.c \ + knot/journal/chgset_ctx.h knot/journal/journal.c \ + knot/journal/journal.h knot/journal/serialization.c \ + knot/journal/serialization.h knot/server/server.c \ + knot/server/server.h knot/server/tcp-handler.c \ + knot/server/tcp-handler.h knot/server/udp-handler.c \ + knot/server/udp-handler.h knot/updates/acl.c \ + knot/updates/acl.h knot/updates/apply.c knot/updates/apply.h \ + knot/updates/changesets.c knot/updates/changesets.h \ + knot/updates/ddns.c knot/updates/ddns.h \ + knot/updates/zone-update.c knot/updates/zone-update.h \ + knot/worker/pool.c knot/worker/pool.h knot/worker/queue.c \ + knot/worker/queue.h knot/zone/contents.c knot/zone/contents.h \ + knot/zone/node.c knot/zone/node.h knot/zone/semantic-check.c \ + knot/zone/semantic-check.h knot/zone/serial.c \ + knot/zone/serial.h knot/zone/timers.c knot/zone/timers.h \ + knot/zone/zone-diff.c knot/zone/zone-diff.h \ + knot/zone/zone-dump.c knot/zone/zone-dump.h \ + knot/zone/zone-load.c knot/zone/zone-load.h \ + knot/zone/zone-tree.c knot/zone/zone-tree.h knot/zone/zone.c \ + knot/zone/zone.h knot/zone/zonedb-load.c \ + knot/zone/zonedb-load.h knot/zone/zonedb.c knot/zone/zonedb.h \ + knot/zone/zonefile.c knot/zone/zonefile.h $(am__append_10) \ + $(am__append_12) $(am__append_14) $(am__append_18) \ + $(am__append_22) $(am__append_24) $(am__append_26) \ + $(am__append_28) $(am__append_30) $(am__append_32) \ + $(am__append_34) +KNOTD_MOD_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAG_VISIBILITY) +KNOTD_MOD_LDFLAGS = $(AM_LDFLAGS) -module -shared -avoid-version +pkglib_LTLIBRARIES = $(am__append_11) $(am__append_13) \ + $(am__append_17) $(am__append_21) $(am__append_23) \ + $(am__append_25) $(am__append_27) $(am__append_29) \ + $(am__append_31) $(am__append_33) $(am__append_35) +knot_modules_cookies_la_SOURCES = knot/modules/cookies/cookies.c +@SHARED_MODULE_cookies_TRUE@knot_modules_cookies_la_LDFLAGS = $(KNOTD_MOD_LDFLAGS) +@SHARED_MODULE_cookies_TRUE@knot_modules_cookies_la_CPPFLAGS = $(KNOTD_MOD_CPPFLAGS) +@SHARED_MODULE_cookies_TRUE@knot_modules_cookies_la_LIBADD = libcontrib.la +knot_modules_dnsproxy_la_SOURCES = knot/modules/dnsproxy/dnsproxy.c +@SHARED_MODULE_dnsproxy_TRUE@knot_modules_dnsproxy_la_LDFLAGS = $(KNOTD_MOD_LDFLAGS) +@SHARED_MODULE_dnsproxy_TRUE@knot_modules_dnsproxy_la_CPPFLAGS = $(KNOTD_MOD_CPPFLAGS) +@SHARED_MODULE_dnsproxy_TRUE@knot_modules_dnsproxy_la_LIBADD = libcontrib.la +knot_modules_dnstap_la_SOURCES = knot/modules/dnstap/dnstap.c +@SHARED_MODULE_dnstap_TRUE@knot_modules_dnstap_la_LDFLAGS = $(KNOTD_MOD_LDFLAGS) +@SHARED_MODULE_dnstap_TRUE@knot_modules_dnstap_la_CPPFLAGS = $(KNOTD_MOD_CPPFLAGS) $(DNSTAP_CFLAGS) +@SHARED_MODULE_dnstap_TRUE@knot_modules_dnstap_la_LIBADD = $(DNSTAP_LIBS) libdnstap.la +knot_modules_geoip_la_SOURCES = knot/modules/geoip/geoip.c \ + knot/modules/geoip/geodb.c \ + knot/modules/geoip/geodb.h + +@SHARED_MODULE_geoip_TRUE@knot_modules_geoip_la_LDFLAGS = $(KNOTD_MOD_LDFLAGS) +@SHARED_MODULE_geoip_TRUE@knot_modules_geoip_la_CPPFLAGS = $(KNOTD_MOD_CPPFLAGS) +knot_modules_noudp_la_SOURCES = knot/modules/noudp/noudp.c +@SHARED_MODULE_noudp_TRUE@knot_modules_noudp_la_LDFLAGS = $(KNOTD_MOD_LDFLAGS) +@SHARED_MODULE_noudp_TRUE@knot_modules_noudp_la_CPPFLAGS = $(KNOTD_MOD_CPPFLAGS) +knot_modules_onlinesign_la_SOURCES = knot/modules/onlinesign/onlinesign.c \ + knot/modules/onlinesign/nsec_next.c \ + knot/modules/onlinesign/nsec_next.h + +@SHARED_MODULE_onlinesign_TRUE@knot_modules_onlinesign_la_LDFLAGS = $(KNOTD_MOD_LDFLAGS) +@SHARED_MODULE_onlinesign_TRUE@knot_modules_onlinesign_la_CPPFLAGS = $(KNOTD_MOD_CPPFLAGS) +@SHARED_MODULE_onlinesign_TRUE@knot_modules_onlinesign_la_LIBADD = libcontrib.la +knot_modules_queryacl_la_SOURCES = knot/modules/queryacl/queryacl.c +@SHARED_MODULE_queryacl_TRUE@knot_modules_queryacl_la_LDFLAGS = $(KNOTD_MOD_LDFLAGS) +@SHARED_MODULE_queryacl_TRUE@knot_modules_queryacl_la_CPPFLAGS = $(KNOTD_MOD_CPPFLAGS) +knot_modules_rrl_la_SOURCES = knot/modules/rrl/rrl.c \ + knot/modules/rrl/functions.c \ + knot/modules/rrl/functions.h + +@SHARED_MODULE_rrl_TRUE@knot_modules_rrl_la_LDFLAGS = $(KNOTD_MOD_LDFLAGS) +@SHARED_MODULE_rrl_TRUE@knot_modules_rrl_la_CPPFLAGS = $(KNOTD_MOD_CPPFLAGS) +@SHARED_MODULE_rrl_TRUE@knot_modules_rrl_la_LIBADD = libcontrib.la +knot_modules_stats_la_SOURCES = knot/modules/stats/stats.c +@SHARED_MODULE_stats_TRUE@knot_modules_stats_la_LDFLAGS = $(KNOTD_MOD_LDFLAGS) +@SHARED_MODULE_stats_TRUE@knot_modules_stats_la_CPPFLAGS = $(KNOTD_MOD_CPPFLAGS) +knot_modules_synthrecord_la_SOURCES = knot/modules/synthrecord/synthrecord.c +@SHARED_MODULE_synthrecord_TRUE@knot_modules_synthrecord_la_LDFLAGS = $(KNOTD_MOD_LDFLAGS) +@SHARED_MODULE_synthrecord_TRUE@knot_modules_synthrecord_la_CPPFLAGS = $(KNOTD_MOD_CPPFLAGS) +@SHARED_MODULE_synthrecord_TRUE@knot_modules_synthrecord_la_LIBADD = libcontrib.la +knot_modules_whoami_la_SOURCES = knot/modules/whoami/whoami.c +@SHARED_MODULE_whoami_TRUE@knot_modules_whoami_la_LDFLAGS = $(KNOTD_MOD_LDFLAGS) +@SHARED_MODULE_whoami_TRUE@knot_modules_whoami_la_CPPFLAGS = $(KNOTD_MOD_CPPFLAGS) +@HAVE_LIBUTILS_TRUE@libknotus_la_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAG_VISIBILITY) $(libidn2_CFLAGS) \ +@HAVE_LIBUTILS_TRUE@ $(libidn_CFLAGS) $(libedit_CFLAGS) $(gnutls_CFLAGS) + +@HAVE_LIBUTILS_TRUE@libknotus_la_LDFLAGS = $(AM_LDFLAGS) $(LDFLAG_EXCLUDE_LIBS) +@HAVE_LIBUTILS_TRUE@libknotus_la_LIBADD = libcontrib.la libknot.la $(libidn2_LIBS) $(libidn_LIBS) \ +@HAVE_LIBUTILS_TRUE@ $(libedit_LIBS) $(gnutls_LIBS) + +@HAVE_LIBUTILS_TRUE@libknotus_la_SOURCES = \ +@HAVE_LIBUTILS_TRUE@ utils/common/cert.c \ +@HAVE_LIBUTILS_TRUE@ utils/common/cert.h \ +@HAVE_LIBUTILS_TRUE@ utils/common/exec.c \ +@HAVE_LIBUTILS_TRUE@ utils/common/exec.h \ +@HAVE_LIBUTILS_TRUE@ utils/common/hex.c \ +@HAVE_LIBUTILS_TRUE@ utils/common/hex.h \ +@HAVE_LIBUTILS_TRUE@ utils/common/lookup.c \ +@HAVE_LIBUTILS_TRUE@ utils/common/lookup.h \ +@HAVE_LIBUTILS_TRUE@ utils/common/msg.c \ +@HAVE_LIBUTILS_TRUE@ utils/common/msg.h \ +@HAVE_LIBUTILS_TRUE@ utils/common/netio.c \ +@HAVE_LIBUTILS_TRUE@ utils/common/netio.h \ +@HAVE_LIBUTILS_TRUE@ utils/common/params.c \ +@HAVE_LIBUTILS_TRUE@ utils/common/params.h \ +@HAVE_LIBUTILS_TRUE@ utils/common/resolv.c \ +@HAVE_LIBUTILS_TRUE@ utils/common/resolv.h \ +@HAVE_LIBUTILS_TRUE@ utils/common/sign.c \ +@HAVE_LIBUTILS_TRUE@ utils/common/sign.h \ +@HAVE_LIBUTILS_TRUE@ utils/common/tls.c \ +@HAVE_LIBUTILS_TRUE@ utils/common/tls.h \ +@HAVE_LIBUTILS_TRUE@ utils/common/token.c \ +@HAVE_LIBUTILS_TRUE@ utils/common/token.h + +@HAVE_UTILS_TRUE@kdig_SOURCES = \ +@HAVE_UTILS_TRUE@ utils/kdig/kdig_exec.c \ +@HAVE_UTILS_TRUE@ utils/kdig/kdig_exec.h \ +@HAVE_UTILS_TRUE@ utils/kdig/kdig_main.c \ +@HAVE_UTILS_TRUE@ utils/kdig/kdig_params.c \ +@HAVE_UTILS_TRUE@ utils/kdig/kdig_params.h + +@HAVE_UTILS_TRUE@khost_SOURCES = \ +@HAVE_UTILS_TRUE@ utils/kdig/kdig_exec.c \ +@HAVE_UTILS_TRUE@ utils/kdig/kdig_exec.h \ +@HAVE_UTILS_TRUE@ utils/kdig/kdig_params.c \ +@HAVE_UTILS_TRUE@ utils/kdig/kdig_params.h \ +@HAVE_UTILS_TRUE@ utils/khost/khost_main.c \ +@HAVE_UTILS_TRUE@ utils/khost/khost_params.c \ +@HAVE_UTILS_TRUE@ utils/khost/khost_params.h + +@HAVE_UTILS_TRUE@knsec3hash_SOURCES = \ +@HAVE_UTILS_TRUE@ utils/knsec3hash/knsec3hash.c + +@HAVE_UTILS_TRUE@knsupdate_SOURCES = \ +@HAVE_UTILS_TRUE@ utils/knsupdate/knsupdate_exec.c \ +@HAVE_UTILS_TRUE@ utils/knsupdate/knsupdate_exec.h \ +@HAVE_UTILS_TRUE@ utils/knsupdate/knsupdate_main.c \ +@HAVE_UTILS_TRUE@ utils/knsupdate/knsupdate_params.c \ +@HAVE_UTILS_TRUE@ utils/knsupdate/knsupdate_params.h + +@HAVE_UTILS_TRUE@kdig_CPPFLAGS = $(AM_CPPFLAGS) $(gnutls_CFLAGS) \ +@HAVE_UTILS_TRUE@ $(am__append_39) +@HAVE_UTILS_TRUE@kdig_LDADD = libknotus.la $(am__append_37) +@HAVE_UTILS_TRUE@khost_CPPFLAGS = $(AM_CPPFLAGS) $(gnutls_CFLAGS) \ +@HAVE_UTILS_TRUE@ $(am__append_40) +@HAVE_UTILS_TRUE@khost_LDADD = libknotus.la $(am__append_38) +@HAVE_UTILS_TRUE@knsec3hash_CPPFLAGS = $(AM_CPPFLAGS) +@HAVE_UTILS_TRUE@knsec3hash_LDADD = libcontrib.la libdnssec.la libknot.la libshared.la +@HAVE_UTILS_TRUE@knsupdate_CPPFLAGS = $(AM_CPPFLAGS) $(gnutls_CFLAGS) +@HAVE_UTILS_TRUE@knsupdate_LDADD = libknotus.la libzscanner.la +@HAVE_DAEMON_TRUE@knotc_SOURCES = \ +@HAVE_DAEMON_TRUE@ utils/knotc/commands.c \ +@HAVE_DAEMON_TRUE@ utils/knotc/commands.h \ +@HAVE_DAEMON_TRUE@ utils/knotc/estimator.c \ +@HAVE_DAEMON_TRUE@ utils/knotc/estimator.h \ +@HAVE_DAEMON_TRUE@ utils/knotc/interactive.c \ +@HAVE_DAEMON_TRUE@ utils/knotc/interactive.h \ +@HAVE_DAEMON_TRUE@ utils/knotc/process.c \ +@HAVE_DAEMON_TRUE@ utils/knotc/process.h \ +@HAVE_DAEMON_TRUE@ utils/knotc/main.c + +@HAVE_DAEMON_TRUE@knotd_SOURCES = \ +@HAVE_DAEMON_TRUE@ utils/knotd/main.c + +@HAVE_DAEMON_TRUE@knotc_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAG_VISIBILITY) $(libedit_CFLAGS) +@HAVE_DAEMON_TRUE@knotc_LDADD = libcontrib.la libknotd.la libknotus.la $(libedit_LIBS) +@HAVE_DAEMON_TRUE@knotc_LDFLAGS = $(AM_LDFLAGS) -rdynamic +@HAVE_DAEMON_TRUE@knotd_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAG_VISIBILITY) $(liburcu_CFLAGS) +@HAVE_DAEMON_TRUE@knotd_LDADD = $(malloc_LIBS) libcontrib.la libknotd.la $(liburcu_LIBS) \ +@HAVE_DAEMON_TRUE@ $(cap_ng_LIBS) + +@HAVE_DAEMON_TRUE@knotd_LDFLAGS = $(AM_LDFLAGS) -rdynamic +@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@kzonecheck_SOURCES = \ +@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ utils/kzonecheck/main.c \ +@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ utils/kzonecheck/zone_check.c \ +@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ utils/kzonecheck/zone_check.h + +@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@keymgr_SOURCES = \ +@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ utils/keymgr/bind_privkey.c \ +@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ utils/keymgr/bind_privkey.h \ +@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ utils/keymgr/functions.c \ +@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ utils/keymgr/functions.h \ +@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ utils/keymgr/main.c + +@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@kjournalprint_SOURCES = \ +@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ utils/kjournalprint/main.c + +@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@kzonecheck_CPPFLAGS = $(AM_CPPFLAGS) +@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@kzonecheck_LDADD = libcontrib.la libknotd.la +@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@keymgr_CPPFLAGS = $(AM_CPPFLAGS) $(gnutls_CFLAGS) +@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@keymgr_LDADD = libcontrib.la libknotd.la libknotus.la libdnssec.la \ +@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@ libshared.la libzscanner.la + +@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@kjournalprint_CPPFLAGS = $(AM_CPPFLAGS) $(gnutls_CFLAGS) +@HAVE_DAEMON_TRUE@@HAVE_UTILS_TRUE@kjournalprint_LDADD = libcontrib.la libknotd.la +all: $(BUILT_SOURCES) config.h + $(MAKE) $(AM_MAKEFLAGS) all-am + +.SUFFIXES: +.SUFFIXES: .proto .pb-c.c .pb-c.h .c .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(srcdir)/contrib/Makefile.inc $(srcdir)/libdnssec/Makefile.inc $(srcdir)/libknot/Makefile.inc $(srcdir)/libzscanner/Makefile.inc $(srcdir)/knot/Makefile.inc $(srcdir)/knot/modules/cookies/Makefile.inc $(srcdir)/knot/modules/dnsproxy/Makefile.inc $(srcdir)/knot/modules/dnstap/Makefile.inc $(srcdir)/knot/modules/geoip/Makefile.inc $(srcdir)/knot/modules/noudp/Makefile.inc $(srcdir)/knot/modules/onlinesign/Makefile.inc $(srcdir)/knot/modules/queryacl/Makefile.inc $(srcdir)/knot/modules/rrl/Makefile.inc $(srcdir)/knot/modules/stats/Makefile.inc $(srcdir)/knot/modules/synthrecord/Makefile.inc $(srcdir)/knot/modules/whoami/Makefile.inc $(srcdir)/utils/Makefile.inc $(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/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; +$(srcdir)/contrib/Makefile.inc $(srcdir)/libdnssec/Makefile.inc $(srcdir)/libknot/Makefile.inc $(srcdir)/libzscanner/Makefile.inc $(srcdir)/knot/Makefile.inc $(srcdir)/knot/modules/cookies/Makefile.inc $(srcdir)/knot/modules/dnsproxy/Makefile.inc $(srcdir)/knot/modules/dnstap/Makefile.inc $(srcdir)/knot/modules/geoip/Makefile.inc $(srcdir)/knot/modules/noudp/Makefile.inc $(srcdir)/knot/modules/onlinesign/Makefile.inc $(srcdir)/knot/modules/queryacl/Makefile.inc $(srcdir)/knot/modules/rrl/Makefile.inc $(srcdir)/knot/modules/stats/Makefile.inc $(srcdir)/knot/modules/synthrecord/Makefile.inc $(srcdir)/knot/modules/whoami/Makefile.inc $(srcdir)/utils/Makefile.inc $(am__empty): + +$(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): + +config.h: stamp-h1 + @test -f $@ || rm -f stamp-h1 + @test -f $@ || $(MAKE) $(AM_MAKEFLAGS) stamp-h1 + +stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status + @rm -f stamp-h1 + cd $(top_builddir) && $(SHELL) ./config.status src/config.h +$(srcdir)/config.h.in: $(am__configure_deps) + ($(am__cd) $(top_srcdir) && $(AUTOHEADER)) + rm -f stamp-h1 + touch $@ + +distclean-hdr: + -rm -f config.h stamp-h1 +knotd.pc: $(top_builddir)/config.status $(srcdir)/knotd.pc.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +libknot.pc: $(top_builddir)/config.status $(srcdir)/libknot.pc.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +libdnssec.pc: $(top_builddir)/config.status $(srcdir)/libdnssec.pc.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +libzscanner.pc: $(top_builddir)/config.status $(srcdir)/libzscanner.pc.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}; \ + } + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_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}; \ + } + +install-pkglibLTLIBRARIES: $(pkglib_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(pkglib_LTLIBRARIES)'; test -n "$(pkglibdir)" || 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)$(pkglibdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pkglibdir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(pkglibdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(pkglibdir)"; \ + } + +uninstall-pkglibLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(pkglib_LTLIBRARIES)'; test -n "$(pkglibdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(pkglibdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(pkglibdir)/$$f"; \ + done + +clean-pkglibLTLIBRARIES: + -test -z "$(pkglib_LTLIBRARIES)" || rm -f $(pkglib_LTLIBRARIES) + @list='$(pkglib_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}; \ + } +knot/modules/cookies/$(am__dirstamp): + @$(MKDIR_P) knot/modules/cookies + @: > knot/modules/cookies/$(am__dirstamp) +knot/modules/cookies/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) knot/modules/cookies/$(DEPDIR) + @: > knot/modules/cookies/$(DEPDIR)/$(am__dirstamp) +knot/modules/cookies/knot_modules_cookies_la-cookies.lo: \ + knot/modules/cookies/$(am__dirstamp) \ + knot/modules/cookies/$(DEPDIR)/$(am__dirstamp) +knot/modules/$(am__dirstamp): + @$(MKDIR_P) knot/modules + @: > knot/modules/$(am__dirstamp) + +knot/modules/cookies.la: $(knot_modules_cookies_la_OBJECTS) $(knot_modules_cookies_la_DEPENDENCIES) $(EXTRA_knot_modules_cookies_la_DEPENDENCIES) knot/modules/$(am__dirstamp) + $(AM_V_CCLD)$(knot_modules_cookies_la_LINK) $(am_knot_modules_cookies_la_rpath) $(knot_modules_cookies_la_OBJECTS) $(knot_modules_cookies_la_LIBADD) $(LIBS) +knot/modules/dnsproxy/$(am__dirstamp): + @$(MKDIR_P) knot/modules/dnsproxy + @: > knot/modules/dnsproxy/$(am__dirstamp) +knot/modules/dnsproxy/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) knot/modules/dnsproxy/$(DEPDIR) + @: > knot/modules/dnsproxy/$(DEPDIR)/$(am__dirstamp) +knot/modules/dnsproxy/knot_modules_dnsproxy_la-dnsproxy.lo: \ + knot/modules/dnsproxy/$(am__dirstamp) \ + knot/modules/dnsproxy/$(DEPDIR)/$(am__dirstamp) + +knot/modules/dnsproxy.la: $(knot_modules_dnsproxy_la_OBJECTS) $(knot_modules_dnsproxy_la_DEPENDENCIES) $(EXTRA_knot_modules_dnsproxy_la_DEPENDENCIES) knot/modules/$(am__dirstamp) + $(AM_V_CCLD)$(knot_modules_dnsproxy_la_LINK) $(am_knot_modules_dnsproxy_la_rpath) $(knot_modules_dnsproxy_la_OBJECTS) $(knot_modules_dnsproxy_la_LIBADD) $(LIBS) +knot/modules/dnstap/$(am__dirstamp): + @$(MKDIR_P) knot/modules/dnstap + @: > knot/modules/dnstap/$(am__dirstamp) +knot/modules/dnstap/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) knot/modules/dnstap/$(DEPDIR) + @: > knot/modules/dnstap/$(DEPDIR)/$(am__dirstamp) +knot/modules/dnstap/knot_modules_dnstap_la-dnstap.lo: \ + knot/modules/dnstap/$(am__dirstamp) \ + knot/modules/dnstap/$(DEPDIR)/$(am__dirstamp) + +knot/modules/dnstap.la: $(knot_modules_dnstap_la_OBJECTS) $(knot_modules_dnstap_la_DEPENDENCIES) $(EXTRA_knot_modules_dnstap_la_DEPENDENCIES) knot/modules/$(am__dirstamp) + $(AM_V_CCLD)$(knot_modules_dnstap_la_LINK) $(am_knot_modules_dnstap_la_rpath) $(knot_modules_dnstap_la_OBJECTS) $(knot_modules_dnstap_la_LIBADD) $(LIBS) +knot/modules/geoip/$(am__dirstamp): + @$(MKDIR_P) knot/modules/geoip + @: > knot/modules/geoip/$(am__dirstamp) +knot/modules/geoip/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) knot/modules/geoip/$(DEPDIR) + @: > knot/modules/geoip/$(DEPDIR)/$(am__dirstamp) +knot/modules/geoip/knot_modules_geoip_la-geoip.lo: \ + knot/modules/geoip/$(am__dirstamp) \ + knot/modules/geoip/$(DEPDIR)/$(am__dirstamp) +knot/modules/geoip/knot_modules_geoip_la-geodb.lo: \ + knot/modules/geoip/$(am__dirstamp) \ + knot/modules/geoip/$(DEPDIR)/$(am__dirstamp) + +knot/modules/geoip.la: $(knot_modules_geoip_la_OBJECTS) $(knot_modules_geoip_la_DEPENDENCIES) $(EXTRA_knot_modules_geoip_la_DEPENDENCIES) knot/modules/$(am__dirstamp) + $(AM_V_CCLD)$(knot_modules_geoip_la_LINK) $(am_knot_modules_geoip_la_rpath) $(knot_modules_geoip_la_OBJECTS) $(knot_modules_geoip_la_LIBADD) $(LIBS) +knot/modules/noudp/$(am__dirstamp): + @$(MKDIR_P) knot/modules/noudp + @: > knot/modules/noudp/$(am__dirstamp) +knot/modules/noudp/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) knot/modules/noudp/$(DEPDIR) + @: > knot/modules/noudp/$(DEPDIR)/$(am__dirstamp) +knot/modules/noudp/knot_modules_noudp_la-noudp.lo: \ + knot/modules/noudp/$(am__dirstamp) \ + knot/modules/noudp/$(DEPDIR)/$(am__dirstamp) + +knot/modules/noudp.la: $(knot_modules_noudp_la_OBJECTS) $(knot_modules_noudp_la_DEPENDENCIES) $(EXTRA_knot_modules_noudp_la_DEPENDENCIES) knot/modules/$(am__dirstamp) + $(AM_V_CCLD)$(knot_modules_noudp_la_LINK) $(am_knot_modules_noudp_la_rpath) $(knot_modules_noudp_la_OBJECTS) $(knot_modules_noudp_la_LIBADD) $(LIBS) +knot/modules/onlinesign/$(am__dirstamp): + @$(MKDIR_P) knot/modules/onlinesign + @: > knot/modules/onlinesign/$(am__dirstamp) +knot/modules/onlinesign/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) knot/modules/onlinesign/$(DEPDIR) + @: > knot/modules/onlinesign/$(DEPDIR)/$(am__dirstamp) +knot/modules/onlinesign/knot_modules_onlinesign_la-onlinesign.lo: \ + knot/modules/onlinesign/$(am__dirstamp) \ + knot/modules/onlinesign/$(DEPDIR)/$(am__dirstamp) +knot/modules/onlinesign/knot_modules_onlinesign_la-nsec_next.lo: \ + knot/modules/onlinesign/$(am__dirstamp) \ + knot/modules/onlinesign/$(DEPDIR)/$(am__dirstamp) + +knot/modules/onlinesign.la: $(knot_modules_onlinesign_la_OBJECTS) $(knot_modules_onlinesign_la_DEPENDENCIES) $(EXTRA_knot_modules_onlinesign_la_DEPENDENCIES) knot/modules/$(am__dirstamp) + $(AM_V_CCLD)$(knot_modules_onlinesign_la_LINK) $(am_knot_modules_onlinesign_la_rpath) $(knot_modules_onlinesign_la_OBJECTS) $(knot_modules_onlinesign_la_LIBADD) $(LIBS) +knot/modules/queryacl/$(am__dirstamp): + @$(MKDIR_P) knot/modules/queryacl + @: > knot/modules/queryacl/$(am__dirstamp) +knot/modules/queryacl/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) knot/modules/queryacl/$(DEPDIR) + @: > knot/modules/queryacl/$(DEPDIR)/$(am__dirstamp) +knot/modules/queryacl/knot_modules_queryacl_la-queryacl.lo: \ + knot/modules/queryacl/$(am__dirstamp) \ + knot/modules/queryacl/$(DEPDIR)/$(am__dirstamp) + +knot/modules/queryacl.la: $(knot_modules_queryacl_la_OBJECTS) $(knot_modules_queryacl_la_DEPENDENCIES) $(EXTRA_knot_modules_queryacl_la_DEPENDENCIES) knot/modules/$(am__dirstamp) + $(AM_V_CCLD)$(knot_modules_queryacl_la_LINK) $(am_knot_modules_queryacl_la_rpath) $(knot_modules_queryacl_la_OBJECTS) $(knot_modules_queryacl_la_LIBADD) $(LIBS) +knot/modules/rrl/$(am__dirstamp): + @$(MKDIR_P) knot/modules/rrl + @: > knot/modules/rrl/$(am__dirstamp) +knot/modules/rrl/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) knot/modules/rrl/$(DEPDIR) + @: > knot/modules/rrl/$(DEPDIR)/$(am__dirstamp) +knot/modules/rrl/knot_modules_rrl_la-rrl.lo: \ + knot/modules/rrl/$(am__dirstamp) \ + knot/modules/rrl/$(DEPDIR)/$(am__dirstamp) +knot/modules/rrl/knot_modules_rrl_la-functions.lo: \ + knot/modules/rrl/$(am__dirstamp) \ + knot/modules/rrl/$(DEPDIR)/$(am__dirstamp) + +knot/modules/rrl.la: $(knot_modules_rrl_la_OBJECTS) $(knot_modules_rrl_la_DEPENDENCIES) $(EXTRA_knot_modules_rrl_la_DEPENDENCIES) knot/modules/$(am__dirstamp) + $(AM_V_CCLD)$(knot_modules_rrl_la_LINK) $(am_knot_modules_rrl_la_rpath) $(knot_modules_rrl_la_OBJECTS) $(knot_modules_rrl_la_LIBADD) $(LIBS) +knot/modules/stats/$(am__dirstamp): + @$(MKDIR_P) knot/modules/stats + @: > knot/modules/stats/$(am__dirstamp) +knot/modules/stats/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) knot/modules/stats/$(DEPDIR) + @: > knot/modules/stats/$(DEPDIR)/$(am__dirstamp) +knot/modules/stats/knot_modules_stats_la-stats.lo: \ + knot/modules/stats/$(am__dirstamp) \ + knot/modules/stats/$(DEPDIR)/$(am__dirstamp) + +knot/modules/stats.la: $(knot_modules_stats_la_OBJECTS) $(knot_modules_stats_la_DEPENDENCIES) $(EXTRA_knot_modules_stats_la_DEPENDENCIES) knot/modules/$(am__dirstamp) + $(AM_V_CCLD)$(knot_modules_stats_la_LINK) $(am_knot_modules_stats_la_rpath) $(knot_modules_stats_la_OBJECTS) $(knot_modules_stats_la_LIBADD) $(LIBS) +knot/modules/synthrecord/$(am__dirstamp): + @$(MKDIR_P) knot/modules/synthrecord + @: > knot/modules/synthrecord/$(am__dirstamp) +knot/modules/synthrecord/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) knot/modules/synthrecord/$(DEPDIR) + @: > knot/modules/synthrecord/$(DEPDIR)/$(am__dirstamp) +knot/modules/synthrecord/knot_modules_synthrecord_la-synthrecord.lo: \ + knot/modules/synthrecord/$(am__dirstamp) \ + knot/modules/synthrecord/$(DEPDIR)/$(am__dirstamp) + +knot/modules/synthrecord.la: $(knot_modules_synthrecord_la_OBJECTS) $(knot_modules_synthrecord_la_DEPENDENCIES) $(EXTRA_knot_modules_synthrecord_la_DEPENDENCIES) knot/modules/$(am__dirstamp) + $(AM_V_CCLD)$(knot_modules_synthrecord_la_LINK) $(am_knot_modules_synthrecord_la_rpath) $(knot_modules_synthrecord_la_OBJECTS) $(knot_modules_synthrecord_la_LIBADD) $(LIBS) +knot/modules/whoami/$(am__dirstamp): + @$(MKDIR_P) knot/modules/whoami + @: > knot/modules/whoami/$(am__dirstamp) +knot/modules/whoami/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) knot/modules/whoami/$(DEPDIR) + @: > knot/modules/whoami/$(DEPDIR)/$(am__dirstamp) +knot/modules/whoami/knot_modules_whoami_la-whoami.lo: \ + knot/modules/whoami/$(am__dirstamp) \ + knot/modules/whoami/$(DEPDIR)/$(am__dirstamp) + +knot/modules/whoami.la: $(knot_modules_whoami_la_OBJECTS) $(knot_modules_whoami_la_DEPENDENCIES) $(EXTRA_knot_modules_whoami_la_DEPENDENCIES) knot/modules/$(am__dirstamp) + $(AM_V_CCLD)$(knot_modules_whoami_la_LINK) $(am_knot_modules_whoami_la_rpath) $(knot_modules_whoami_la_OBJECTS) $(knot_modules_whoami_la_LIBADD) $(LIBS) +contrib/$(am__dirstamp): + @$(MKDIR_P) contrib + @: > contrib/$(am__dirstamp) +contrib/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) contrib/$(DEPDIR) + @: > contrib/$(DEPDIR)/$(am__dirstamp) +contrib/libcontrib_la-base32hex.lo: contrib/$(am__dirstamp) \ + contrib/$(DEPDIR)/$(am__dirstamp) +contrib/libcontrib_la-base64.lo: contrib/$(am__dirstamp) \ + contrib/$(DEPDIR)/$(am__dirstamp) +contrib/libcontrib_la-files.lo: contrib/$(am__dirstamp) \ + contrib/$(DEPDIR)/$(am__dirstamp) +contrib/libcontrib_la-getline.lo: contrib/$(am__dirstamp) \ + contrib/$(DEPDIR)/$(am__dirstamp) +contrib/libcontrib_la-mempattern.lo: contrib/$(am__dirstamp) \ + contrib/$(DEPDIR)/$(am__dirstamp) +contrib/libcontrib_la-net.lo: contrib/$(am__dirstamp) \ + contrib/$(DEPDIR)/$(am__dirstamp) +contrib/qp-trie/$(am__dirstamp): + @$(MKDIR_P) contrib/qp-trie + @: > contrib/qp-trie/$(am__dirstamp) +contrib/qp-trie/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) contrib/qp-trie/$(DEPDIR) + @: > contrib/qp-trie/$(DEPDIR)/$(am__dirstamp) +contrib/qp-trie/libcontrib_la-trie.lo: \ + contrib/qp-trie/$(am__dirstamp) \ + contrib/qp-trie/$(DEPDIR)/$(am__dirstamp) +contrib/libcontrib_la-sockaddr.lo: contrib/$(am__dirstamp) \ + contrib/$(DEPDIR)/$(am__dirstamp) +contrib/libcontrib_la-string.lo: contrib/$(am__dirstamp) \ + contrib/$(DEPDIR)/$(am__dirstamp) +contrib/libcontrib_la-time.lo: contrib/$(am__dirstamp) \ + contrib/$(DEPDIR)/$(am__dirstamp) +contrib/openbsd/$(am__dirstamp): + @$(MKDIR_P) contrib/openbsd + @: > contrib/openbsd/$(am__dirstamp) +contrib/openbsd/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) contrib/openbsd/$(DEPDIR) + @: > contrib/openbsd/$(DEPDIR)/$(am__dirstamp) +contrib/openbsd/libcontrib_la-siphash.lo: \ + contrib/openbsd/$(am__dirstamp) \ + contrib/openbsd/$(DEPDIR)/$(am__dirstamp) +contrib/openbsd/libcontrib_la-strlcat.lo: \ + contrib/openbsd/$(am__dirstamp) \ + contrib/openbsd/$(DEPDIR)/$(am__dirstamp) +contrib/openbsd/libcontrib_la-strlcpy.lo: \ + contrib/openbsd/$(am__dirstamp) \ + contrib/openbsd/$(DEPDIR)/$(am__dirstamp) +contrib/ucw/$(am__dirstamp): + @$(MKDIR_P) contrib/ucw + @: > contrib/ucw/$(am__dirstamp) +contrib/ucw/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) contrib/ucw/$(DEPDIR) + @: > contrib/ucw/$(DEPDIR)/$(am__dirstamp) +contrib/ucw/libcontrib_la-heap.lo: contrib/ucw/$(am__dirstamp) \ + contrib/ucw/$(DEPDIR)/$(am__dirstamp) +contrib/ucw/libcontrib_la-lists.lo: contrib/ucw/$(am__dirstamp) \ + contrib/ucw/$(DEPDIR)/$(am__dirstamp) +contrib/ucw/libcontrib_la-mempool.lo: contrib/ucw/$(am__dirstamp) \ + contrib/ucw/$(DEPDIR)/$(am__dirstamp) +contrib/lmdb/$(am__dirstamp): + @$(MKDIR_P) contrib/lmdb + @: > contrib/lmdb/$(am__dirstamp) +contrib/lmdb/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) contrib/lmdb/$(DEPDIR) + @: > contrib/lmdb/$(DEPDIR)/$(am__dirstamp) +contrib/lmdb/libcontrib_la-mdb.lo: contrib/lmdb/$(am__dirstamp) \ + contrib/lmdb/$(DEPDIR)/$(am__dirstamp) +contrib/lmdb/libcontrib_la-midl.lo: contrib/lmdb/$(am__dirstamp) \ + contrib/lmdb/$(DEPDIR)/$(am__dirstamp) + +libcontrib.la: $(libcontrib_la_OBJECTS) $(libcontrib_la_DEPENDENCIES) $(EXTRA_libcontrib_la_DEPENDENCIES) + $(AM_V_CCLD)$(libcontrib_la_LINK) $(libcontrib_la_OBJECTS) $(libcontrib_la_LIBADD) $(LIBS) +libdnssec/contrib/$(am__dirstamp): + @$(MKDIR_P) libdnssec/contrib + @: > libdnssec/contrib/$(am__dirstamp) +libdnssec/contrib/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) libdnssec/contrib/$(DEPDIR) + @: > libdnssec/contrib/$(DEPDIR)/$(am__dirstamp) +libdnssec/contrib/libdnssec_la-vpool.lo: \ + libdnssec/contrib/$(am__dirstamp) \ + libdnssec/contrib/$(DEPDIR)/$(am__dirstamp) +libdnssec/$(am__dirstamp): + @$(MKDIR_P) libdnssec + @: > libdnssec/$(am__dirstamp) +libdnssec/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) libdnssec/$(DEPDIR) + @: > libdnssec/$(DEPDIR)/$(am__dirstamp) +libdnssec/libdnssec_la-binary.lo: libdnssec/$(am__dirstamp) \ + libdnssec/$(DEPDIR)/$(am__dirstamp) +libdnssec/libdnssec_la-crypto.lo: libdnssec/$(am__dirstamp) \ + libdnssec/$(DEPDIR)/$(am__dirstamp) +libdnssec/libdnssec_la-error.lo: libdnssec/$(am__dirstamp) \ + libdnssec/$(DEPDIR)/$(am__dirstamp) +libdnssec/key/$(am__dirstamp): + @$(MKDIR_P) libdnssec/key + @: > libdnssec/key/$(am__dirstamp) +libdnssec/key/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) libdnssec/key/$(DEPDIR) + @: > libdnssec/key/$(DEPDIR)/$(am__dirstamp) +libdnssec/key/libdnssec_la-algorithm.lo: \ + libdnssec/key/$(am__dirstamp) \ + libdnssec/key/$(DEPDIR)/$(am__dirstamp) +libdnssec/key/libdnssec_la-convert.lo: libdnssec/key/$(am__dirstamp) \ + libdnssec/key/$(DEPDIR)/$(am__dirstamp) +libdnssec/key/libdnssec_la-dnskey.lo: libdnssec/key/$(am__dirstamp) \ + libdnssec/key/$(DEPDIR)/$(am__dirstamp) +libdnssec/key/libdnssec_la-ds.lo: libdnssec/key/$(am__dirstamp) \ + libdnssec/key/$(DEPDIR)/$(am__dirstamp) +libdnssec/key/libdnssec_la-key.lo: libdnssec/key/$(am__dirstamp) \ + libdnssec/key/$(DEPDIR)/$(am__dirstamp) +libdnssec/key/libdnssec_la-keytag.lo: libdnssec/key/$(am__dirstamp) \ + libdnssec/key/$(DEPDIR)/$(am__dirstamp) +libdnssec/key/libdnssec_la-privkey.lo: libdnssec/key/$(am__dirstamp) \ + libdnssec/key/$(DEPDIR)/$(am__dirstamp) +libdnssec/key/libdnssec_la-simple.lo: libdnssec/key/$(am__dirstamp) \ + libdnssec/key/$(DEPDIR)/$(am__dirstamp) +libdnssec/libdnssec_la-keyid.lo: libdnssec/$(am__dirstamp) \ + libdnssec/$(DEPDIR)/$(am__dirstamp) +libdnssec/keystore/$(am__dirstamp): + @$(MKDIR_P) libdnssec/keystore + @: > libdnssec/keystore/$(am__dirstamp) +libdnssec/keystore/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) libdnssec/keystore/$(DEPDIR) + @: > libdnssec/keystore/$(DEPDIR)/$(am__dirstamp) +libdnssec/keystore/libdnssec_la-keystore.lo: \ + libdnssec/keystore/$(am__dirstamp) \ + libdnssec/keystore/$(DEPDIR)/$(am__dirstamp) +libdnssec/keystore/libdnssec_la-pkcs11.lo: \ + libdnssec/keystore/$(am__dirstamp) \ + libdnssec/keystore/$(DEPDIR)/$(am__dirstamp) +libdnssec/keystore/libdnssec_la-pkcs8.lo: \ + libdnssec/keystore/$(am__dirstamp) \ + libdnssec/keystore/$(DEPDIR)/$(am__dirstamp) +libdnssec/keystore/libdnssec_la-pkcs8_dir.lo: \ + libdnssec/keystore/$(am__dirstamp) \ + libdnssec/keystore/$(DEPDIR)/$(am__dirstamp) +libdnssec/list/$(am__dirstamp): + @$(MKDIR_P) libdnssec/list + @: > libdnssec/list/$(am__dirstamp) +libdnssec/list/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) libdnssec/list/$(DEPDIR) + @: > libdnssec/list/$(DEPDIR)/$(am__dirstamp) +libdnssec/list/libdnssec_la-list.lo: libdnssec/list/$(am__dirstamp) \ + libdnssec/list/$(DEPDIR)/$(am__dirstamp) +libdnssec/nsec/$(am__dirstamp): + @$(MKDIR_P) libdnssec/nsec + @: > libdnssec/nsec/$(am__dirstamp) +libdnssec/nsec/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) libdnssec/nsec/$(DEPDIR) + @: > libdnssec/nsec/$(DEPDIR)/$(am__dirstamp) +libdnssec/nsec/libdnssec_la-bitmap.lo: libdnssec/nsec/$(am__dirstamp) \ + libdnssec/nsec/$(DEPDIR)/$(am__dirstamp) +libdnssec/nsec/libdnssec_la-hash.lo: libdnssec/nsec/$(am__dirstamp) \ + libdnssec/nsec/$(DEPDIR)/$(am__dirstamp) +libdnssec/nsec/libdnssec_la-nsec.lo: libdnssec/nsec/$(am__dirstamp) \ + libdnssec/nsec/$(DEPDIR)/$(am__dirstamp) +libdnssec/p11/$(am__dirstamp): + @$(MKDIR_P) libdnssec/p11 + @: > libdnssec/p11/$(am__dirstamp) +libdnssec/p11/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) libdnssec/p11/$(DEPDIR) + @: > libdnssec/p11/$(DEPDIR)/$(am__dirstamp) +libdnssec/p11/libdnssec_la-p11.lo: libdnssec/p11/$(am__dirstamp) \ + libdnssec/p11/$(DEPDIR)/$(am__dirstamp) +libdnssec/libdnssec_la-random.lo: libdnssec/$(am__dirstamp) \ + libdnssec/$(DEPDIR)/$(am__dirstamp) +libdnssec/sign/$(am__dirstamp): + @$(MKDIR_P) libdnssec/sign + @: > libdnssec/sign/$(am__dirstamp) +libdnssec/sign/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) libdnssec/sign/$(DEPDIR) + @: > libdnssec/sign/$(DEPDIR)/$(am__dirstamp) +libdnssec/sign/libdnssec_la-der.lo: libdnssec/sign/$(am__dirstamp) \ + libdnssec/sign/$(DEPDIR)/$(am__dirstamp) +libdnssec/sign/libdnssec_la-sign.lo: libdnssec/sign/$(am__dirstamp) \ + libdnssec/sign/$(DEPDIR)/$(am__dirstamp) +libdnssec/libdnssec_la-tsig.lo: libdnssec/$(am__dirstamp) \ + libdnssec/$(DEPDIR)/$(am__dirstamp) + +libdnssec.la: $(libdnssec_la_OBJECTS) $(libdnssec_la_DEPENDENCIES) $(EXTRA_libdnssec_la_DEPENDENCIES) + $(AM_V_CCLD)$(libdnssec_la_LINK) -rpath $(libdir) $(libdnssec_la_OBJECTS) $(libdnssec_la_LIBADD) $(LIBS) +contrib/dnstap/$(am__dirstamp): + @$(MKDIR_P) contrib/dnstap + @: > contrib/dnstap/$(am__dirstamp) +contrib/dnstap/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) contrib/dnstap/$(DEPDIR) + @: > contrib/dnstap/$(DEPDIR)/$(am__dirstamp) +contrib/dnstap/libdnstap_la-convert.lo: \ + contrib/dnstap/$(am__dirstamp) \ + contrib/dnstap/$(DEPDIR)/$(am__dirstamp) +contrib/dnstap/libdnstap_la-dnstap.lo: contrib/dnstap/$(am__dirstamp) \ + contrib/dnstap/$(DEPDIR)/$(am__dirstamp) +contrib/dnstap/libdnstap_la-message.lo: \ + contrib/dnstap/$(am__dirstamp) \ + contrib/dnstap/$(DEPDIR)/$(am__dirstamp) +contrib/dnstap/libdnstap_la-reader.lo: contrib/dnstap/$(am__dirstamp) \ + contrib/dnstap/$(DEPDIR)/$(am__dirstamp) +contrib/dnstap/libdnstap_la-writer.lo: contrib/dnstap/$(am__dirstamp) \ + contrib/dnstap/$(DEPDIR)/$(am__dirstamp) +contrib/dnstap/libdnstap_la-dnstap.pb-c.lo: \ + contrib/dnstap/$(am__dirstamp) \ + contrib/dnstap/$(DEPDIR)/$(am__dirstamp) + +libdnstap.la: $(libdnstap_la_OBJECTS) $(libdnstap_la_DEPENDENCIES) $(EXTRA_libdnstap_la_DEPENDENCIES) + $(AM_V_CCLD)$(libdnstap_la_LINK) $(am_libdnstap_la_rpath) $(libdnstap_la_OBJECTS) $(libdnstap_la_LIBADD) $(LIBS) +libknot/$(am__dirstamp): + @$(MKDIR_P) libknot + @: > libknot/$(am__dirstamp) +libknot/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) libknot/$(DEPDIR) + @: > libknot/$(DEPDIR)/$(am__dirstamp) +libknot/libknot_la-codes.lo: libknot/$(am__dirstamp) \ + libknot/$(DEPDIR)/$(am__dirstamp) +libknot/control/$(am__dirstamp): + @$(MKDIR_P) libknot/control + @: > libknot/control/$(am__dirstamp) +libknot/control/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) libknot/control/$(DEPDIR) + @: > libknot/control/$(DEPDIR)/$(am__dirstamp) +libknot/control/libknot_la-control.lo: \ + libknot/control/$(am__dirstamp) \ + libknot/control/$(DEPDIR)/$(am__dirstamp) +libknot/libknot_la-cookies.lo: libknot/$(am__dirstamp) \ + libknot/$(DEPDIR)/$(am__dirstamp) +libknot/libknot_la-descriptor.lo: libknot/$(am__dirstamp) \ + libknot/$(DEPDIR)/$(am__dirstamp) +libknot/libknot_la-dname.lo: libknot/$(am__dirstamp) \ + libknot/$(DEPDIR)/$(am__dirstamp) +libknot/libknot_la-error.lo: libknot/$(am__dirstamp) \ + libknot/$(DEPDIR)/$(am__dirstamp) +libknot/db/$(am__dirstamp): + @$(MKDIR_P) libknot/db + @: > libknot/db/$(am__dirstamp) +libknot/db/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) libknot/db/$(DEPDIR) + @: > libknot/db/$(DEPDIR)/$(am__dirstamp) +libknot/db/libknot_la-db_lmdb.lo: libknot/db/$(am__dirstamp) \ + libknot/db/$(DEPDIR)/$(am__dirstamp) +libknot/db/libknot_la-db_trie.lo: libknot/db/$(am__dirstamp) \ + libknot/db/$(DEPDIR)/$(am__dirstamp) +libknot/packet/$(am__dirstamp): + @$(MKDIR_P) libknot/packet + @: > libknot/packet/$(am__dirstamp) +libknot/packet/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) libknot/packet/$(DEPDIR) + @: > libknot/packet/$(DEPDIR)/$(am__dirstamp) +libknot/packet/libknot_la-pkt.lo: libknot/packet/$(am__dirstamp) \ + libknot/packet/$(DEPDIR)/$(am__dirstamp) +libknot/packet/libknot_la-rrset-wire.lo: \ + libknot/packet/$(am__dirstamp) \ + libknot/packet/$(DEPDIR)/$(am__dirstamp) +libknot/libknot_la-rdataset.lo: libknot/$(am__dirstamp) \ + libknot/$(DEPDIR)/$(am__dirstamp) +libknot/libknot_la-rrset-dump.lo: libknot/$(am__dirstamp) \ + libknot/$(DEPDIR)/$(am__dirstamp) +libknot/libknot_la-rrset.lo: libknot/$(am__dirstamp) \ + libknot/$(DEPDIR)/$(am__dirstamp) +libknot/rrtype/$(am__dirstamp): + @$(MKDIR_P) libknot/rrtype + @: > libknot/rrtype/$(am__dirstamp) +libknot/rrtype/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) libknot/rrtype/$(DEPDIR) + @: > libknot/rrtype/$(DEPDIR)/$(am__dirstamp) +libknot/rrtype/libknot_la-naptr.lo: libknot/rrtype/$(am__dirstamp) \ + libknot/rrtype/$(DEPDIR)/$(am__dirstamp) +libknot/rrtype/libknot_la-opt.lo: libknot/rrtype/$(am__dirstamp) \ + libknot/rrtype/$(DEPDIR)/$(am__dirstamp) +libknot/rrtype/libknot_la-tsig.lo: libknot/rrtype/$(am__dirstamp) \ + libknot/rrtype/$(DEPDIR)/$(am__dirstamp) +libknot/libknot_la-tsig-op.lo: libknot/$(am__dirstamp) \ + libknot/$(DEPDIR)/$(am__dirstamp) +libknot/libknot_la-tsig.lo: libknot/$(am__dirstamp) \ + libknot/$(DEPDIR)/$(am__dirstamp) +libknot/yparser/$(am__dirstamp): + @$(MKDIR_P) libknot/yparser + @: > libknot/yparser/$(am__dirstamp) +libknot/yparser/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) libknot/yparser/$(DEPDIR) + @: > libknot/yparser/$(DEPDIR)/$(am__dirstamp) +libknot/yparser/libknot_la-yparser.lo: \ + libknot/yparser/$(am__dirstamp) \ + libknot/yparser/$(DEPDIR)/$(am__dirstamp) +libknot/yparser/libknot_la-ypbody.lo: libknot/yparser/$(am__dirstamp) \ + libknot/yparser/$(DEPDIR)/$(am__dirstamp) +libknot/yparser/libknot_la-ypformat.lo: \ + libknot/yparser/$(am__dirstamp) \ + libknot/yparser/$(DEPDIR)/$(am__dirstamp) +libknot/yparser/libknot_la-ypschema.lo: \ + libknot/yparser/$(am__dirstamp) \ + libknot/yparser/$(DEPDIR)/$(am__dirstamp) +libknot/yparser/libknot_la-yptrafo.lo: \ + libknot/yparser/$(am__dirstamp) \ + libknot/yparser/$(DEPDIR)/$(am__dirstamp) + +libknot.la: $(libknot_la_OBJECTS) $(libknot_la_DEPENDENCIES) $(EXTRA_libknot_la_DEPENDENCIES) + $(AM_V_CCLD)$(libknot_la_LINK) -rpath $(libdir) $(libknot_la_OBJECTS) $(libknot_la_LIBADD) $(LIBS) +knot/conf/$(am__dirstamp): + @$(MKDIR_P) knot/conf + @: > knot/conf/$(am__dirstamp) +knot/conf/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) knot/conf/$(DEPDIR) + @: > knot/conf/$(DEPDIR)/$(am__dirstamp) +knot/conf/libknotd_la-base.lo: knot/conf/$(am__dirstamp) \ + knot/conf/$(DEPDIR)/$(am__dirstamp) +knot/conf/libknotd_la-conf.lo: knot/conf/$(am__dirstamp) \ + knot/conf/$(DEPDIR)/$(am__dirstamp) +knot/conf/libknotd_la-confdb.lo: knot/conf/$(am__dirstamp) \ + knot/conf/$(DEPDIR)/$(am__dirstamp) +knot/conf/libknotd_la-confio.lo: knot/conf/$(am__dirstamp) \ + knot/conf/$(DEPDIR)/$(am__dirstamp) +knot/conf/libknotd_la-migration.lo: knot/conf/$(am__dirstamp) \ + knot/conf/$(DEPDIR)/$(am__dirstamp) +knot/conf/libknotd_la-module.lo: knot/conf/$(am__dirstamp) \ + knot/conf/$(DEPDIR)/$(am__dirstamp) +knot/conf/libknotd_la-schema.lo: knot/conf/$(am__dirstamp) \ + knot/conf/$(DEPDIR)/$(am__dirstamp) +knot/conf/libknotd_la-tools.lo: knot/conf/$(am__dirstamp) \ + knot/conf/$(DEPDIR)/$(am__dirstamp) +knot/ctl/$(am__dirstamp): + @$(MKDIR_P) knot/ctl + @: > knot/ctl/$(am__dirstamp) +knot/ctl/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) knot/ctl/$(DEPDIR) + @: > knot/ctl/$(DEPDIR)/$(am__dirstamp) +knot/ctl/libknotd_la-commands.lo: knot/ctl/$(am__dirstamp) \ + knot/ctl/$(DEPDIR)/$(am__dirstamp) +knot/ctl/libknotd_la-process.lo: knot/ctl/$(am__dirstamp) \ + knot/ctl/$(DEPDIR)/$(am__dirstamp) +knot/dnssec/$(am__dirstamp): + @$(MKDIR_P) knot/dnssec + @: > knot/dnssec/$(am__dirstamp) +knot/dnssec/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) knot/dnssec/$(DEPDIR) + @: > knot/dnssec/$(DEPDIR)/$(am__dirstamp) +knot/dnssec/libknotd_la-context.lo: knot/dnssec/$(am__dirstamp) \ + knot/dnssec/$(DEPDIR)/$(am__dirstamp) +knot/dnssec/libknotd_la-ds_query.lo: knot/dnssec/$(am__dirstamp) \ + knot/dnssec/$(DEPDIR)/$(am__dirstamp) +knot/dnssec/kasp/$(am__dirstamp): + @$(MKDIR_P) knot/dnssec/kasp + @: > knot/dnssec/kasp/$(am__dirstamp) +knot/dnssec/kasp/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) knot/dnssec/kasp/$(DEPDIR) + @: > knot/dnssec/kasp/$(DEPDIR)/$(am__dirstamp) +knot/dnssec/kasp/libknotd_la-kasp_db.lo: \ + knot/dnssec/kasp/$(am__dirstamp) \ + knot/dnssec/kasp/$(DEPDIR)/$(am__dirstamp) +knot/dnssec/kasp/libknotd_la-kasp_zone.lo: \ + knot/dnssec/kasp/$(am__dirstamp) \ + knot/dnssec/kasp/$(DEPDIR)/$(am__dirstamp) +knot/dnssec/kasp/libknotd_la-keystate.lo: \ + knot/dnssec/kasp/$(am__dirstamp) \ + knot/dnssec/kasp/$(DEPDIR)/$(am__dirstamp) +knot/dnssec/kasp/libknotd_la-keystore.lo: \ + knot/dnssec/kasp/$(am__dirstamp) \ + knot/dnssec/kasp/$(DEPDIR)/$(am__dirstamp) +knot/dnssec/libknotd_la-key-events.lo: knot/dnssec/$(am__dirstamp) \ + knot/dnssec/$(DEPDIR)/$(am__dirstamp) +knot/dnssec/libknotd_la-nsec-chain.lo: knot/dnssec/$(am__dirstamp) \ + knot/dnssec/$(DEPDIR)/$(am__dirstamp) +knot/dnssec/libknotd_la-nsec3-chain.lo: knot/dnssec/$(am__dirstamp) \ + knot/dnssec/$(DEPDIR)/$(am__dirstamp) +knot/dnssec/libknotd_la-policy.lo: knot/dnssec/$(am__dirstamp) \ + knot/dnssec/$(DEPDIR)/$(am__dirstamp) +knot/dnssec/libknotd_la-rrset-sign.lo: knot/dnssec/$(am__dirstamp) \ + knot/dnssec/$(DEPDIR)/$(am__dirstamp) +knot/dnssec/libknotd_la-zone-events.lo: knot/dnssec/$(am__dirstamp) \ + knot/dnssec/$(DEPDIR)/$(am__dirstamp) +knot/dnssec/libknotd_la-zone-keys.lo: knot/dnssec/$(am__dirstamp) \ + knot/dnssec/$(DEPDIR)/$(am__dirstamp) +knot/dnssec/libknotd_la-zone-nsec.lo: knot/dnssec/$(am__dirstamp) \ + knot/dnssec/$(DEPDIR)/$(am__dirstamp) +knot/dnssec/libknotd_la-zone-sign.lo: knot/dnssec/$(am__dirstamp) \ + knot/dnssec/$(DEPDIR)/$(am__dirstamp) +knot/events/$(am__dirstamp): + @$(MKDIR_P) knot/events + @: > knot/events/$(am__dirstamp) +knot/events/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) knot/events/$(DEPDIR) + @: > knot/events/$(DEPDIR)/$(am__dirstamp) +knot/events/libknotd_la-events.lo: knot/events/$(am__dirstamp) \ + knot/events/$(DEPDIR)/$(am__dirstamp) +knot/events/handlers/$(am__dirstamp): + @$(MKDIR_P) knot/events/handlers + @: > knot/events/handlers/$(am__dirstamp) +knot/events/handlers/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) knot/events/handlers/$(DEPDIR) + @: > knot/events/handlers/$(DEPDIR)/$(am__dirstamp) +knot/events/handlers/libknotd_la-dnssec.lo: \ + knot/events/handlers/$(am__dirstamp) \ + knot/events/handlers/$(DEPDIR)/$(am__dirstamp) +knot/events/handlers/libknotd_la-expire.lo: \ + knot/events/handlers/$(am__dirstamp) \ + knot/events/handlers/$(DEPDIR)/$(am__dirstamp) +knot/events/handlers/libknotd_la-flush.lo: \ + knot/events/handlers/$(am__dirstamp) \ + knot/events/handlers/$(DEPDIR)/$(am__dirstamp) +knot/events/handlers/libknotd_la-freeze_thaw.lo: \ + knot/events/handlers/$(am__dirstamp) \ + knot/events/handlers/$(DEPDIR)/$(am__dirstamp) +knot/events/handlers/libknotd_la-load.lo: \ + knot/events/handlers/$(am__dirstamp) \ + knot/events/handlers/$(DEPDIR)/$(am__dirstamp) +knot/events/handlers/libknotd_la-notify.lo: \ + knot/events/handlers/$(am__dirstamp) \ + knot/events/handlers/$(DEPDIR)/$(am__dirstamp) +knot/events/handlers/libknotd_la-nsec3resalt.lo: \ + knot/events/handlers/$(am__dirstamp) \ + knot/events/handlers/$(DEPDIR)/$(am__dirstamp) +knot/events/handlers/libknotd_la-refresh.lo: \ + knot/events/handlers/$(am__dirstamp) \ + knot/events/handlers/$(DEPDIR)/$(am__dirstamp) +knot/events/handlers/libknotd_la-update.lo: \ + knot/events/handlers/$(am__dirstamp) \ + knot/events/handlers/$(DEPDIR)/$(am__dirstamp) +knot/events/handlers/libknotd_la-parent_ds_query.lo: \ + knot/events/handlers/$(am__dirstamp) \ + knot/events/handlers/$(DEPDIR)/$(am__dirstamp) +knot/events/libknotd_la-replan.lo: knot/events/$(am__dirstamp) \ + knot/events/$(DEPDIR)/$(am__dirstamp) +knot/nameserver/$(am__dirstamp): + @$(MKDIR_P) knot/nameserver + @: > knot/nameserver/$(am__dirstamp) +knot/nameserver/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) knot/nameserver/$(DEPDIR) + @: > knot/nameserver/$(DEPDIR)/$(am__dirstamp) +knot/nameserver/libknotd_la-axfr.lo: knot/nameserver/$(am__dirstamp) \ + knot/nameserver/$(DEPDIR)/$(am__dirstamp) +knot/nameserver/libknotd_la-chaos.lo: knot/nameserver/$(am__dirstamp) \ + knot/nameserver/$(DEPDIR)/$(am__dirstamp) +knot/nameserver/libknotd_la-internet.lo: \ + knot/nameserver/$(am__dirstamp) \ + knot/nameserver/$(DEPDIR)/$(am__dirstamp) +knot/nameserver/libknotd_la-ixfr.lo: knot/nameserver/$(am__dirstamp) \ + knot/nameserver/$(DEPDIR)/$(am__dirstamp) +knot/nameserver/libknotd_la-notify.lo: \ + knot/nameserver/$(am__dirstamp) \ + knot/nameserver/$(DEPDIR)/$(am__dirstamp) +knot/nameserver/libknotd_la-nsec_proofs.lo: \ + knot/nameserver/$(am__dirstamp) \ + knot/nameserver/$(DEPDIR)/$(am__dirstamp) +knot/nameserver/libknotd_la-process_query.lo: \ + knot/nameserver/$(am__dirstamp) \ + knot/nameserver/$(DEPDIR)/$(am__dirstamp) +knot/nameserver/libknotd_la-query_module.lo: \ + knot/nameserver/$(am__dirstamp) \ + knot/nameserver/$(DEPDIR)/$(am__dirstamp) +knot/nameserver/libknotd_la-tsig_ctx.lo: \ + knot/nameserver/$(am__dirstamp) \ + knot/nameserver/$(DEPDIR)/$(am__dirstamp) +knot/nameserver/libknotd_la-update.lo: \ + knot/nameserver/$(am__dirstamp) \ + knot/nameserver/$(DEPDIR)/$(am__dirstamp) +knot/nameserver/libknotd_la-xfr.lo: knot/nameserver/$(am__dirstamp) \ + knot/nameserver/$(DEPDIR)/$(am__dirstamp) +knot/query/$(am__dirstamp): + @$(MKDIR_P) knot/query + @: > knot/query/$(am__dirstamp) +knot/query/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) knot/query/$(DEPDIR) + @: > knot/query/$(DEPDIR)/$(am__dirstamp) +knot/query/libknotd_la-capture.lo: knot/query/$(am__dirstamp) \ + knot/query/$(DEPDIR)/$(am__dirstamp) +knot/query/libknotd_la-query.lo: knot/query/$(am__dirstamp) \ + knot/query/$(DEPDIR)/$(am__dirstamp) +knot/query/libknotd_la-requestor.lo: knot/query/$(am__dirstamp) \ + knot/query/$(DEPDIR)/$(am__dirstamp) +knot/common/$(am__dirstamp): + @$(MKDIR_P) knot/common + @: > knot/common/$(am__dirstamp) +knot/common/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) knot/common/$(DEPDIR) + @: > knot/common/$(DEPDIR)/$(am__dirstamp) +knot/common/libknotd_la-evsched.lo: knot/common/$(am__dirstamp) \ + knot/common/$(DEPDIR)/$(am__dirstamp) +knot/common/libknotd_la-fdset.lo: knot/common/$(am__dirstamp) \ + knot/common/$(DEPDIR)/$(am__dirstamp) +knot/common/libknotd_la-log.lo: knot/common/$(am__dirstamp) \ + knot/common/$(DEPDIR)/$(am__dirstamp) +knot/common/libknotd_la-process.lo: knot/common/$(am__dirstamp) \ + knot/common/$(DEPDIR)/$(am__dirstamp) +knot/common/libknotd_la-ref.lo: knot/common/$(am__dirstamp) \ + knot/common/$(DEPDIR)/$(am__dirstamp) +knot/common/libknotd_la-stats.lo: knot/common/$(am__dirstamp) \ + knot/common/$(DEPDIR)/$(am__dirstamp) +knot/server/$(am__dirstamp): + @$(MKDIR_P) knot/server + @: > knot/server/$(am__dirstamp) +knot/server/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) knot/server/$(DEPDIR) + @: > knot/server/$(DEPDIR)/$(am__dirstamp) +knot/server/libknotd_la-dthreads.lo: knot/server/$(am__dirstamp) \ + knot/server/$(DEPDIR)/$(am__dirstamp) +knot/journal/$(am__dirstamp): + @$(MKDIR_P) knot/journal + @: > knot/journal/$(am__dirstamp) +knot/journal/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) knot/journal/$(DEPDIR) + @: > knot/journal/$(DEPDIR)/$(am__dirstamp) +knot/journal/libknotd_la-chgset_ctx.lo: knot/journal/$(am__dirstamp) \ + knot/journal/$(DEPDIR)/$(am__dirstamp) +knot/journal/libknotd_la-journal.lo: knot/journal/$(am__dirstamp) \ + knot/journal/$(DEPDIR)/$(am__dirstamp) +knot/journal/libknotd_la-serialization.lo: \ + knot/journal/$(am__dirstamp) \ + knot/journal/$(DEPDIR)/$(am__dirstamp) +knot/server/libknotd_la-server.lo: knot/server/$(am__dirstamp) \ + knot/server/$(DEPDIR)/$(am__dirstamp) +knot/server/libknotd_la-tcp-handler.lo: knot/server/$(am__dirstamp) \ + knot/server/$(DEPDIR)/$(am__dirstamp) +knot/server/libknotd_la-udp-handler.lo: knot/server/$(am__dirstamp) \ + knot/server/$(DEPDIR)/$(am__dirstamp) +knot/updates/$(am__dirstamp): + @$(MKDIR_P) knot/updates + @: > knot/updates/$(am__dirstamp) +knot/updates/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) knot/updates/$(DEPDIR) + @: > knot/updates/$(DEPDIR)/$(am__dirstamp) +knot/updates/libknotd_la-acl.lo: knot/updates/$(am__dirstamp) \ + knot/updates/$(DEPDIR)/$(am__dirstamp) +knot/updates/libknotd_la-apply.lo: knot/updates/$(am__dirstamp) \ + knot/updates/$(DEPDIR)/$(am__dirstamp) +knot/updates/libknotd_la-changesets.lo: knot/updates/$(am__dirstamp) \ + knot/updates/$(DEPDIR)/$(am__dirstamp) +knot/updates/libknotd_la-ddns.lo: knot/updates/$(am__dirstamp) \ + knot/updates/$(DEPDIR)/$(am__dirstamp) +knot/updates/libknotd_la-zone-update.lo: knot/updates/$(am__dirstamp) \ + knot/updates/$(DEPDIR)/$(am__dirstamp) +knot/worker/$(am__dirstamp): + @$(MKDIR_P) knot/worker + @: > knot/worker/$(am__dirstamp) +knot/worker/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) knot/worker/$(DEPDIR) + @: > knot/worker/$(DEPDIR)/$(am__dirstamp) +knot/worker/libknotd_la-pool.lo: knot/worker/$(am__dirstamp) \ + knot/worker/$(DEPDIR)/$(am__dirstamp) +knot/worker/libknotd_la-queue.lo: knot/worker/$(am__dirstamp) \ + knot/worker/$(DEPDIR)/$(am__dirstamp) +knot/zone/$(am__dirstamp): + @$(MKDIR_P) knot/zone + @: > knot/zone/$(am__dirstamp) +knot/zone/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) knot/zone/$(DEPDIR) + @: > knot/zone/$(DEPDIR)/$(am__dirstamp) +knot/zone/libknotd_la-contents.lo: knot/zone/$(am__dirstamp) \ + knot/zone/$(DEPDIR)/$(am__dirstamp) +knot/zone/libknotd_la-node.lo: knot/zone/$(am__dirstamp) \ + knot/zone/$(DEPDIR)/$(am__dirstamp) +knot/zone/libknotd_la-semantic-check.lo: knot/zone/$(am__dirstamp) \ + knot/zone/$(DEPDIR)/$(am__dirstamp) +knot/zone/libknotd_la-serial.lo: knot/zone/$(am__dirstamp) \ + knot/zone/$(DEPDIR)/$(am__dirstamp) +knot/zone/libknotd_la-timers.lo: knot/zone/$(am__dirstamp) \ + knot/zone/$(DEPDIR)/$(am__dirstamp) +knot/zone/libknotd_la-zone-diff.lo: knot/zone/$(am__dirstamp) \ + knot/zone/$(DEPDIR)/$(am__dirstamp) +knot/zone/libknotd_la-zone-dump.lo: knot/zone/$(am__dirstamp) \ + knot/zone/$(DEPDIR)/$(am__dirstamp) +knot/zone/libknotd_la-zone-load.lo: knot/zone/$(am__dirstamp) \ + knot/zone/$(DEPDIR)/$(am__dirstamp) +knot/zone/libknotd_la-zone-tree.lo: knot/zone/$(am__dirstamp) \ + knot/zone/$(DEPDIR)/$(am__dirstamp) +knot/zone/libknotd_la-zone.lo: knot/zone/$(am__dirstamp) \ + knot/zone/$(DEPDIR)/$(am__dirstamp) +knot/zone/libknotd_la-zonedb-load.lo: knot/zone/$(am__dirstamp) \ + knot/zone/$(DEPDIR)/$(am__dirstamp) +knot/zone/libknotd_la-zonedb.lo: knot/zone/$(am__dirstamp) \ + knot/zone/$(DEPDIR)/$(am__dirstamp) +knot/zone/libknotd_la-zonefile.lo: knot/zone/$(am__dirstamp) \ + knot/zone/$(DEPDIR)/$(am__dirstamp) +knot/modules/cookies/libknotd_la-cookies.lo: \ + knot/modules/cookies/$(am__dirstamp) \ + knot/modules/cookies/$(DEPDIR)/$(am__dirstamp) +knot/modules/dnsproxy/libknotd_la-dnsproxy.lo: \ + knot/modules/dnsproxy/$(am__dirstamp) \ + knot/modules/dnsproxy/$(DEPDIR)/$(am__dirstamp) +knot/modules/dnstap/libknotd_la-dnstap.lo: \ + knot/modules/dnstap/$(am__dirstamp) \ + knot/modules/dnstap/$(DEPDIR)/$(am__dirstamp) +knot/modules/geoip/libknotd_la-geoip.lo: \ + knot/modules/geoip/$(am__dirstamp) \ + knot/modules/geoip/$(DEPDIR)/$(am__dirstamp) +knot/modules/geoip/libknotd_la-geodb.lo: \ + knot/modules/geoip/$(am__dirstamp) \ + knot/modules/geoip/$(DEPDIR)/$(am__dirstamp) +knot/modules/noudp/libknotd_la-noudp.lo: \ + knot/modules/noudp/$(am__dirstamp) \ + knot/modules/noudp/$(DEPDIR)/$(am__dirstamp) +knot/modules/onlinesign/libknotd_la-onlinesign.lo: \ + knot/modules/onlinesign/$(am__dirstamp) \ + knot/modules/onlinesign/$(DEPDIR)/$(am__dirstamp) +knot/modules/onlinesign/libknotd_la-nsec_next.lo: \ + knot/modules/onlinesign/$(am__dirstamp) \ + knot/modules/onlinesign/$(DEPDIR)/$(am__dirstamp) +knot/modules/queryacl/libknotd_la-queryacl.lo: \ + knot/modules/queryacl/$(am__dirstamp) \ + knot/modules/queryacl/$(DEPDIR)/$(am__dirstamp) +knot/modules/rrl/libknotd_la-rrl.lo: knot/modules/rrl/$(am__dirstamp) \ + knot/modules/rrl/$(DEPDIR)/$(am__dirstamp) +knot/modules/rrl/libknotd_la-functions.lo: \ + knot/modules/rrl/$(am__dirstamp) \ + knot/modules/rrl/$(DEPDIR)/$(am__dirstamp) +knot/modules/stats/libknotd_la-stats.lo: \ + knot/modules/stats/$(am__dirstamp) \ + knot/modules/stats/$(DEPDIR)/$(am__dirstamp) +knot/modules/synthrecord/libknotd_la-synthrecord.lo: \ + knot/modules/synthrecord/$(am__dirstamp) \ + knot/modules/synthrecord/$(DEPDIR)/$(am__dirstamp) +knot/modules/whoami/libknotd_la-whoami.lo: \ + knot/modules/whoami/$(am__dirstamp) \ + knot/modules/whoami/$(DEPDIR)/$(am__dirstamp) + +libknotd.la: $(libknotd_la_OBJECTS) $(libknotd_la_DEPENDENCIES) $(EXTRA_libknotd_la_DEPENDENCIES) + $(AM_V_CCLD)$(libknotd_la_LINK) $(am_libknotd_la_rpath) $(libknotd_la_OBJECTS) $(libknotd_la_LIBADD) $(LIBS) +utils/common/$(am__dirstamp): + @$(MKDIR_P) utils/common + @: > utils/common/$(am__dirstamp) +utils/common/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) utils/common/$(DEPDIR) + @: > utils/common/$(DEPDIR)/$(am__dirstamp) +utils/common/libknotus_la-cert.lo: utils/common/$(am__dirstamp) \ + utils/common/$(DEPDIR)/$(am__dirstamp) +utils/common/libknotus_la-exec.lo: utils/common/$(am__dirstamp) \ + utils/common/$(DEPDIR)/$(am__dirstamp) +utils/common/libknotus_la-hex.lo: utils/common/$(am__dirstamp) \ + utils/common/$(DEPDIR)/$(am__dirstamp) +utils/common/libknotus_la-lookup.lo: utils/common/$(am__dirstamp) \ + utils/common/$(DEPDIR)/$(am__dirstamp) +utils/common/libknotus_la-msg.lo: utils/common/$(am__dirstamp) \ + utils/common/$(DEPDIR)/$(am__dirstamp) +utils/common/libknotus_la-netio.lo: utils/common/$(am__dirstamp) \ + utils/common/$(DEPDIR)/$(am__dirstamp) +utils/common/libknotus_la-params.lo: utils/common/$(am__dirstamp) \ + utils/common/$(DEPDIR)/$(am__dirstamp) +utils/common/libknotus_la-resolv.lo: utils/common/$(am__dirstamp) \ + utils/common/$(DEPDIR)/$(am__dirstamp) +utils/common/libknotus_la-sign.lo: utils/common/$(am__dirstamp) \ + utils/common/$(DEPDIR)/$(am__dirstamp) +utils/common/libknotus_la-tls.lo: utils/common/$(am__dirstamp) \ + utils/common/$(DEPDIR)/$(am__dirstamp) +utils/common/libknotus_la-token.lo: utils/common/$(am__dirstamp) \ + utils/common/$(DEPDIR)/$(am__dirstamp) + +libknotus.la: $(libknotus_la_OBJECTS) $(libknotus_la_DEPENDENCIES) $(EXTRA_libknotus_la_DEPENDENCIES) + $(AM_V_CCLD)$(libknotus_la_LINK) $(am_libknotus_la_rpath) $(libknotus_la_OBJECTS) $(libknotus_la_LIBADD) $(LIBS) +libdnssec/shared/$(am__dirstamp): + @$(MKDIR_P) libdnssec/shared + @: > libdnssec/shared/$(am__dirstamp) +libdnssec/shared/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) libdnssec/shared/$(DEPDIR) + @: > libdnssec/shared/$(DEPDIR)/$(am__dirstamp) +libdnssec/shared/libshared_la-bignum.lo: \ + libdnssec/shared/$(am__dirstamp) \ + libdnssec/shared/$(DEPDIR)/$(am__dirstamp) +libdnssec/shared/libshared_la-dname.lo: \ + libdnssec/shared/$(am__dirstamp) \ + libdnssec/shared/$(DEPDIR)/$(am__dirstamp) +libdnssec/shared/libshared_la-fs.lo: libdnssec/shared/$(am__dirstamp) \ + libdnssec/shared/$(DEPDIR)/$(am__dirstamp) +libdnssec/shared/libshared_la-hex.lo: \ + libdnssec/shared/$(am__dirstamp) \ + libdnssec/shared/$(DEPDIR)/$(am__dirstamp) +libdnssec/shared/libshared_la-keyid_gnutls.lo: \ + libdnssec/shared/$(am__dirstamp) \ + libdnssec/shared/$(DEPDIR)/$(am__dirstamp) +libdnssec/shared/libshared_la-pem.lo: \ + libdnssec/shared/$(am__dirstamp) \ + libdnssec/shared/$(DEPDIR)/$(am__dirstamp) + +libshared.la: $(libshared_la_OBJECTS) $(libshared_la_DEPENDENCIES) $(EXTRA_libshared_la_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(libshared_la_OBJECTS) $(libshared_la_LIBADD) $(LIBS) +libzscanner/$(am__dirstamp): + @$(MKDIR_P) libzscanner + @: > libzscanner/$(am__dirstamp) +libzscanner/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) libzscanner/$(DEPDIR) + @: > libzscanner/$(DEPDIR)/$(am__dirstamp) +libzscanner/libzscanner_la-error.lo: libzscanner/$(am__dirstamp) \ + libzscanner/$(DEPDIR)/$(am__dirstamp) +libzscanner/libzscanner_la-functions.lo: libzscanner/$(am__dirstamp) \ + libzscanner/$(DEPDIR)/$(am__dirstamp) +libzscanner/libzscanner_la-scanner.lo: libzscanner/$(am__dirstamp) \ + libzscanner/$(DEPDIR)/$(am__dirstamp) + +libzscanner.la: $(libzscanner_la_OBJECTS) $(libzscanner_la_DEPENDENCIES) $(EXTRA_libzscanner_la_DEPENDENCIES) + $(AM_V_CCLD)$(libzscanner_la_LINK) -rpath $(libdir) $(libzscanner_la_OBJECTS) $(libzscanner_la_LIBADD) $(LIBS) +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p \ + || test -f $$p1 \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' \ + `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(bindir)" && rm -f $$files + +clean-binPROGRAMS: + @list='$(bin_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 + +installcheck-binPROGRAMS: $(bin_PROGRAMS) + bad=0; pid=$$$$; list="$(bin_PROGRAMS)"; for p in $$list; do \ + case ' $(AM_INSTALLCHECK_STD_OPTIONS_EXEMPT) ' in \ + *" $$p "* | *" $(srcdir)/$$p "*) continue;; \ + esac; \ + f=`echo "$$p" | \ + sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \ + for opt in --help --version; do \ + if "$(DESTDIR)$(bindir)/$$f" $$opt >c$${pid}_.out \ + 2>c$${pid}_.err </dev/null \ + && test -n "`cat c$${pid}_.out`" \ + && test -z "`cat c$${pid}_.err`"; then :; \ + else echo "$$f does not support $$opt" 1>&2; bad=1; fi; \ + done; \ + done; rm -f c$${pid}_.???; exit $$bad +install-sbinPROGRAMS: $(sbin_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p \ + || test -f $$p1 \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-sbinPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' \ + `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(sbindir)" && rm -f $$files + +clean-sbinPROGRAMS: + @list='$(sbin_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +installcheck-sbinPROGRAMS: $(sbin_PROGRAMS) + bad=0; pid=$$$$; list="$(sbin_PROGRAMS)"; for p in $$list; do \ + case ' $(AM_INSTALLCHECK_STD_OPTIONS_EXEMPT) ' in \ + *" $$p "* | *" $(srcdir)/$$p "*) continue;; \ + esac; \ + f=`echo "$$p" | \ + sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \ + for opt in --help --version; do \ + if "$(DESTDIR)$(sbindir)/$$f" $$opt >c$${pid}_.out \ + 2>c$${pid}_.err </dev/null \ + && test -n "`cat c$${pid}_.out`" \ + && test -z "`cat c$${pid}_.err`"; then :; \ + else echo "$$f does not support $$opt" 1>&2; bad=1; fi; \ + done; \ + done; rm -f c$${pid}_.???; exit $$bad +utils/kdig/$(am__dirstamp): + @$(MKDIR_P) utils/kdig + @: > utils/kdig/$(am__dirstamp) +utils/kdig/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) utils/kdig/$(DEPDIR) + @: > utils/kdig/$(DEPDIR)/$(am__dirstamp) +utils/kdig/kdig-kdig_exec.$(OBJEXT): utils/kdig/$(am__dirstamp) \ + utils/kdig/$(DEPDIR)/$(am__dirstamp) +utils/kdig/kdig-kdig_main.$(OBJEXT): utils/kdig/$(am__dirstamp) \ + utils/kdig/$(DEPDIR)/$(am__dirstamp) +utils/kdig/kdig-kdig_params.$(OBJEXT): utils/kdig/$(am__dirstamp) \ + utils/kdig/$(DEPDIR)/$(am__dirstamp) + +kdig$(EXEEXT): $(kdig_OBJECTS) $(kdig_DEPENDENCIES) $(EXTRA_kdig_DEPENDENCIES) + @rm -f kdig$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(kdig_OBJECTS) $(kdig_LDADD) $(LIBS) +utils/keymgr/$(am__dirstamp): + @$(MKDIR_P) utils/keymgr + @: > utils/keymgr/$(am__dirstamp) +utils/keymgr/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) utils/keymgr/$(DEPDIR) + @: > utils/keymgr/$(DEPDIR)/$(am__dirstamp) +utils/keymgr/keymgr-bind_privkey.$(OBJEXT): \ + utils/keymgr/$(am__dirstamp) \ + utils/keymgr/$(DEPDIR)/$(am__dirstamp) +utils/keymgr/keymgr-functions.$(OBJEXT): utils/keymgr/$(am__dirstamp) \ + utils/keymgr/$(DEPDIR)/$(am__dirstamp) +utils/keymgr/keymgr-main.$(OBJEXT): utils/keymgr/$(am__dirstamp) \ + utils/keymgr/$(DEPDIR)/$(am__dirstamp) + +keymgr$(EXEEXT): $(keymgr_OBJECTS) $(keymgr_DEPENDENCIES) $(EXTRA_keymgr_DEPENDENCIES) + @rm -f keymgr$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(keymgr_OBJECTS) $(keymgr_LDADD) $(LIBS) +utils/kdig/khost-kdig_exec.$(OBJEXT): utils/kdig/$(am__dirstamp) \ + utils/kdig/$(DEPDIR)/$(am__dirstamp) +utils/kdig/khost-kdig_params.$(OBJEXT): utils/kdig/$(am__dirstamp) \ + utils/kdig/$(DEPDIR)/$(am__dirstamp) +utils/khost/$(am__dirstamp): + @$(MKDIR_P) utils/khost + @: > utils/khost/$(am__dirstamp) +utils/khost/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) utils/khost/$(DEPDIR) + @: > utils/khost/$(DEPDIR)/$(am__dirstamp) +utils/khost/khost-khost_main.$(OBJEXT): utils/khost/$(am__dirstamp) \ + utils/khost/$(DEPDIR)/$(am__dirstamp) +utils/khost/khost-khost_params.$(OBJEXT): utils/khost/$(am__dirstamp) \ + utils/khost/$(DEPDIR)/$(am__dirstamp) + +khost$(EXEEXT): $(khost_OBJECTS) $(khost_DEPENDENCIES) $(EXTRA_khost_DEPENDENCIES) + @rm -f khost$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(khost_OBJECTS) $(khost_LDADD) $(LIBS) +utils/kjournalprint/$(am__dirstamp): + @$(MKDIR_P) utils/kjournalprint + @: > utils/kjournalprint/$(am__dirstamp) +utils/kjournalprint/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) utils/kjournalprint/$(DEPDIR) + @: > utils/kjournalprint/$(DEPDIR)/$(am__dirstamp) +utils/kjournalprint/kjournalprint-main.$(OBJEXT): \ + utils/kjournalprint/$(am__dirstamp) \ + utils/kjournalprint/$(DEPDIR)/$(am__dirstamp) + +kjournalprint$(EXEEXT): $(kjournalprint_OBJECTS) $(kjournalprint_DEPENDENCIES) $(EXTRA_kjournalprint_DEPENDENCIES) + @rm -f kjournalprint$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(kjournalprint_OBJECTS) $(kjournalprint_LDADD) $(LIBS) +utils/knotc/$(am__dirstamp): + @$(MKDIR_P) utils/knotc + @: > utils/knotc/$(am__dirstamp) +utils/knotc/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) utils/knotc/$(DEPDIR) + @: > utils/knotc/$(DEPDIR)/$(am__dirstamp) +utils/knotc/knotc-commands.$(OBJEXT): utils/knotc/$(am__dirstamp) \ + utils/knotc/$(DEPDIR)/$(am__dirstamp) +utils/knotc/knotc-estimator.$(OBJEXT): utils/knotc/$(am__dirstamp) \ + utils/knotc/$(DEPDIR)/$(am__dirstamp) +utils/knotc/knotc-interactive.$(OBJEXT): utils/knotc/$(am__dirstamp) \ + utils/knotc/$(DEPDIR)/$(am__dirstamp) +utils/knotc/knotc-process.$(OBJEXT): utils/knotc/$(am__dirstamp) \ + utils/knotc/$(DEPDIR)/$(am__dirstamp) +utils/knotc/knotc-main.$(OBJEXT): utils/knotc/$(am__dirstamp) \ + utils/knotc/$(DEPDIR)/$(am__dirstamp) + +knotc$(EXEEXT): $(knotc_OBJECTS) $(knotc_DEPENDENCIES) $(EXTRA_knotc_DEPENDENCIES) + @rm -f knotc$(EXEEXT) + $(AM_V_CCLD)$(knotc_LINK) $(knotc_OBJECTS) $(knotc_LDADD) $(LIBS) +utils/knotd/$(am__dirstamp): + @$(MKDIR_P) utils/knotd + @: > utils/knotd/$(am__dirstamp) +utils/knotd/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) utils/knotd/$(DEPDIR) + @: > utils/knotd/$(DEPDIR)/$(am__dirstamp) +utils/knotd/knotd-main.$(OBJEXT): utils/knotd/$(am__dirstamp) \ + utils/knotd/$(DEPDIR)/$(am__dirstamp) + +knotd$(EXEEXT): $(knotd_OBJECTS) $(knotd_DEPENDENCIES) $(EXTRA_knotd_DEPENDENCIES) + @rm -f knotd$(EXEEXT) + $(AM_V_CCLD)$(knotd_LINK) $(knotd_OBJECTS) $(knotd_LDADD) $(LIBS) +utils/knsec3hash/$(am__dirstamp): + @$(MKDIR_P) utils/knsec3hash + @: > utils/knsec3hash/$(am__dirstamp) +utils/knsec3hash/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) utils/knsec3hash/$(DEPDIR) + @: > utils/knsec3hash/$(DEPDIR)/$(am__dirstamp) +utils/knsec3hash/knsec3hash-knsec3hash.$(OBJEXT): \ + utils/knsec3hash/$(am__dirstamp) \ + utils/knsec3hash/$(DEPDIR)/$(am__dirstamp) + +knsec3hash$(EXEEXT): $(knsec3hash_OBJECTS) $(knsec3hash_DEPENDENCIES) $(EXTRA_knsec3hash_DEPENDENCIES) + @rm -f knsec3hash$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(knsec3hash_OBJECTS) $(knsec3hash_LDADD) $(LIBS) +utils/knsupdate/$(am__dirstamp): + @$(MKDIR_P) utils/knsupdate + @: > utils/knsupdate/$(am__dirstamp) +utils/knsupdate/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) utils/knsupdate/$(DEPDIR) + @: > utils/knsupdate/$(DEPDIR)/$(am__dirstamp) +utils/knsupdate/knsupdate-knsupdate_exec.$(OBJEXT): \ + utils/knsupdate/$(am__dirstamp) \ + utils/knsupdate/$(DEPDIR)/$(am__dirstamp) +utils/knsupdate/knsupdate-knsupdate_main.$(OBJEXT): \ + utils/knsupdate/$(am__dirstamp) \ + utils/knsupdate/$(DEPDIR)/$(am__dirstamp) +utils/knsupdate/knsupdate-knsupdate_params.$(OBJEXT): \ + utils/knsupdate/$(am__dirstamp) \ + utils/knsupdate/$(DEPDIR)/$(am__dirstamp) + +knsupdate$(EXEEXT): $(knsupdate_OBJECTS) $(knsupdate_DEPENDENCIES) $(EXTRA_knsupdate_DEPENDENCIES) + @rm -f knsupdate$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(knsupdate_OBJECTS) $(knsupdate_LDADD) $(LIBS) +utils/kzonecheck/$(am__dirstamp): + @$(MKDIR_P) utils/kzonecheck + @: > utils/kzonecheck/$(am__dirstamp) +utils/kzonecheck/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) utils/kzonecheck/$(DEPDIR) + @: > utils/kzonecheck/$(DEPDIR)/$(am__dirstamp) +utils/kzonecheck/kzonecheck-main.$(OBJEXT): \ + utils/kzonecheck/$(am__dirstamp) \ + utils/kzonecheck/$(DEPDIR)/$(am__dirstamp) +utils/kzonecheck/kzonecheck-zone_check.$(OBJEXT): \ + utils/kzonecheck/$(am__dirstamp) \ + utils/kzonecheck/$(DEPDIR)/$(am__dirstamp) + +kzonecheck$(EXEEXT): $(kzonecheck_OBJECTS) $(kzonecheck_DEPENDENCIES) $(EXTRA_kzonecheck_DEPENDENCIES) + @rm -f kzonecheck$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(kzonecheck_OBJECTS) $(kzonecheck_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + -rm -f contrib/*.$(OBJEXT) + -rm -f contrib/*.lo + -rm -f contrib/dnstap/*.$(OBJEXT) + -rm -f contrib/dnstap/*.lo + -rm -f contrib/lmdb/*.$(OBJEXT) + -rm -f contrib/lmdb/*.lo + -rm -f contrib/openbsd/*.$(OBJEXT) + -rm -f contrib/openbsd/*.lo + -rm -f contrib/qp-trie/*.$(OBJEXT) + -rm -f contrib/qp-trie/*.lo + -rm -f contrib/ucw/*.$(OBJEXT) + -rm -f contrib/ucw/*.lo + -rm -f knot/common/*.$(OBJEXT) + -rm -f knot/common/*.lo + -rm -f knot/conf/*.$(OBJEXT) + -rm -f knot/conf/*.lo + -rm -f knot/ctl/*.$(OBJEXT) + -rm -f knot/ctl/*.lo + -rm -f knot/dnssec/*.$(OBJEXT) + -rm -f knot/dnssec/*.lo + -rm -f knot/dnssec/kasp/*.$(OBJEXT) + -rm -f knot/dnssec/kasp/*.lo + -rm -f knot/events/*.$(OBJEXT) + -rm -f knot/events/*.lo + -rm -f knot/events/handlers/*.$(OBJEXT) + -rm -f knot/events/handlers/*.lo + -rm -f knot/journal/*.$(OBJEXT) + -rm -f knot/journal/*.lo + -rm -f knot/modules/cookies/*.$(OBJEXT) + -rm -f knot/modules/cookies/*.lo + -rm -f knot/modules/dnsproxy/*.$(OBJEXT) + -rm -f knot/modules/dnsproxy/*.lo + -rm -f knot/modules/dnstap/*.$(OBJEXT) + -rm -f knot/modules/dnstap/*.lo + -rm -f knot/modules/geoip/*.$(OBJEXT) + -rm -f knot/modules/geoip/*.lo + -rm -f knot/modules/noudp/*.$(OBJEXT) + -rm -f knot/modules/noudp/*.lo + -rm -f knot/modules/onlinesign/*.$(OBJEXT) + -rm -f knot/modules/onlinesign/*.lo + -rm -f knot/modules/queryacl/*.$(OBJEXT) + -rm -f knot/modules/queryacl/*.lo + -rm -f knot/modules/rrl/*.$(OBJEXT) + -rm -f knot/modules/rrl/*.lo + -rm -f knot/modules/stats/*.$(OBJEXT) + -rm -f knot/modules/stats/*.lo + -rm -f knot/modules/synthrecord/*.$(OBJEXT) + -rm -f knot/modules/synthrecord/*.lo + -rm -f knot/modules/whoami/*.$(OBJEXT) + -rm -f knot/modules/whoami/*.lo + -rm -f knot/nameserver/*.$(OBJEXT) + -rm -f knot/nameserver/*.lo + -rm -f knot/query/*.$(OBJEXT) + -rm -f knot/query/*.lo + -rm -f knot/server/*.$(OBJEXT) + -rm -f knot/server/*.lo + -rm -f knot/updates/*.$(OBJEXT) + -rm -f knot/updates/*.lo + -rm -f knot/worker/*.$(OBJEXT) + -rm -f knot/worker/*.lo + -rm -f knot/zone/*.$(OBJEXT) + -rm -f knot/zone/*.lo + -rm -f libdnssec/*.$(OBJEXT) + -rm -f libdnssec/*.lo + -rm -f libdnssec/contrib/*.$(OBJEXT) + -rm -f libdnssec/contrib/*.lo + -rm -f libdnssec/key/*.$(OBJEXT) + -rm -f libdnssec/key/*.lo + -rm -f libdnssec/keystore/*.$(OBJEXT) + -rm -f libdnssec/keystore/*.lo + -rm -f libdnssec/list/*.$(OBJEXT) + -rm -f libdnssec/list/*.lo + -rm -f libdnssec/nsec/*.$(OBJEXT) + -rm -f libdnssec/nsec/*.lo + -rm -f libdnssec/p11/*.$(OBJEXT) + -rm -f libdnssec/p11/*.lo + -rm -f libdnssec/shared/*.$(OBJEXT) + -rm -f libdnssec/shared/*.lo + -rm -f libdnssec/sign/*.$(OBJEXT) + -rm -f libdnssec/sign/*.lo + -rm -f libknot/*.$(OBJEXT) + -rm -f libknot/*.lo + -rm -f libknot/control/*.$(OBJEXT) + -rm -f libknot/control/*.lo + -rm -f libknot/db/*.$(OBJEXT) + -rm -f libknot/db/*.lo + -rm -f libknot/packet/*.$(OBJEXT) + -rm -f libknot/packet/*.lo + -rm -f libknot/rrtype/*.$(OBJEXT) + -rm -f libknot/rrtype/*.lo + -rm -f libknot/yparser/*.$(OBJEXT) + -rm -f libknot/yparser/*.lo + -rm -f libzscanner/*.$(OBJEXT) + -rm -f libzscanner/*.lo + -rm -f utils/common/*.$(OBJEXT) + -rm -f utils/common/*.lo + -rm -f utils/kdig/*.$(OBJEXT) + -rm -f utils/keymgr/*.$(OBJEXT) + -rm -f utils/khost/*.$(OBJEXT) + -rm -f utils/kjournalprint/*.$(OBJEXT) + -rm -f utils/knotc/*.$(OBJEXT) + -rm -f utils/knotd/*.$(OBJEXT) + -rm -f utils/knsec3hash/*.$(OBJEXT) + -rm -f utils/knsupdate/*.$(OBJEXT) + -rm -f utils/kzonecheck/*.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/libcontrib_la-base32hex.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/libcontrib_la-base64.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/libcontrib_la-files.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/libcontrib_la-getline.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/libcontrib_la-mempattern.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/libcontrib_la-net.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/libcontrib_la-sockaddr.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/libcontrib_la-string.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@contrib/$(DEPDIR)/libcontrib_la-time.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@contrib/dnstap/$(DEPDIR)/libdnstap_la-convert.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@contrib/dnstap/$(DEPDIR)/libdnstap_la-dnstap.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@contrib/dnstap/$(DEPDIR)/libdnstap_la-dnstap.pb-c.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@contrib/dnstap/$(DEPDIR)/libdnstap_la-message.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@contrib/dnstap/$(DEPDIR)/libdnstap_la-reader.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@contrib/dnstap/$(DEPDIR)/libdnstap_la-writer.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@contrib/lmdb/$(DEPDIR)/libcontrib_la-mdb.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@contrib/lmdb/$(DEPDIR)/libcontrib_la-midl.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@contrib/openbsd/$(DEPDIR)/libcontrib_la-siphash.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@contrib/openbsd/$(DEPDIR)/libcontrib_la-strlcat.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@contrib/openbsd/$(DEPDIR)/libcontrib_la-strlcpy.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@contrib/qp-trie/$(DEPDIR)/libcontrib_la-trie.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@contrib/ucw/$(DEPDIR)/libcontrib_la-heap.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@contrib/ucw/$(DEPDIR)/libcontrib_la-lists.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@contrib/ucw/$(DEPDIR)/libcontrib_la-mempool.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/common/$(DEPDIR)/libknotd_la-evsched.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/common/$(DEPDIR)/libknotd_la-fdset.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/common/$(DEPDIR)/libknotd_la-log.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/common/$(DEPDIR)/libknotd_la-process.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/common/$(DEPDIR)/libknotd_la-ref.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/common/$(DEPDIR)/libknotd_la-stats.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/conf/$(DEPDIR)/libknotd_la-base.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/conf/$(DEPDIR)/libknotd_la-conf.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/conf/$(DEPDIR)/libknotd_la-confdb.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/conf/$(DEPDIR)/libknotd_la-confio.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/conf/$(DEPDIR)/libknotd_la-migration.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/conf/$(DEPDIR)/libknotd_la-module.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/conf/$(DEPDIR)/libknotd_la-schema.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/conf/$(DEPDIR)/libknotd_la-tools.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/ctl/$(DEPDIR)/libknotd_la-commands.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/ctl/$(DEPDIR)/libknotd_la-process.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/dnssec/$(DEPDIR)/libknotd_la-context.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/dnssec/$(DEPDIR)/libknotd_la-ds_query.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/dnssec/$(DEPDIR)/libknotd_la-key-events.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/dnssec/$(DEPDIR)/libknotd_la-nsec-chain.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/dnssec/$(DEPDIR)/libknotd_la-nsec3-chain.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/dnssec/$(DEPDIR)/libknotd_la-policy.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/dnssec/$(DEPDIR)/libknotd_la-rrset-sign.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/dnssec/$(DEPDIR)/libknotd_la-zone-events.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/dnssec/$(DEPDIR)/libknotd_la-zone-keys.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/dnssec/$(DEPDIR)/libknotd_la-zone-nsec.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/dnssec/$(DEPDIR)/libknotd_la-zone-sign.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/dnssec/kasp/$(DEPDIR)/libknotd_la-kasp_db.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/dnssec/kasp/$(DEPDIR)/libknotd_la-kasp_zone.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/dnssec/kasp/$(DEPDIR)/libknotd_la-keystate.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/dnssec/kasp/$(DEPDIR)/libknotd_la-keystore.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/events/$(DEPDIR)/libknotd_la-events.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/events/$(DEPDIR)/libknotd_la-replan.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/events/handlers/$(DEPDIR)/libknotd_la-dnssec.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/events/handlers/$(DEPDIR)/libknotd_la-expire.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/events/handlers/$(DEPDIR)/libknotd_la-flush.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/events/handlers/$(DEPDIR)/libknotd_la-freeze_thaw.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/events/handlers/$(DEPDIR)/libknotd_la-load.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/events/handlers/$(DEPDIR)/libknotd_la-notify.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/events/handlers/$(DEPDIR)/libknotd_la-nsec3resalt.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/events/handlers/$(DEPDIR)/libknotd_la-parent_ds_query.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/events/handlers/$(DEPDIR)/libknotd_la-refresh.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/events/handlers/$(DEPDIR)/libknotd_la-update.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/journal/$(DEPDIR)/libknotd_la-chgset_ctx.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/journal/$(DEPDIR)/libknotd_la-journal.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/journal/$(DEPDIR)/libknotd_la-serialization.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/cookies/$(DEPDIR)/knot_modules_cookies_la-cookies.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/cookies/$(DEPDIR)/libknotd_la-cookies.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/dnsproxy/$(DEPDIR)/knot_modules_dnsproxy_la-dnsproxy.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/dnsproxy/$(DEPDIR)/libknotd_la-dnsproxy.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/dnstap/$(DEPDIR)/knot_modules_dnstap_la-dnstap.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/dnstap/$(DEPDIR)/libknotd_la-dnstap.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/geoip/$(DEPDIR)/knot_modules_geoip_la-geodb.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/geoip/$(DEPDIR)/knot_modules_geoip_la-geoip.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/geoip/$(DEPDIR)/libknotd_la-geodb.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/geoip/$(DEPDIR)/libknotd_la-geoip.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/noudp/$(DEPDIR)/knot_modules_noudp_la-noudp.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/noudp/$(DEPDIR)/libknotd_la-noudp.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/onlinesign/$(DEPDIR)/knot_modules_onlinesign_la-nsec_next.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/onlinesign/$(DEPDIR)/knot_modules_onlinesign_la-onlinesign.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/onlinesign/$(DEPDIR)/libknotd_la-nsec_next.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/onlinesign/$(DEPDIR)/libknotd_la-onlinesign.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/queryacl/$(DEPDIR)/knot_modules_queryacl_la-queryacl.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/queryacl/$(DEPDIR)/libknotd_la-queryacl.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/rrl/$(DEPDIR)/knot_modules_rrl_la-functions.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/rrl/$(DEPDIR)/knot_modules_rrl_la-rrl.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/rrl/$(DEPDIR)/libknotd_la-functions.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/rrl/$(DEPDIR)/libknotd_la-rrl.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/stats/$(DEPDIR)/knot_modules_stats_la-stats.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/stats/$(DEPDIR)/libknotd_la-stats.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/synthrecord/$(DEPDIR)/knot_modules_synthrecord_la-synthrecord.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/synthrecord/$(DEPDIR)/libknotd_la-synthrecord.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/whoami/$(DEPDIR)/knot_modules_whoami_la-whoami.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/modules/whoami/$(DEPDIR)/libknotd_la-whoami.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/nameserver/$(DEPDIR)/libknotd_la-axfr.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/nameserver/$(DEPDIR)/libknotd_la-chaos.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/nameserver/$(DEPDIR)/libknotd_la-internet.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/nameserver/$(DEPDIR)/libknotd_la-ixfr.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/nameserver/$(DEPDIR)/libknotd_la-notify.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/nameserver/$(DEPDIR)/libknotd_la-nsec_proofs.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/nameserver/$(DEPDIR)/libknotd_la-process_query.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/nameserver/$(DEPDIR)/libknotd_la-query_module.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/nameserver/$(DEPDIR)/libknotd_la-tsig_ctx.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/nameserver/$(DEPDIR)/libknotd_la-update.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/nameserver/$(DEPDIR)/libknotd_la-xfr.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/query/$(DEPDIR)/libknotd_la-capture.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/query/$(DEPDIR)/libknotd_la-query.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/query/$(DEPDIR)/libknotd_la-requestor.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/server/$(DEPDIR)/libknotd_la-dthreads.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/server/$(DEPDIR)/libknotd_la-server.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/server/$(DEPDIR)/libknotd_la-tcp-handler.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/server/$(DEPDIR)/libknotd_la-udp-handler.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/updates/$(DEPDIR)/libknotd_la-acl.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/updates/$(DEPDIR)/libknotd_la-apply.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/updates/$(DEPDIR)/libknotd_la-changesets.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/updates/$(DEPDIR)/libknotd_la-ddns.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/updates/$(DEPDIR)/libknotd_la-zone-update.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/worker/$(DEPDIR)/libknotd_la-pool.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/worker/$(DEPDIR)/libknotd_la-queue.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/zone/$(DEPDIR)/libknotd_la-contents.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/zone/$(DEPDIR)/libknotd_la-node.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/zone/$(DEPDIR)/libknotd_la-semantic-check.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/zone/$(DEPDIR)/libknotd_la-serial.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/zone/$(DEPDIR)/libknotd_la-timers.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/zone/$(DEPDIR)/libknotd_la-zone-diff.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/zone/$(DEPDIR)/libknotd_la-zone-dump.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/zone/$(DEPDIR)/libknotd_la-zone-load.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/zone/$(DEPDIR)/libknotd_la-zone-tree.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/zone/$(DEPDIR)/libknotd_la-zone.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/zone/$(DEPDIR)/libknotd_la-zonedb-load.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/zone/$(DEPDIR)/libknotd_la-zonedb.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@knot/zone/$(DEPDIR)/libknotd_la-zonefile.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/libdnssec_la-binary.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/libdnssec_la-crypto.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/libdnssec_la-error.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/libdnssec_la-keyid.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/libdnssec_la-random.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/$(DEPDIR)/libdnssec_la-tsig.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/contrib/$(DEPDIR)/libdnssec_la-vpool.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/key/$(DEPDIR)/libdnssec_la-algorithm.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/key/$(DEPDIR)/libdnssec_la-convert.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/key/$(DEPDIR)/libdnssec_la-dnskey.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/key/$(DEPDIR)/libdnssec_la-ds.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/key/$(DEPDIR)/libdnssec_la-key.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/key/$(DEPDIR)/libdnssec_la-keytag.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/key/$(DEPDIR)/libdnssec_la-privkey.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/key/$(DEPDIR)/libdnssec_la-simple.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/keystore/$(DEPDIR)/libdnssec_la-keystore.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/keystore/$(DEPDIR)/libdnssec_la-pkcs11.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/keystore/$(DEPDIR)/libdnssec_la-pkcs8.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/keystore/$(DEPDIR)/libdnssec_la-pkcs8_dir.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/list/$(DEPDIR)/libdnssec_la-list.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/nsec/$(DEPDIR)/libdnssec_la-bitmap.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/nsec/$(DEPDIR)/libdnssec_la-hash.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/nsec/$(DEPDIR)/libdnssec_la-nsec.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/p11/$(DEPDIR)/libdnssec_la-p11.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/shared/$(DEPDIR)/libshared_la-bignum.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/shared/$(DEPDIR)/libshared_la-dname.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/shared/$(DEPDIR)/libshared_la-fs.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/shared/$(DEPDIR)/libshared_la-hex.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/shared/$(DEPDIR)/libshared_la-keyid_gnutls.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/shared/$(DEPDIR)/libshared_la-pem.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/sign/$(DEPDIR)/libdnssec_la-der.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libdnssec/sign/$(DEPDIR)/libdnssec_la-sign.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/libknot_la-codes.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/libknot_la-cookies.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/libknot_la-descriptor.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/libknot_la-dname.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/libknot_la-error.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/libknot_la-rdataset.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/libknot_la-rrset-dump.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/libknot_la-rrset.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/libknot_la-tsig-op.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libknot/$(DEPDIR)/libknot_la-tsig.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libknot/control/$(DEPDIR)/libknot_la-control.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libknot/db/$(DEPDIR)/libknot_la-db_lmdb.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libknot/db/$(DEPDIR)/libknot_la-db_trie.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libknot/packet/$(DEPDIR)/libknot_la-pkt.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libknot/packet/$(DEPDIR)/libknot_la-rrset-wire.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libknot/rrtype/$(DEPDIR)/libknot_la-naptr.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libknot/rrtype/$(DEPDIR)/libknot_la-opt.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libknot/rrtype/$(DEPDIR)/libknot_la-tsig.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libknot/yparser/$(DEPDIR)/libknot_la-yparser.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libknot/yparser/$(DEPDIR)/libknot_la-ypbody.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libknot/yparser/$(DEPDIR)/libknot_la-ypformat.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libknot/yparser/$(DEPDIR)/libknot_la-ypschema.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libknot/yparser/$(DEPDIR)/libknot_la-yptrafo.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libzscanner/$(DEPDIR)/libzscanner_la-error.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libzscanner/$(DEPDIR)/libzscanner_la-functions.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@libzscanner/$(DEPDIR)/libzscanner_la-scanner.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@utils/common/$(DEPDIR)/libknotus_la-cert.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@utils/common/$(DEPDIR)/libknotus_la-exec.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@utils/common/$(DEPDIR)/libknotus_la-hex.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@utils/common/$(DEPDIR)/libknotus_la-lookup.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@utils/common/$(DEPDIR)/libknotus_la-msg.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@utils/common/$(DEPDIR)/libknotus_la-netio.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@utils/common/$(DEPDIR)/libknotus_la-params.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@utils/common/$(DEPDIR)/libknotus_la-resolv.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@utils/common/$(DEPDIR)/libknotus_la-sign.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@utils/common/$(DEPDIR)/libknotus_la-tls.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@utils/common/$(DEPDIR)/libknotus_la-token.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@utils/kdig/$(DEPDIR)/kdig-kdig_exec.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@utils/kdig/$(DEPDIR)/kdig-kdig_main.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@utils/kdig/$(DEPDIR)/kdig-kdig_params.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@utils/kdig/$(DEPDIR)/khost-kdig_exec.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@utils/kdig/$(DEPDIR)/khost-kdig_params.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@utils/keymgr/$(DEPDIR)/keymgr-bind_privkey.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@utils/keymgr/$(DEPDIR)/keymgr-functions.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@utils/keymgr/$(DEPDIR)/keymgr-main.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@utils/khost/$(DEPDIR)/khost-khost_main.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@utils/khost/$(DEPDIR)/khost-khost_params.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@utils/kjournalprint/$(DEPDIR)/kjournalprint-main.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@utils/knotc/$(DEPDIR)/knotc-commands.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@utils/knotc/$(DEPDIR)/knotc-estimator.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@utils/knotc/$(DEPDIR)/knotc-interactive.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@utils/knotc/$(DEPDIR)/knotc-main.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@utils/knotc/$(DEPDIR)/knotc-process.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@utils/knotd/$(DEPDIR)/knotd-main.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@utils/knsec3hash/$(DEPDIR)/knsec3hash-knsec3hash.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_exec.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_main.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_params.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@utils/kzonecheck/$(DEPDIR)/kzonecheck-main.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@utils/kzonecheck/$(DEPDIR)/kzonecheck-zone_check.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +knot/modules/cookies/knot_modules_cookies_la-cookies.lo: knot/modules/cookies/cookies.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_cookies_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/cookies/knot_modules_cookies_la-cookies.lo -MD -MP -MF knot/modules/cookies/$(DEPDIR)/knot_modules_cookies_la-cookies.Tpo -c -o knot/modules/cookies/knot_modules_cookies_la-cookies.lo `test -f 'knot/modules/cookies/cookies.c' || echo '$(srcdir)/'`knot/modules/cookies/cookies.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/cookies/$(DEPDIR)/knot_modules_cookies_la-cookies.Tpo knot/modules/cookies/$(DEPDIR)/knot_modules_cookies_la-cookies.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/cookies/cookies.c' object='knot/modules/cookies/knot_modules_cookies_la-cookies.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_cookies_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/cookies/knot_modules_cookies_la-cookies.lo `test -f 'knot/modules/cookies/cookies.c' || echo '$(srcdir)/'`knot/modules/cookies/cookies.c + +knot/modules/dnsproxy/knot_modules_dnsproxy_la-dnsproxy.lo: knot/modules/dnsproxy/dnsproxy.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_dnsproxy_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/dnsproxy/knot_modules_dnsproxy_la-dnsproxy.lo -MD -MP -MF knot/modules/dnsproxy/$(DEPDIR)/knot_modules_dnsproxy_la-dnsproxy.Tpo -c -o knot/modules/dnsproxy/knot_modules_dnsproxy_la-dnsproxy.lo `test -f 'knot/modules/dnsproxy/dnsproxy.c' || echo '$(srcdir)/'`knot/modules/dnsproxy/dnsproxy.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/dnsproxy/$(DEPDIR)/knot_modules_dnsproxy_la-dnsproxy.Tpo knot/modules/dnsproxy/$(DEPDIR)/knot_modules_dnsproxy_la-dnsproxy.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/dnsproxy/dnsproxy.c' object='knot/modules/dnsproxy/knot_modules_dnsproxy_la-dnsproxy.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_dnsproxy_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/dnsproxy/knot_modules_dnsproxy_la-dnsproxy.lo `test -f 'knot/modules/dnsproxy/dnsproxy.c' || echo '$(srcdir)/'`knot/modules/dnsproxy/dnsproxy.c + +knot/modules/dnstap/knot_modules_dnstap_la-dnstap.lo: knot/modules/dnstap/dnstap.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_dnstap_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/dnstap/knot_modules_dnstap_la-dnstap.lo -MD -MP -MF knot/modules/dnstap/$(DEPDIR)/knot_modules_dnstap_la-dnstap.Tpo -c -o knot/modules/dnstap/knot_modules_dnstap_la-dnstap.lo `test -f 'knot/modules/dnstap/dnstap.c' || echo '$(srcdir)/'`knot/modules/dnstap/dnstap.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/dnstap/$(DEPDIR)/knot_modules_dnstap_la-dnstap.Tpo knot/modules/dnstap/$(DEPDIR)/knot_modules_dnstap_la-dnstap.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/dnstap/dnstap.c' object='knot/modules/dnstap/knot_modules_dnstap_la-dnstap.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_dnstap_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/dnstap/knot_modules_dnstap_la-dnstap.lo `test -f 'knot/modules/dnstap/dnstap.c' || echo '$(srcdir)/'`knot/modules/dnstap/dnstap.c + +knot/modules/geoip/knot_modules_geoip_la-geoip.lo: knot/modules/geoip/geoip.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_geoip_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/geoip/knot_modules_geoip_la-geoip.lo -MD -MP -MF knot/modules/geoip/$(DEPDIR)/knot_modules_geoip_la-geoip.Tpo -c -o knot/modules/geoip/knot_modules_geoip_la-geoip.lo `test -f 'knot/modules/geoip/geoip.c' || echo '$(srcdir)/'`knot/modules/geoip/geoip.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/geoip/$(DEPDIR)/knot_modules_geoip_la-geoip.Tpo knot/modules/geoip/$(DEPDIR)/knot_modules_geoip_la-geoip.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/geoip/geoip.c' object='knot/modules/geoip/knot_modules_geoip_la-geoip.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_geoip_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/geoip/knot_modules_geoip_la-geoip.lo `test -f 'knot/modules/geoip/geoip.c' || echo '$(srcdir)/'`knot/modules/geoip/geoip.c + +knot/modules/geoip/knot_modules_geoip_la-geodb.lo: knot/modules/geoip/geodb.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_geoip_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/geoip/knot_modules_geoip_la-geodb.lo -MD -MP -MF knot/modules/geoip/$(DEPDIR)/knot_modules_geoip_la-geodb.Tpo -c -o knot/modules/geoip/knot_modules_geoip_la-geodb.lo `test -f 'knot/modules/geoip/geodb.c' || echo '$(srcdir)/'`knot/modules/geoip/geodb.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/geoip/$(DEPDIR)/knot_modules_geoip_la-geodb.Tpo knot/modules/geoip/$(DEPDIR)/knot_modules_geoip_la-geodb.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/geoip/geodb.c' object='knot/modules/geoip/knot_modules_geoip_la-geodb.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_geoip_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/geoip/knot_modules_geoip_la-geodb.lo `test -f 'knot/modules/geoip/geodb.c' || echo '$(srcdir)/'`knot/modules/geoip/geodb.c + +knot/modules/noudp/knot_modules_noudp_la-noudp.lo: knot/modules/noudp/noudp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_noudp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/noudp/knot_modules_noudp_la-noudp.lo -MD -MP -MF knot/modules/noudp/$(DEPDIR)/knot_modules_noudp_la-noudp.Tpo -c -o knot/modules/noudp/knot_modules_noudp_la-noudp.lo `test -f 'knot/modules/noudp/noudp.c' || echo '$(srcdir)/'`knot/modules/noudp/noudp.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/noudp/$(DEPDIR)/knot_modules_noudp_la-noudp.Tpo knot/modules/noudp/$(DEPDIR)/knot_modules_noudp_la-noudp.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/noudp/noudp.c' object='knot/modules/noudp/knot_modules_noudp_la-noudp.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_noudp_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/noudp/knot_modules_noudp_la-noudp.lo `test -f 'knot/modules/noudp/noudp.c' || echo '$(srcdir)/'`knot/modules/noudp/noudp.c + +knot/modules/onlinesign/knot_modules_onlinesign_la-onlinesign.lo: knot/modules/onlinesign/onlinesign.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_onlinesign_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/onlinesign/knot_modules_onlinesign_la-onlinesign.lo -MD -MP -MF knot/modules/onlinesign/$(DEPDIR)/knot_modules_onlinesign_la-onlinesign.Tpo -c -o knot/modules/onlinesign/knot_modules_onlinesign_la-onlinesign.lo `test -f 'knot/modules/onlinesign/onlinesign.c' || echo '$(srcdir)/'`knot/modules/onlinesign/onlinesign.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/onlinesign/$(DEPDIR)/knot_modules_onlinesign_la-onlinesign.Tpo knot/modules/onlinesign/$(DEPDIR)/knot_modules_onlinesign_la-onlinesign.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/onlinesign/onlinesign.c' object='knot/modules/onlinesign/knot_modules_onlinesign_la-onlinesign.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_onlinesign_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/onlinesign/knot_modules_onlinesign_la-onlinesign.lo `test -f 'knot/modules/onlinesign/onlinesign.c' || echo '$(srcdir)/'`knot/modules/onlinesign/onlinesign.c + +knot/modules/onlinesign/knot_modules_onlinesign_la-nsec_next.lo: knot/modules/onlinesign/nsec_next.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_onlinesign_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/onlinesign/knot_modules_onlinesign_la-nsec_next.lo -MD -MP -MF knot/modules/onlinesign/$(DEPDIR)/knot_modules_onlinesign_la-nsec_next.Tpo -c -o knot/modules/onlinesign/knot_modules_onlinesign_la-nsec_next.lo `test -f 'knot/modules/onlinesign/nsec_next.c' || echo '$(srcdir)/'`knot/modules/onlinesign/nsec_next.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/onlinesign/$(DEPDIR)/knot_modules_onlinesign_la-nsec_next.Tpo knot/modules/onlinesign/$(DEPDIR)/knot_modules_onlinesign_la-nsec_next.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/onlinesign/nsec_next.c' object='knot/modules/onlinesign/knot_modules_onlinesign_la-nsec_next.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_onlinesign_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/onlinesign/knot_modules_onlinesign_la-nsec_next.lo `test -f 'knot/modules/onlinesign/nsec_next.c' || echo '$(srcdir)/'`knot/modules/onlinesign/nsec_next.c + +knot/modules/queryacl/knot_modules_queryacl_la-queryacl.lo: knot/modules/queryacl/queryacl.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_queryacl_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/queryacl/knot_modules_queryacl_la-queryacl.lo -MD -MP -MF knot/modules/queryacl/$(DEPDIR)/knot_modules_queryacl_la-queryacl.Tpo -c -o knot/modules/queryacl/knot_modules_queryacl_la-queryacl.lo `test -f 'knot/modules/queryacl/queryacl.c' || echo '$(srcdir)/'`knot/modules/queryacl/queryacl.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/queryacl/$(DEPDIR)/knot_modules_queryacl_la-queryacl.Tpo knot/modules/queryacl/$(DEPDIR)/knot_modules_queryacl_la-queryacl.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/queryacl/queryacl.c' object='knot/modules/queryacl/knot_modules_queryacl_la-queryacl.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_queryacl_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/queryacl/knot_modules_queryacl_la-queryacl.lo `test -f 'knot/modules/queryacl/queryacl.c' || echo '$(srcdir)/'`knot/modules/queryacl/queryacl.c + +knot/modules/rrl/knot_modules_rrl_la-rrl.lo: knot/modules/rrl/rrl.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_rrl_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/rrl/knot_modules_rrl_la-rrl.lo -MD -MP -MF knot/modules/rrl/$(DEPDIR)/knot_modules_rrl_la-rrl.Tpo -c -o knot/modules/rrl/knot_modules_rrl_la-rrl.lo `test -f 'knot/modules/rrl/rrl.c' || echo '$(srcdir)/'`knot/modules/rrl/rrl.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/rrl/$(DEPDIR)/knot_modules_rrl_la-rrl.Tpo knot/modules/rrl/$(DEPDIR)/knot_modules_rrl_la-rrl.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/rrl/rrl.c' object='knot/modules/rrl/knot_modules_rrl_la-rrl.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_rrl_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/rrl/knot_modules_rrl_la-rrl.lo `test -f 'knot/modules/rrl/rrl.c' || echo '$(srcdir)/'`knot/modules/rrl/rrl.c + +knot/modules/rrl/knot_modules_rrl_la-functions.lo: knot/modules/rrl/functions.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_rrl_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/rrl/knot_modules_rrl_la-functions.lo -MD -MP -MF knot/modules/rrl/$(DEPDIR)/knot_modules_rrl_la-functions.Tpo -c -o knot/modules/rrl/knot_modules_rrl_la-functions.lo `test -f 'knot/modules/rrl/functions.c' || echo '$(srcdir)/'`knot/modules/rrl/functions.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/rrl/$(DEPDIR)/knot_modules_rrl_la-functions.Tpo knot/modules/rrl/$(DEPDIR)/knot_modules_rrl_la-functions.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/rrl/functions.c' object='knot/modules/rrl/knot_modules_rrl_la-functions.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_rrl_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/rrl/knot_modules_rrl_la-functions.lo `test -f 'knot/modules/rrl/functions.c' || echo '$(srcdir)/'`knot/modules/rrl/functions.c + +knot/modules/stats/knot_modules_stats_la-stats.lo: knot/modules/stats/stats.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_stats_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/stats/knot_modules_stats_la-stats.lo -MD -MP -MF knot/modules/stats/$(DEPDIR)/knot_modules_stats_la-stats.Tpo -c -o knot/modules/stats/knot_modules_stats_la-stats.lo `test -f 'knot/modules/stats/stats.c' || echo '$(srcdir)/'`knot/modules/stats/stats.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/stats/$(DEPDIR)/knot_modules_stats_la-stats.Tpo knot/modules/stats/$(DEPDIR)/knot_modules_stats_la-stats.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/stats/stats.c' object='knot/modules/stats/knot_modules_stats_la-stats.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_stats_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/stats/knot_modules_stats_la-stats.lo `test -f 'knot/modules/stats/stats.c' || echo '$(srcdir)/'`knot/modules/stats/stats.c + +knot/modules/synthrecord/knot_modules_synthrecord_la-synthrecord.lo: knot/modules/synthrecord/synthrecord.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_synthrecord_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/synthrecord/knot_modules_synthrecord_la-synthrecord.lo -MD -MP -MF knot/modules/synthrecord/$(DEPDIR)/knot_modules_synthrecord_la-synthrecord.Tpo -c -o knot/modules/synthrecord/knot_modules_synthrecord_la-synthrecord.lo `test -f 'knot/modules/synthrecord/synthrecord.c' || echo '$(srcdir)/'`knot/modules/synthrecord/synthrecord.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/synthrecord/$(DEPDIR)/knot_modules_synthrecord_la-synthrecord.Tpo knot/modules/synthrecord/$(DEPDIR)/knot_modules_synthrecord_la-synthrecord.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/synthrecord/synthrecord.c' object='knot/modules/synthrecord/knot_modules_synthrecord_la-synthrecord.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_synthrecord_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/synthrecord/knot_modules_synthrecord_la-synthrecord.lo `test -f 'knot/modules/synthrecord/synthrecord.c' || echo '$(srcdir)/'`knot/modules/synthrecord/synthrecord.c + +knot/modules/whoami/knot_modules_whoami_la-whoami.lo: knot/modules/whoami/whoami.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_whoami_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/whoami/knot_modules_whoami_la-whoami.lo -MD -MP -MF knot/modules/whoami/$(DEPDIR)/knot_modules_whoami_la-whoami.Tpo -c -o knot/modules/whoami/knot_modules_whoami_la-whoami.lo `test -f 'knot/modules/whoami/whoami.c' || echo '$(srcdir)/'`knot/modules/whoami/whoami.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/whoami/$(DEPDIR)/knot_modules_whoami_la-whoami.Tpo knot/modules/whoami/$(DEPDIR)/knot_modules_whoami_la-whoami.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/whoami/whoami.c' object='knot/modules/whoami/knot_modules_whoami_la-whoami.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knot_modules_whoami_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/whoami/knot_modules_whoami_la-whoami.lo `test -f 'knot/modules/whoami/whoami.c' || echo '$(srcdir)/'`knot/modules/whoami/whoami.c + +contrib/libcontrib_la-base32hex.lo: contrib/base32hex.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libcontrib_la-base32hex.lo -MD -MP -MF contrib/$(DEPDIR)/libcontrib_la-base32hex.Tpo -c -o contrib/libcontrib_la-base32hex.lo `test -f 'contrib/base32hex.c' || echo '$(srcdir)/'`contrib/base32hex.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/$(DEPDIR)/libcontrib_la-base32hex.Tpo contrib/$(DEPDIR)/libcontrib_la-base32hex.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/base32hex.c' object='contrib/libcontrib_la-base32hex.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libcontrib_la-base32hex.lo `test -f 'contrib/base32hex.c' || echo '$(srcdir)/'`contrib/base32hex.c + +contrib/libcontrib_la-base64.lo: contrib/base64.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libcontrib_la-base64.lo -MD -MP -MF contrib/$(DEPDIR)/libcontrib_la-base64.Tpo -c -o contrib/libcontrib_la-base64.lo `test -f 'contrib/base64.c' || echo '$(srcdir)/'`contrib/base64.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/$(DEPDIR)/libcontrib_la-base64.Tpo contrib/$(DEPDIR)/libcontrib_la-base64.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/base64.c' object='contrib/libcontrib_la-base64.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libcontrib_la-base64.lo `test -f 'contrib/base64.c' || echo '$(srcdir)/'`contrib/base64.c + +contrib/libcontrib_la-files.lo: contrib/files.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libcontrib_la-files.lo -MD -MP -MF contrib/$(DEPDIR)/libcontrib_la-files.Tpo -c -o contrib/libcontrib_la-files.lo `test -f 'contrib/files.c' || echo '$(srcdir)/'`contrib/files.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/$(DEPDIR)/libcontrib_la-files.Tpo contrib/$(DEPDIR)/libcontrib_la-files.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/files.c' object='contrib/libcontrib_la-files.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libcontrib_la-files.lo `test -f 'contrib/files.c' || echo '$(srcdir)/'`contrib/files.c + +contrib/libcontrib_la-getline.lo: contrib/getline.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libcontrib_la-getline.lo -MD -MP -MF contrib/$(DEPDIR)/libcontrib_la-getline.Tpo -c -o contrib/libcontrib_la-getline.lo `test -f 'contrib/getline.c' || echo '$(srcdir)/'`contrib/getline.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/$(DEPDIR)/libcontrib_la-getline.Tpo contrib/$(DEPDIR)/libcontrib_la-getline.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/getline.c' object='contrib/libcontrib_la-getline.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libcontrib_la-getline.lo `test -f 'contrib/getline.c' || echo '$(srcdir)/'`contrib/getline.c + +contrib/libcontrib_la-mempattern.lo: contrib/mempattern.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libcontrib_la-mempattern.lo -MD -MP -MF contrib/$(DEPDIR)/libcontrib_la-mempattern.Tpo -c -o contrib/libcontrib_la-mempattern.lo `test -f 'contrib/mempattern.c' || echo '$(srcdir)/'`contrib/mempattern.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/$(DEPDIR)/libcontrib_la-mempattern.Tpo contrib/$(DEPDIR)/libcontrib_la-mempattern.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/mempattern.c' object='contrib/libcontrib_la-mempattern.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libcontrib_la-mempattern.lo `test -f 'contrib/mempattern.c' || echo '$(srcdir)/'`contrib/mempattern.c + +contrib/libcontrib_la-net.lo: contrib/net.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libcontrib_la-net.lo -MD -MP -MF contrib/$(DEPDIR)/libcontrib_la-net.Tpo -c -o contrib/libcontrib_la-net.lo `test -f 'contrib/net.c' || echo '$(srcdir)/'`contrib/net.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/$(DEPDIR)/libcontrib_la-net.Tpo contrib/$(DEPDIR)/libcontrib_la-net.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/net.c' object='contrib/libcontrib_la-net.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libcontrib_la-net.lo `test -f 'contrib/net.c' || echo '$(srcdir)/'`contrib/net.c + +contrib/qp-trie/libcontrib_la-trie.lo: contrib/qp-trie/trie.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/qp-trie/libcontrib_la-trie.lo -MD -MP -MF contrib/qp-trie/$(DEPDIR)/libcontrib_la-trie.Tpo -c -o contrib/qp-trie/libcontrib_la-trie.lo `test -f 'contrib/qp-trie/trie.c' || echo '$(srcdir)/'`contrib/qp-trie/trie.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/qp-trie/$(DEPDIR)/libcontrib_la-trie.Tpo contrib/qp-trie/$(DEPDIR)/libcontrib_la-trie.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/qp-trie/trie.c' object='contrib/qp-trie/libcontrib_la-trie.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/qp-trie/libcontrib_la-trie.lo `test -f 'contrib/qp-trie/trie.c' || echo '$(srcdir)/'`contrib/qp-trie/trie.c + +contrib/libcontrib_la-sockaddr.lo: contrib/sockaddr.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libcontrib_la-sockaddr.lo -MD -MP -MF contrib/$(DEPDIR)/libcontrib_la-sockaddr.Tpo -c -o contrib/libcontrib_la-sockaddr.lo `test -f 'contrib/sockaddr.c' || echo '$(srcdir)/'`contrib/sockaddr.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/$(DEPDIR)/libcontrib_la-sockaddr.Tpo contrib/$(DEPDIR)/libcontrib_la-sockaddr.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/sockaddr.c' object='contrib/libcontrib_la-sockaddr.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libcontrib_la-sockaddr.lo `test -f 'contrib/sockaddr.c' || echo '$(srcdir)/'`contrib/sockaddr.c + +contrib/libcontrib_la-string.lo: contrib/string.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libcontrib_la-string.lo -MD -MP -MF contrib/$(DEPDIR)/libcontrib_la-string.Tpo -c -o contrib/libcontrib_la-string.lo `test -f 'contrib/string.c' || echo '$(srcdir)/'`contrib/string.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/$(DEPDIR)/libcontrib_la-string.Tpo contrib/$(DEPDIR)/libcontrib_la-string.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/string.c' object='contrib/libcontrib_la-string.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libcontrib_la-string.lo `test -f 'contrib/string.c' || echo '$(srcdir)/'`contrib/string.c + +contrib/libcontrib_la-time.lo: contrib/time.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/libcontrib_la-time.lo -MD -MP -MF contrib/$(DEPDIR)/libcontrib_la-time.Tpo -c -o contrib/libcontrib_la-time.lo `test -f 'contrib/time.c' || echo '$(srcdir)/'`contrib/time.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/$(DEPDIR)/libcontrib_la-time.Tpo contrib/$(DEPDIR)/libcontrib_la-time.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/time.c' object='contrib/libcontrib_la-time.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/libcontrib_la-time.lo `test -f 'contrib/time.c' || echo '$(srcdir)/'`contrib/time.c + +contrib/openbsd/libcontrib_la-siphash.lo: contrib/openbsd/siphash.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/openbsd/libcontrib_la-siphash.lo -MD -MP -MF contrib/openbsd/$(DEPDIR)/libcontrib_la-siphash.Tpo -c -o contrib/openbsd/libcontrib_la-siphash.lo `test -f 'contrib/openbsd/siphash.c' || echo '$(srcdir)/'`contrib/openbsd/siphash.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/openbsd/$(DEPDIR)/libcontrib_la-siphash.Tpo contrib/openbsd/$(DEPDIR)/libcontrib_la-siphash.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/openbsd/siphash.c' object='contrib/openbsd/libcontrib_la-siphash.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/openbsd/libcontrib_la-siphash.lo `test -f 'contrib/openbsd/siphash.c' || echo '$(srcdir)/'`contrib/openbsd/siphash.c + +contrib/openbsd/libcontrib_la-strlcat.lo: contrib/openbsd/strlcat.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/openbsd/libcontrib_la-strlcat.lo -MD -MP -MF contrib/openbsd/$(DEPDIR)/libcontrib_la-strlcat.Tpo -c -o contrib/openbsd/libcontrib_la-strlcat.lo `test -f 'contrib/openbsd/strlcat.c' || echo '$(srcdir)/'`contrib/openbsd/strlcat.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/openbsd/$(DEPDIR)/libcontrib_la-strlcat.Tpo contrib/openbsd/$(DEPDIR)/libcontrib_la-strlcat.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/openbsd/strlcat.c' object='contrib/openbsd/libcontrib_la-strlcat.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/openbsd/libcontrib_la-strlcat.lo `test -f 'contrib/openbsd/strlcat.c' || echo '$(srcdir)/'`contrib/openbsd/strlcat.c + +contrib/openbsd/libcontrib_la-strlcpy.lo: contrib/openbsd/strlcpy.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/openbsd/libcontrib_la-strlcpy.lo -MD -MP -MF contrib/openbsd/$(DEPDIR)/libcontrib_la-strlcpy.Tpo -c -o contrib/openbsd/libcontrib_la-strlcpy.lo `test -f 'contrib/openbsd/strlcpy.c' || echo '$(srcdir)/'`contrib/openbsd/strlcpy.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/openbsd/$(DEPDIR)/libcontrib_la-strlcpy.Tpo contrib/openbsd/$(DEPDIR)/libcontrib_la-strlcpy.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/openbsd/strlcpy.c' object='contrib/openbsd/libcontrib_la-strlcpy.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/openbsd/libcontrib_la-strlcpy.lo `test -f 'contrib/openbsd/strlcpy.c' || echo '$(srcdir)/'`contrib/openbsd/strlcpy.c + +contrib/ucw/libcontrib_la-heap.lo: contrib/ucw/heap.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/ucw/libcontrib_la-heap.lo -MD -MP -MF contrib/ucw/$(DEPDIR)/libcontrib_la-heap.Tpo -c -o contrib/ucw/libcontrib_la-heap.lo `test -f 'contrib/ucw/heap.c' || echo '$(srcdir)/'`contrib/ucw/heap.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/ucw/$(DEPDIR)/libcontrib_la-heap.Tpo contrib/ucw/$(DEPDIR)/libcontrib_la-heap.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/ucw/heap.c' object='contrib/ucw/libcontrib_la-heap.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/ucw/libcontrib_la-heap.lo `test -f 'contrib/ucw/heap.c' || echo '$(srcdir)/'`contrib/ucw/heap.c + +contrib/ucw/libcontrib_la-lists.lo: contrib/ucw/lists.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/ucw/libcontrib_la-lists.lo -MD -MP -MF contrib/ucw/$(DEPDIR)/libcontrib_la-lists.Tpo -c -o contrib/ucw/libcontrib_la-lists.lo `test -f 'contrib/ucw/lists.c' || echo '$(srcdir)/'`contrib/ucw/lists.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/ucw/$(DEPDIR)/libcontrib_la-lists.Tpo contrib/ucw/$(DEPDIR)/libcontrib_la-lists.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/ucw/lists.c' object='contrib/ucw/libcontrib_la-lists.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/ucw/libcontrib_la-lists.lo `test -f 'contrib/ucw/lists.c' || echo '$(srcdir)/'`contrib/ucw/lists.c + +contrib/ucw/libcontrib_la-mempool.lo: contrib/ucw/mempool.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/ucw/libcontrib_la-mempool.lo -MD -MP -MF contrib/ucw/$(DEPDIR)/libcontrib_la-mempool.Tpo -c -o contrib/ucw/libcontrib_la-mempool.lo `test -f 'contrib/ucw/mempool.c' || echo '$(srcdir)/'`contrib/ucw/mempool.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/ucw/$(DEPDIR)/libcontrib_la-mempool.Tpo contrib/ucw/$(DEPDIR)/libcontrib_la-mempool.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/ucw/mempool.c' object='contrib/ucw/libcontrib_la-mempool.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/ucw/libcontrib_la-mempool.lo `test -f 'contrib/ucw/mempool.c' || echo '$(srcdir)/'`contrib/ucw/mempool.c + +contrib/lmdb/libcontrib_la-mdb.lo: contrib/lmdb/mdb.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/lmdb/libcontrib_la-mdb.lo -MD -MP -MF contrib/lmdb/$(DEPDIR)/libcontrib_la-mdb.Tpo -c -o contrib/lmdb/libcontrib_la-mdb.lo `test -f 'contrib/lmdb/mdb.c' || echo '$(srcdir)/'`contrib/lmdb/mdb.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/lmdb/$(DEPDIR)/libcontrib_la-mdb.Tpo contrib/lmdb/$(DEPDIR)/libcontrib_la-mdb.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/lmdb/mdb.c' object='contrib/lmdb/libcontrib_la-mdb.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/lmdb/libcontrib_la-mdb.lo `test -f 'contrib/lmdb/mdb.c' || echo '$(srcdir)/'`contrib/lmdb/mdb.c + +contrib/lmdb/libcontrib_la-midl.lo: contrib/lmdb/midl.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/lmdb/libcontrib_la-midl.lo -MD -MP -MF contrib/lmdb/$(DEPDIR)/libcontrib_la-midl.Tpo -c -o contrib/lmdb/libcontrib_la-midl.lo `test -f 'contrib/lmdb/midl.c' || echo '$(srcdir)/'`contrib/lmdb/midl.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/lmdb/$(DEPDIR)/libcontrib_la-midl.Tpo contrib/lmdb/$(DEPDIR)/libcontrib_la-midl.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/lmdb/midl.c' object='contrib/lmdb/libcontrib_la-midl.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcontrib_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/lmdb/libcontrib_la-midl.lo `test -f 'contrib/lmdb/midl.c' || echo '$(srcdir)/'`contrib/lmdb/midl.c + +libdnssec/contrib/libdnssec_la-vpool.lo: libdnssec/contrib/vpool.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/contrib/libdnssec_la-vpool.lo -MD -MP -MF libdnssec/contrib/$(DEPDIR)/libdnssec_la-vpool.Tpo -c -o libdnssec/contrib/libdnssec_la-vpool.lo `test -f 'libdnssec/contrib/vpool.c' || echo '$(srcdir)/'`libdnssec/contrib/vpool.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/contrib/$(DEPDIR)/libdnssec_la-vpool.Tpo libdnssec/contrib/$(DEPDIR)/libdnssec_la-vpool.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/contrib/vpool.c' object='libdnssec/contrib/libdnssec_la-vpool.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/contrib/libdnssec_la-vpool.lo `test -f 'libdnssec/contrib/vpool.c' || echo '$(srcdir)/'`libdnssec/contrib/vpool.c + +libdnssec/libdnssec_la-binary.lo: libdnssec/binary.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/libdnssec_la-binary.lo -MD -MP -MF libdnssec/$(DEPDIR)/libdnssec_la-binary.Tpo -c -o libdnssec/libdnssec_la-binary.lo `test -f 'libdnssec/binary.c' || echo '$(srcdir)/'`libdnssec/binary.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/$(DEPDIR)/libdnssec_la-binary.Tpo libdnssec/$(DEPDIR)/libdnssec_la-binary.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/binary.c' object='libdnssec/libdnssec_la-binary.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/libdnssec_la-binary.lo `test -f 'libdnssec/binary.c' || echo '$(srcdir)/'`libdnssec/binary.c + +libdnssec/libdnssec_la-crypto.lo: libdnssec/crypto.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/libdnssec_la-crypto.lo -MD -MP -MF libdnssec/$(DEPDIR)/libdnssec_la-crypto.Tpo -c -o libdnssec/libdnssec_la-crypto.lo `test -f 'libdnssec/crypto.c' || echo '$(srcdir)/'`libdnssec/crypto.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/$(DEPDIR)/libdnssec_la-crypto.Tpo libdnssec/$(DEPDIR)/libdnssec_la-crypto.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/crypto.c' object='libdnssec/libdnssec_la-crypto.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/libdnssec_la-crypto.lo `test -f 'libdnssec/crypto.c' || echo '$(srcdir)/'`libdnssec/crypto.c + +libdnssec/libdnssec_la-error.lo: libdnssec/error.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/libdnssec_la-error.lo -MD -MP -MF libdnssec/$(DEPDIR)/libdnssec_la-error.Tpo -c -o libdnssec/libdnssec_la-error.lo `test -f 'libdnssec/error.c' || echo '$(srcdir)/'`libdnssec/error.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/$(DEPDIR)/libdnssec_la-error.Tpo libdnssec/$(DEPDIR)/libdnssec_la-error.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/error.c' object='libdnssec/libdnssec_la-error.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/libdnssec_la-error.lo `test -f 'libdnssec/error.c' || echo '$(srcdir)/'`libdnssec/error.c + +libdnssec/key/libdnssec_la-algorithm.lo: libdnssec/key/algorithm.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/key/libdnssec_la-algorithm.lo -MD -MP -MF libdnssec/key/$(DEPDIR)/libdnssec_la-algorithm.Tpo -c -o libdnssec/key/libdnssec_la-algorithm.lo `test -f 'libdnssec/key/algorithm.c' || echo '$(srcdir)/'`libdnssec/key/algorithm.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/key/$(DEPDIR)/libdnssec_la-algorithm.Tpo libdnssec/key/$(DEPDIR)/libdnssec_la-algorithm.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/key/algorithm.c' object='libdnssec/key/libdnssec_la-algorithm.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/key/libdnssec_la-algorithm.lo `test -f 'libdnssec/key/algorithm.c' || echo '$(srcdir)/'`libdnssec/key/algorithm.c + +libdnssec/key/libdnssec_la-convert.lo: libdnssec/key/convert.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/key/libdnssec_la-convert.lo -MD -MP -MF libdnssec/key/$(DEPDIR)/libdnssec_la-convert.Tpo -c -o libdnssec/key/libdnssec_la-convert.lo `test -f 'libdnssec/key/convert.c' || echo '$(srcdir)/'`libdnssec/key/convert.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/key/$(DEPDIR)/libdnssec_la-convert.Tpo libdnssec/key/$(DEPDIR)/libdnssec_la-convert.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/key/convert.c' object='libdnssec/key/libdnssec_la-convert.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/key/libdnssec_la-convert.lo `test -f 'libdnssec/key/convert.c' || echo '$(srcdir)/'`libdnssec/key/convert.c + +libdnssec/key/libdnssec_la-dnskey.lo: libdnssec/key/dnskey.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/key/libdnssec_la-dnskey.lo -MD -MP -MF libdnssec/key/$(DEPDIR)/libdnssec_la-dnskey.Tpo -c -o libdnssec/key/libdnssec_la-dnskey.lo `test -f 'libdnssec/key/dnskey.c' || echo '$(srcdir)/'`libdnssec/key/dnskey.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/key/$(DEPDIR)/libdnssec_la-dnskey.Tpo libdnssec/key/$(DEPDIR)/libdnssec_la-dnskey.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/key/dnskey.c' object='libdnssec/key/libdnssec_la-dnskey.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/key/libdnssec_la-dnskey.lo `test -f 'libdnssec/key/dnskey.c' || echo '$(srcdir)/'`libdnssec/key/dnskey.c + +libdnssec/key/libdnssec_la-ds.lo: libdnssec/key/ds.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/key/libdnssec_la-ds.lo -MD -MP -MF libdnssec/key/$(DEPDIR)/libdnssec_la-ds.Tpo -c -o libdnssec/key/libdnssec_la-ds.lo `test -f 'libdnssec/key/ds.c' || echo '$(srcdir)/'`libdnssec/key/ds.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/key/$(DEPDIR)/libdnssec_la-ds.Tpo libdnssec/key/$(DEPDIR)/libdnssec_la-ds.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/key/ds.c' object='libdnssec/key/libdnssec_la-ds.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/key/libdnssec_la-ds.lo `test -f 'libdnssec/key/ds.c' || echo '$(srcdir)/'`libdnssec/key/ds.c + +libdnssec/key/libdnssec_la-key.lo: libdnssec/key/key.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/key/libdnssec_la-key.lo -MD -MP -MF libdnssec/key/$(DEPDIR)/libdnssec_la-key.Tpo -c -o libdnssec/key/libdnssec_la-key.lo `test -f 'libdnssec/key/key.c' || echo '$(srcdir)/'`libdnssec/key/key.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/key/$(DEPDIR)/libdnssec_la-key.Tpo libdnssec/key/$(DEPDIR)/libdnssec_la-key.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/key/key.c' object='libdnssec/key/libdnssec_la-key.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/key/libdnssec_la-key.lo `test -f 'libdnssec/key/key.c' || echo '$(srcdir)/'`libdnssec/key/key.c + +libdnssec/key/libdnssec_la-keytag.lo: libdnssec/key/keytag.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/key/libdnssec_la-keytag.lo -MD -MP -MF libdnssec/key/$(DEPDIR)/libdnssec_la-keytag.Tpo -c -o libdnssec/key/libdnssec_la-keytag.lo `test -f 'libdnssec/key/keytag.c' || echo '$(srcdir)/'`libdnssec/key/keytag.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/key/$(DEPDIR)/libdnssec_la-keytag.Tpo libdnssec/key/$(DEPDIR)/libdnssec_la-keytag.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/key/keytag.c' object='libdnssec/key/libdnssec_la-keytag.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/key/libdnssec_la-keytag.lo `test -f 'libdnssec/key/keytag.c' || echo '$(srcdir)/'`libdnssec/key/keytag.c + +libdnssec/key/libdnssec_la-privkey.lo: libdnssec/key/privkey.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/key/libdnssec_la-privkey.lo -MD -MP -MF libdnssec/key/$(DEPDIR)/libdnssec_la-privkey.Tpo -c -o libdnssec/key/libdnssec_la-privkey.lo `test -f 'libdnssec/key/privkey.c' || echo '$(srcdir)/'`libdnssec/key/privkey.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/key/$(DEPDIR)/libdnssec_la-privkey.Tpo libdnssec/key/$(DEPDIR)/libdnssec_la-privkey.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/key/privkey.c' object='libdnssec/key/libdnssec_la-privkey.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/key/libdnssec_la-privkey.lo `test -f 'libdnssec/key/privkey.c' || echo '$(srcdir)/'`libdnssec/key/privkey.c + +libdnssec/key/libdnssec_la-simple.lo: libdnssec/key/simple.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/key/libdnssec_la-simple.lo -MD -MP -MF libdnssec/key/$(DEPDIR)/libdnssec_la-simple.Tpo -c -o libdnssec/key/libdnssec_la-simple.lo `test -f 'libdnssec/key/simple.c' || echo '$(srcdir)/'`libdnssec/key/simple.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/key/$(DEPDIR)/libdnssec_la-simple.Tpo libdnssec/key/$(DEPDIR)/libdnssec_la-simple.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/key/simple.c' object='libdnssec/key/libdnssec_la-simple.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/key/libdnssec_la-simple.lo `test -f 'libdnssec/key/simple.c' || echo '$(srcdir)/'`libdnssec/key/simple.c + +libdnssec/libdnssec_la-keyid.lo: libdnssec/keyid.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/libdnssec_la-keyid.lo -MD -MP -MF libdnssec/$(DEPDIR)/libdnssec_la-keyid.Tpo -c -o libdnssec/libdnssec_la-keyid.lo `test -f 'libdnssec/keyid.c' || echo '$(srcdir)/'`libdnssec/keyid.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/$(DEPDIR)/libdnssec_la-keyid.Tpo libdnssec/$(DEPDIR)/libdnssec_la-keyid.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/keyid.c' object='libdnssec/libdnssec_la-keyid.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/libdnssec_la-keyid.lo `test -f 'libdnssec/keyid.c' || echo '$(srcdir)/'`libdnssec/keyid.c + +libdnssec/keystore/libdnssec_la-keystore.lo: libdnssec/keystore/keystore.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/keystore/libdnssec_la-keystore.lo -MD -MP -MF libdnssec/keystore/$(DEPDIR)/libdnssec_la-keystore.Tpo -c -o libdnssec/keystore/libdnssec_la-keystore.lo `test -f 'libdnssec/keystore/keystore.c' || echo '$(srcdir)/'`libdnssec/keystore/keystore.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/keystore/$(DEPDIR)/libdnssec_la-keystore.Tpo libdnssec/keystore/$(DEPDIR)/libdnssec_la-keystore.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/keystore/keystore.c' object='libdnssec/keystore/libdnssec_la-keystore.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/keystore/libdnssec_la-keystore.lo `test -f 'libdnssec/keystore/keystore.c' || echo '$(srcdir)/'`libdnssec/keystore/keystore.c + +libdnssec/keystore/libdnssec_la-pkcs11.lo: libdnssec/keystore/pkcs11.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/keystore/libdnssec_la-pkcs11.lo -MD -MP -MF libdnssec/keystore/$(DEPDIR)/libdnssec_la-pkcs11.Tpo -c -o libdnssec/keystore/libdnssec_la-pkcs11.lo `test -f 'libdnssec/keystore/pkcs11.c' || echo '$(srcdir)/'`libdnssec/keystore/pkcs11.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/keystore/$(DEPDIR)/libdnssec_la-pkcs11.Tpo libdnssec/keystore/$(DEPDIR)/libdnssec_la-pkcs11.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/keystore/pkcs11.c' object='libdnssec/keystore/libdnssec_la-pkcs11.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/keystore/libdnssec_la-pkcs11.lo `test -f 'libdnssec/keystore/pkcs11.c' || echo '$(srcdir)/'`libdnssec/keystore/pkcs11.c + +libdnssec/keystore/libdnssec_la-pkcs8.lo: libdnssec/keystore/pkcs8.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/keystore/libdnssec_la-pkcs8.lo -MD -MP -MF libdnssec/keystore/$(DEPDIR)/libdnssec_la-pkcs8.Tpo -c -o libdnssec/keystore/libdnssec_la-pkcs8.lo `test -f 'libdnssec/keystore/pkcs8.c' || echo '$(srcdir)/'`libdnssec/keystore/pkcs8.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/keystore/$(DEPDIR)/libdnssec_la-pkcs8.Tpo libdnssec/keystore/$(DEPDIR)/libdnssec_la-pkcs8.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/keystore/pkcs8.c' object='libdnssec/keystore/libdnssec_la-pkcs8.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/keystore/libdnssec_la-pkcs8.lo `test -f 'libdnssec/keystore/pkcs8.c' || echo '$(srcdir)/'`libdnssec/keystore/pkcs8.c + +libdnssec/keystore/libdnssec_la-pkcs8_dir.lo: libdnssec/keystore/pkcs8_dir.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/keystore/libdnssec_la-pkcs8_dir.lo -MD -MP -MF libdnssec/keystore/$(DEPDIR)/libdnssec_la-pkcs8_dir.Tpo -c -o libdnssec/keystore/libdnssec_la-pkcs8_dir.lo `test -f 'libdnssec/keystore/pkcs8_dir.c' || echo '$(srcdir)/'`libdnssec/keystore/pkcs8_dir.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/keystore/$(DEPDIR)/libdnssec_la-pkcs8_dir.Tpo libdnssec/keystore/$(DEPDIR)/libdnssec_la-pkcs8_dir.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/keystore/pkcs8_dir.c' object='libdnssec/keystore/libdnssec_la-pkcs8_dir.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/keystore/libdnssec_la-pkcs8_dir.lo `test -f 'libdnssec/keystore/pkcs8_dir.c' || echo '$(srcdir)/'`libdnssec/keystore/pkcs8_dir.c + +libdnssec/list/libdnssec_la-list.lo: libdnssec/list/list.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/list/libdnssec_la-list.lo -MD -MP -MF libdnssec/list/$(DEPDIR)/libdnssec_la-list.Tpo -c -o libdnssec/list/libdnssec_la-list.lo `test -f 'libdnssec/list/list.c' || echo '$(srcdir)/'`libdnssec/list/list.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/list/$(DEPDIR)/libdnssec_la-list.Tpo libdnssec/list/$(DEPDIR)/libdnssec_la-list.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/list/list.c' object='libdnssec/list/libdnssec_la-list.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/list/libdnssec_la-list.lo `test -f 'libdnssec/list/list.c' || echo '$(srcdir)/'`libdnssec/list/list.c + +libdnssec/nsec/libdnssec_la-bitmap.lo: libdnssec/nsec/bitmap.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/nsec/libdnssec_la-bitmap.lo -MD -MP -MF libdnssec/nsec/$(DEPDIR)/libdnssec_la-bitmap.Tpo -c -o libdnssec/nsec/libdnssec_la-bitmap.lo `test -f 'libdnssec/nsec/bitmap.c' || echo '$(srcdir)/'`libdnssec/nsec/bitmap.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/nsec/$(DEPDIR)/libdnssec_la-bitmap.Tpo libdnssec/nsec/$(DEPDIR)/libdnssec_la-bitmap.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/nsec/bitmap.c' object='libdnssec/nsec/libdnssec_la-bitmap.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/nsec/libdnssec_la-bitmap.lo `test -f 'libdnssec/nsec/bitmap.c' || echo '$(srcdir)/'`libdnssec/nsec/bitmap.c + +libdnssec/nsec/libdnssec_la-hash.lo: libdnssec/nsec/hash.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/nsec/libdnssec_la-hash.lo -MD -MP -MF libdnssec/nsec/$(DEPDIR)/libdnssec_la-hash.Tpo -c -o libdnssec/nsec/libdnssec_la-hash.lo `test -f 'libdnssec/nsec/hash.c' || echo '$(srcdir)/'`libdnssec/nsec/hash.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/nsec/$(DEPDIR)/libdnssec_la-hash.Tpo libdnssec/nsec/$(DEPDIR)/libdnssec_la-hash.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/nsec/hash.c' object='libdnssec/nsec/libdnssec_la-hash.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/nsec/libdnssec_la-hash.lo `test -f 'libdnssec/nsec/hash.c' || echo '$(srcdir)/'`libdnssec/nsec/hash.c + +libdnssec/nsec/libdnssec_la-nsec.lo: libdnssec/nsec/nsec.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/nsec/libdnssec_la-nsec.lo -MD -MP -MF libdnssec/nsec/$(DEPDIR)/libdnssec_la-nsec.Tpo -c -o libdnssec/nsec/libdnssec_la-nsec.lo `test -f 'libdnssec/nsec/nsec.c' || echo '$(srcdir)/'`libdnssec/nsec/nsec.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/nsec/$(DEPDIR)/libdnssec_la-nsec.Tpo libdnssec/nsec/$(DEPDIR)/libdnssec_la-nsec.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/nsec/nsec.c' object='libdnssec/nsec/libdnssec_la-nsec.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/nsec/libdnssec_la-nsec.lo `test -f 'libdnssec/nsec/nsec.c' || echo '$(srcdir)/'`libdnssec/nsec/nsec.c + +libdnssec/p11/libdnssec_la-p11.lo: libdnssec/p11/p11.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/p11/libdnssec_la-p11.lo -MD -MP -MF libdnssec/p11/$(DEPDIR)/libdnssec_la-p11.Tpo -c -o libdnssec/p11/libdnssec_la-p11.lo `test -f 'libdnssec/p11/p11.c' || echo '$(srcdir)/'`libdnssec/p11/p11.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/p11/$(DEPDIR)/libdnssec_la-p11.Tpo libdnssec/p11/$(DEPDIR)/libdnssec_la-p11.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/p11/p11.c' object='libdnssec/p11/libdnssec_la-p11.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/p11/libdnssec_la-p11.lo `test -f 'libdnssec/p11/p11.c' || echo '$(srcdir)/'`libdnssec/p11/p11.c + +libdnssec/libdnssec_la-random.lo: libdnssec/random.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/libdnssec_la-random.lo -MD -MP -MF libdnssec/$(DEPDIR)/libdnssec_la-random.Tpo -c -o libdnssec/libdnssec_la-random.lo `test -f 'libdnssec/random.c' || echo '$(srcdir)/'`libdnssec/random.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/$(DEPDIR)/libdnssec_la-random.Tpo libdnssec/$(DEPDIR)/libdnssec_la-random.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/random.c' object='libdnssec/libdnssec_la-random.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/libdnssec_la-random.lo `test -f 'libdnssec/random.c' || echo '$(srcdir)/'`libdnssec/random.c + +libdnssec/sign/libdnssec_la-der.lo: libdnssec/sign/der.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/sign/libdnssec_la-der.lo -MD -MP -MF libdnssec/sign/$(DEPDIR)/libdnssec_la-der.Tpo -c -o libdnssec/sign/libdnssec_la-der.lo `test -f 'libdnssec/sign/der.c' || echo '$(srcdir)/'`libdnssec/sign/der.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/sign/$(DEPDIR)/libdnssec_la-der.Tpo libdnssec/sign/$(DEPDIR)/libdnssec_la-der.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/sign/der.c' object='libdnssec/sign/libdnssec_la-der.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/sign/libdnssec_la-der.lo `test -f 'libdnssec/sign/der.c' || echo '$(srcdir)/'`libdnssec/sign/der.c + +libdnssec/sign/libdnssec_la-sign.lo: libdnssec/sign/sign.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/sign/libdnssec_la-sign.lo -MD -MP -MF libdnssec/sign/$(DEPDIR)/libdnssec_la-sign.Tpo -c -o libdnssec/sign/libdnssec_la-sign.lo `test -f 'libdnssec/sign/sign.c' || echo '$(srcdir)/'`libdnssec/sign/sign.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/sign/$(DEPDIR)/libdnssec_la-sign.Tpo libdnssec/sign/$(DEPDIR)/libdnssec_la-sign.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/sign/sign.c' object='libdnssec/sign/libdnssec_la-sign.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/sign/libdnssec_la-sign.lo `test -f 'libdnssec/sign/sign.c' || echo '$(srcdir)/'`libdnssec/sign/sign.c + +libdnssec/libdnssec_la-tsig.lo: libdnssec/tsig.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/libdnssec_la-tsig.lo -MD -MP -MF libdnssec/$(DEPDIR)/libdnssec_la-tsig.Tpo -c -o libdnssec/libdnssec_la-tsig.lo `test -f 'libdnssec/tsig.c' || echo '$(srcdir)/'`libdnssec/tsig.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/$(DEPDIR)/libdnssec_la-tsig.Tpo libdnssec/$(DEPDIR)/libdnssec_la-tsig.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/tsig.c' object='libdnssec/libdnssec_la-tsig.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnssec_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/libdnssec_la-tsig.lo `test -f 'libdnssec/tsig.c' || echo '$(srcdir)/'`libdnssec/tsig.c + +contrib/dnstap/libdnstap_la-convert.lo: contrib/dnstap/convert.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnstap_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/dnstap/libdnstap_la-convert.lo -MD -MP -MF contrib/dnstap/$(DEPDIR)/libdnstap_la-convert.Tpo -c -o contrib/dnstap/libdnstap_la-convert.lo `test -f 'contrib/dnstap/convert.c' || echo '$(srcdir)/'`contrib/dnstap/convert.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/dnstap/$(DEPDIR)/libdnstap_la-convert.Tpo contrib/dnstap/$(DEPDIR)/libdnstap_la-convert.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/dnstap/convert.c' object='contrib/dnstap/libdnstap_la-convert.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnstap_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/dnstap/libdnstap_la-convert.lo `test -f 'contrib/dnstap/convert.c' || echo '$(srcdir)/'`contrib/dnstap/convert.c + +contrib/dnstap/libdnstap_la-dnstap.lo: contrib/dnstap/dnstap.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnstap_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/dnstap/libdnstap_la-dnstap.lo -MD -MP -MF contrib/dnstap/$(DEPDIR)/libdnstap_la-dnstap.Tpo -c -o contrib/dnstap/libdnstap_la-dnstap.lo `test -f 'contrib/dnstap/dnstap.c' || echo '$(srcdir)/'`contrib/dnstap/dnstap.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/dnstap/$(DEPDIR)/libdnstap_la-dnstap.Tpo contrib/dnstap/$(DEPDIR)/libdnstap_la-dnstap.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/dnstap/dnstap.c' object='contrib/dnstap/libdnstap_la-dnstap.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnstap_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/dnstap/libdnstap_la-dnstap.lo `test -f 'contrib/dnstap/dnstap.c' || echo '$(srcdir)/'`contrib/dnstap/dnstap.c + +contrib/dnstap/libdnstap_la-message.lo: contrib/dnstap/message.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnstap_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/dnstap/libdnstap_la-message.lo -MD -MP -MF contrib/dnstap/$(DEPDIR)/libdnstap_la-message.Tpo -c -o contrib/dnstap/libdnstap_la-message.lo `test -f 'contrib/dnstap/message.c' || echo '$(srcdir)/'`contrib/dnstap/message.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/dnstap/$(DEPDIR)/libdnstap_la-message.Tpo contrib/dnstap/$(DEPDIR)/libdnstap_la-message.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/dnstap/message.c' object='contrib/dnstap/libdnstap_la-message.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnstap_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/dnstap/libdnstap_la-message.lo `test -f 'contrib/dnstap/message.c' || echo '$(srcdir)/'`contrib/dnstap/message.c + +contrib/dnstap/libdnstap_la-reader.lo: contrib/dnstap/reader.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnstap_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/dnstap/libdnstap_la-reader.lo -MD -MP -MF contrib/dnstap/$(DEPDIR)/libdnstap_la-reader.Tpo -c -o contrib/dnstap/libdnstap_la-reader.lo `test -f 'contrib/dnstap/reader.c' || echo '$(srcdir)/'`contrib/dnstap/reader.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/dnstap/$(DEPDIR)/libdnstap_la-reader.Tpo contrib/dnstap/$(DEPDIR)/libdnstap_la-reader.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/dnstap/reader.c' object='contrib/dnstap/libdnstap_la-reader.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnstap_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/dnstap/libdnstap_la-reader.lo `test -f 'contrib/dnstap/reader.c' || echo '$(srcdir)/'`contrib/dnstap/reader.c + +contrib/dnstap/libdnstap_la-writer.lo: contrib/dnstap/writer.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnstap_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/dnstap/libdnstap_la-writer.lo -MD -MP -MF contrib/dnstap/$(DEPDIR)/libdnstap_la-writer.Tpo -c -o contrib/dnstap/libdnstap_la-writer.lo `test -f 'contrib/dnstap/writer.c' || echo '$(srcdir)/'`contrib/dnstap/writer.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/dnstap/$(DEPDIR)/libdnstap_la-writer.Tpo contrib/dnstap/$(DEPDIR)/libdnstap_la-writer.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/dnstap/writer.c' object='contrib/dnstap/libdnstap_la-writer.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnstap_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/dnstap/libdnstap_la-writer.lo `test -f 'contrib/dnstap/writer.c' || echo '$(srcdir)/'`contrib/dnstap/writer.c + +contrib/dnstap/libdnstap_la-dnstap.pb-c.lo: contrib/dnstap/dnstap.pb-c.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnstap_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT contrib/dnstap/libdnstap_la-dnstap.pb-c.lo -MD -MP -MF contrib/dnstap/$(DEPDIR)/libdnstap_la-dnstap.pb-c.Tpo -c -o contrib/dnstap/libdnstap_la-dnstap.pb-c.lo `test -f 'contrib/dnstap/dnstap.pb-c.c' || echo '$(srcdir)/'`contrib/dnstap/dnstap.pb-c.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) contrib/dnstap/$(DEPDIR)/libdnstap_la-dnstap.pb-c.Tpo contrib/dnstap/$(DEPDIR)/libdnstap_la-dnstap.pb-c.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='contrib/dnstap/dnstap.pb-c.c' object='contrib/dnstap/libdnstap_la-dnstap.pb-c.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdnstap_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o contrib/dnstap/libdnstap_la-dnstap.pb-c.lo `test -f 'contrib/dnstap/dnstap.pb-c.c' || echo '$(srcdir)/'`contrib/dnstap/dnstap.pb-c.c + +libknot/libknot_la-codes.lo: libknot/codes.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/libknot_la-codes.lo -MD -MP -MF libknot/$(DEPDIR)/libknot_la-codes.Tpo -c -o libknot/libknot_la-codes.lo `test -f 'libknot/codes.c' || echo '$(srcdir)/'`libknot/codes.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/$(DEPDIR)/libknot_la-codes.Tpo libknot/$(DEPDIR)/libknot_la-codes.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libknot/codes.c' object='libknot/libknot_la-codes.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/libknot_la-codes.lo `test -f 'libknot/codes.c' || echo '$(srcdir)/'`libknot/codes.c + +libknot/control/libknot_la-control.lo: libknot/control/control.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/control/libknot_la-control.lo -MD -MP -MF libknot/control/$(DEPDIR)/libknot_la-control.Tpo -c -o libknot/control/libknot_la-control.lo `test -f 'libknot/control/control.c' || echo '$(srcdir)/'`libknot/control/control.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/control/$(DEPDIR)/libknot_la-control.Tpo libknot/control/$(DEPDIR)/libknot_la-control.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libknot/control/control.c' object='libknot/control/libknot_la-control.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/control/libknot_la-control.lo `test -f 'libknot/control/control.c' || echo '$(srcdir)/'`libknot/control/control.c + +libknot/libknot_la-cookies.lo: libknot/cookies.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/libknot_la-cookies.lo -MD -MP -MF libknot/$(DEPDIR)/libknot_la-cookies.Tpo -c -o libknot/libknot_la-cookies.lo `test -f 'libknot/cookies.c' || echo '$(srcdir)/'`libknot/cookies.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/$(DEPDIR)/libknot_la-cookies.Tpo libknot/$(DEPDIR)/libknot_la-cookies.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libknot/cookies.c' object='libknot/libknot_la-cookies.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/libknot_la-cookies.lo `test -f 'libknot/cookies.c' || echo '$(srcdir)/'`libknot/cookies.c + +libknot/libknot_la-descriptor.lo: libknot/descriptor.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/libknot_la-descriptor.lo -MD -MP -MF libknot/$(DEPDIR)/libknot_la-descriptor.Tpo -c -o libknot/libknot_la-descriptor.lo `test -f 'libknot/descriptor.c' || echo '$(srcdir)/'`libknot/descriptor.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/$(DEPDIR)/libknot_la-descriptor.Tpo libknot/$(DEPDIR)/libknot_la-descriptor.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libknot/descriptor.c' object='libknot/libknot_la-descriptor.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/libknot_la-descriptor.lo `test -f 'libknot/descriptor.c' || echo '$(srcdir)/'`libknot/descriptor.c + +libknot/libknot_la-dname.lo: libknot/dname.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/libknot_la-dname.lo -MD -MP -MF libknot/$(DEPDIR)/libknot_la-dname.Tpo -c -o libknot/libknot_la-dname.lo `test -f 'libknot/dname.c' || echo '$(srcdir)/'`libknot/dname.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/$(DEPDIR)/libknot_la-dname.Tpo libknot/$(DEPDIR)/libknot_la-dname.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libknot/dname.c' object='libknot/libknot_la-dname.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/libknot_la-dname.lo `test -f 'libknot/dname.c' || echo '$(srcdir)/'`libknot/dname.c + +libknot/libknot_la-error.lo: libknot/error.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/libknot_la-error.lo -MD -MP -MF libknot/$(DEPDIR)/libknot_la-error.Tpo -c -o libknot/libknot_la-error.lo `test -f 'libknot/error.c' || echo '$(srcdir)/'`libknot/error.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/$(DEPDIR)/libknot_la-error.Tpo libknot/$(DEPDIR)/libknot_la-error.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libknot/error.c' object='libknot/libknot_la-error.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/libknot_la-error.lo `test -f 'libknot/error.c' || echo '$(srcdir)/'`libknot/error.c + +libknot/db/libknot_la-db_lmdb.lo: libknot/db/db_lmdb.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/db/libknot_la-db_lmdb.lo -MD -MP -MF libknot/db/$(DEPDIR)/libknot_la-db_lmdb.Tpo -c -o libknot/db/libknot_la-db_lmdb.lo `test -f 'libknot/db/db_lmdb.c' || echo '$(srcdir)/'`libknot/db/db_lmdb.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/db/$(DEPDIR)/libknot_la-db_lmdb.Tpo libknot/db/$(DEPDIR)/libknot_la-db_lmdb.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libknot/db/db_lmdb.c' object='libknot/db/libknot_la-db_lmdb.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/db/libknot_la-db_lmdb.lo `test -f 'libknot/db/db_lmdb.c' || echo '$(srcdir)/'`libknot/db/db_lmdb.c + +libknot/db/libknot_la-db_trie.lo: libknot/db/db_trie.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/db/libknot_la-db_trie.lo -MD -MP -MF libknot/db/$(DEPDIR)/libknot_la-db_trie.Tpo -c -o libknot/db/libknot_la-db_trie.lo `test -f 'libknot/db/db_trie.c' || echo '$(srcdir)/'`libknot/db/db_trie.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/db/$(DEPDIR)/libknot_la-db_trie.Tpo libknot/db/$(DEPDIR)/libknot_la-db_trie.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libknot/db/db_trie.c' object='libknot/db/libknot_la-db_trie.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/db/libknot_la-db_trie.lo `test -f 'libknot/db/db_trie.c' || echo '$(srcdir)/'`libknot/db/db_trie.c + +libknot/packet/libknot_la-pkt.lo: libknot/packet/pkt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/packet/libknot_la-pkt.lo -MD -MP -MF libknot/packet/$(DEPDIR)/libknot_la-pkt.Tpo -c -o libknot/packet/libknot_la-pkt.lo `test -f 'libknot/packet/pkt.c' || echo '$(srcdir)/'`libknot/packet/pkt.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/packet/$(DEPDIR)/libknot_la-pkt.Tpo libknot/packet/$(DEPDIR)/libknot_la-pkt.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libknot/packet/pkt.c' object='libknot/packet/libknot_la-pkt.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/packet/libknot_la-pkt.lo `test -f 'libknot/packet/pkt.c' || echo '$(srcdir)/'`libknot/packet/pkt.c + +libknot/packet/libknot_la-rrset-wire.lo: libknot/packet/rrset-wire.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/packet/libknot_la-rrset-wire.lo -MD -MP -MF libknot/packet/$(DEPDIR)/libknot_la-rrset-wire.Tpo -c -o libknot/packet/libknot_la-rrset-wire.lo `test -f 'libknot/packet/rrset-wire.c' || echo '$(srcdir)/'`libknot/packet/rrset-wire.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/packet/$(DEPDIR)/libknot_la-rrset-wire.Tpo libknot/packet/$(DEPDIR)/libknot_la-rrset-wire.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libknot/packet/rrset-wire.c' object='libknot/packet/libknot_la-rrset-wire.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/packet/libknot_la-rrset-wire.lo `test -f 'libknot/packet/rrset-wire.c' || echo '$(srcdir)/'`libknot/packet/rrset-wire.c + +libknot/libknot_la-rdataset.lo: libknot/rdataset.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/libknot_la-rdataset.lo -MD -MP -MF libknot/$(DEPDIR)/libknot_la-rdataset.Tpo -c -o libknot/libknot_la-rdataset.lo `test -f 'libknot/rdataset.c' || echo '$(srcdir)/'`libknot/rdataset.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/$(DEPDIR)/libknot_la-rdataset.Tpo libknot/$(DEPDIR)/libknot_la-rdataset.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libknot/rdataset.c' object='libknot/libknot_la-rdataset.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/libknot_la-rdataset.lo `test -f 'libknot/rdataset.c' || echo '$(srcdir)/'`libknot/rdataset.c + +libknot/libknot_la-rrset-dump.lo: libknot/rrset-dump.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/libknot_la-rrset-dump.lo -MD -MP -MF libknot/$(DEPDIR)/libknot_la-rrset-dump.Tpo -c -o libknot/libknot_la-rrset-dump.lo `test -f 'libknot/rrset-dump.c' || echo '$(srcdir)/'`libknot/rrset-dump.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/$(DEPDIR)/libknot_la-rrset-dump.Tpo libknot/$(DEPDIR)/libknot_la-rrset-dump.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libknot/rrset-dump.c' object='libknot/libknot_la-rrset-dump.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/libknot_la-rrset-dump.lo `test -f 'libknot/rrset-dump.c' || echo '$(srcdir)/'`libknot/rrset-dump.c + +libknot/libknot_la-rrset.lo: libknot/rrset.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/libknot_la-rrset.lo -MD -MP -MF libknot/$(DEPDIR)/libknot_la-rrset.Tpo -c -o libknot/libknot_la-rrset.lo `test -f 'libknot/rrset.c' || echo '$(srcdir)/'`libknot/rrset.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/$(DEPDIR)/libknot_la-rrset.Tpo libknot/$(DEPDIR)/libknot_la-rrset.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libknot/rrset.c' object='libknot/libknot_la-rrset.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/libknot_la-rrset.lo `test -f 'libknot/rrset.c' || echo '$(srcdir)/'`libknot/rrset.c + +libknot/rrtype/libknot_la-naptr.lo: libknot/rrtype/naptr.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/rrtype/libknot_la-naptr.lo -MD -MP -MF libknot/rrtype/$(DEPDIR)/libknot_la-naptr.Tpo -c -o libknot/rrtype/libknot_la-naptr.lo `test -f 'libknot/rrtype/naptr.c' || echo '$(srcdir)/'`libknot/rrtype/naptr.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/rrtype/$(DEPDIR)/libknot_la-naptr.Tpo libknot/rrtype/$(DEPDIR)/libknot_la-naptr.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libknot/rrtype/naptr.c' object='libknot/rrtype/libknot_la-naptr.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/rrtype/libknot_la-naptr.lo `test -f 'libknot/rrtype/naptr.c' || echo '$(srcdir)/'`libknot/rrtype/naptr.c + +libknot/rrtype/libknot_la-opt.lo: libknot/rrtype/opt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/rrtype/libknot_la-opt.lo -MD -MP -MF libknot/rrtype/$(DEPDIR)/libknot_la-opt.Tpo -c -o libknot/rrtype/libknot_la-opt.lo `test -f 'libknot/rrtype/opt.c' || echo '$(srcdir)/'`libknot/rrtype/opt.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/rrtype/$(DEPDIR)/libknot_la-opt.Tpo libknot/rrtype/$(DEPDIR)/libknot_la-opt.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libknot/rrtype/opt.c' object='libknot/rrtype/libknot_la-opt.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/rrtype/libknot_la-opt.lo `test -f 'libknot/rrtype/opt.c' || echo '$(srcdir)/'`libknot/rrtype/opt.c + +libknot/rrtype/libknot_la-tsig.lo: libknot/rrtype/tsig.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/rrtype/libknot_la-tsig.lo -MD -MP -MF libknot/rrtype/$(DEPDIR)/libknot_la-tsig.Tpo -c -o libknot/rrtype/libknot_la-tsig.lo `test -f 'libknot/rrtype/tsig.c' || echo '$(srcdir)/'`libknot/rrtype/tsig.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/rrtype/$(DEPDIR)/libknot_la-tsig.Tpo libknot/rrtype/$(DEPDIR)/libknot_la-tsig.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libknot/rrtype/tsig.c' object='libknot/rrtype/libknot_la-tsig.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/rrtype/libknot_la-tsig.lo `test -f 'libknot/rrtype/tsig.c' || echo '$(srcdir)/'`libknot/rrtype/tsig.c + +libknot/libknot_la-tsig-op.lo: libknot/tsig-op.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/libknot_la-tsig-op.lo -MD -MP -MF libknot/$(DEPDIR)/libknot_la-tsig-op.Tpo -c -o libknot/libknot_la-tsig-op.lo `test -f 'libknot/tsig-op.c' || echo '$(srcdir)/'`libknot/tsig-op.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/$(DEPDIR)/libknot_la-tsig-op.Tpo libknot/$(DEPDIR)/libknot_la-tsig-op.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libknot/tsig-op.c' object='libknot/libknot_la-tsig-op.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/libknot_la-tsig-op.lo `test -f 'libknot/tsig-op.c' || echo '$(srcdir)/'`libknot/tsig-op.c + +libknot/libknot_la-tsig.lo: libknot/tsig.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/libknot_la-tsig.lo -MD -MP -MF libknot/$(DEPDIR)/libknot_la-tsig.Tpo -c -o libknot/libknot_la-tsig.lo `test -f 'libknot/tsig.c' || echo '$(srcdir)/'`libknot/tsig.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/$(DEPDIR)/libknot_la-tsig.Tpo libknot/$(DEPDIR)/libknot_la-tsig.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libknot/tsig.c' object='libknot/libknot_la-tsig.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/libknot_la-tsig.lo `test -f 'libknot/tsig.c' || echo '$(srcdir)/'`libknot/tsig.c + +libknot/yparser/libknot_la-yparser.lo: libknot/yparser/yparser.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/yparser/libknot_la-yparser.lo -MD -MP -MF libknot/yparser/$(DEPDIR)/libknot_la-yparser.Tpo -c -o libknot/yparser/libknot_la-yparser.lo `test -f 'libknot/yparser/yparser.c' || echo '$(srcdir)/'`libknot/yparser/yparser.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/yparser/$(DEPDIR)/libknot_la-yparser.Tpo libknot/yparser/$(DEPDIR)/libknot_la-yparser.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libknot/yparser/yparser.c' object='libknot/yparser/libknot_la-yparser.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/yparser/libknot_la-yparser.lo `test -f 'libknot/yparser/yparser.c' || echo '$(srcdir)/'`libknot/yparser/yparser.c + +libknot/yparser/libknot_la-ypbody.lo: libknot/yparser/ypbody.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/yparser/libknot_la-ypbody.lo -MD -MP -MF libknot/yparser/$(DEPDIR)/libknot_la-ypbody.Tpo -c -o libknot/yparser/libknot_la-ypbody.lo `test -f 'libknot/yparser/ypbody.c' || echo '$(srcdir)/'`libknot/yparser/ypbody.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/yparser/$(DEPDIR)/libknot_la-ypbody.Tpo libknot/yparser/$(DEPDIR)/libknot_la-ypbody.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libknot/yparser/ypbody.c' object='libknot/yparser/libknot_la-ypbody.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/yparser/libknot_la-ypbody.lo `test -f 'libknot/yparser/ypbody.c' || echo '$(srcdir)/'`libknot/yparser/ypbody.c + +libknot/yparser/libknot_la-ypformat.lo: libknot/yparser/ypformat.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/yparser/libknot_la-ypformat.lo -MD -MP -MF libknot/yparser/$(DEPDIR)/libknot_la-ypformat.Tpo -c -o libknot/yparser/libknot_la-ypformat.lo `test -f 'libknot/yparser/ypformat.c' || echo '$(srcdir)/'`libknot/yparser/ypformat.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/yparser/$(DEPDIR)/libknot_la-ypformat.Tpo libknot/yparser/$(DEPDIR)/libknot_la-ypformat.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libknot/yparser/ypformat.c' object='libknot/yparser/libknot_la-ypformat.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/yparser/libknot_la-ypformat.lo `test -f 'libknot/yparser/ypformat.c' || echo '$(srcdir)/'`libknot/yparser/ypformat.c + +libknot/yparser/libknot_la-ypschema.lo: libknot/yparser/ypschema.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/yparser/libknot_la-ypschema.lo -MD -MP -MF libknot/yparser/$(DEPDIR)/libknot_la-ypschema.Tpo -c -o libknot/yparser/libknot_la-ypschema.lo `test -f 'libknot/yparser/ypschema.c' || echo '$(srcdir)/'`libknot/yparser/ypschema.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/yparser/$(DEPDIR)/libknot_la-ypschema.Tpo libknot/yparser/$(DEPDIR)/libknot_la-ypschema.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libknot/yparser/ypschema.c' object='libknot/yparser/libknot_la-ypschema.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/yparser/libknot_la-ypschema.lo `test -f 'libknot/yparser/ypschema.c' || echo '$(srcdir)/'`libknot/yparser/ypschema.c + +libknot/yparser/libknot_la-yptrafo.lo: libknot/yparser/yptrafo.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libknot/yparser/libknot_la-yptrafo.lo -MD -MP -MF libknot/yparser/$(DEPDIR)/libknot_la-yptrafo.Tpo -c -o libknot/yparser/libknot_la-yptrafo.lo `test -f 'libknot/yparser/yptrafo.c' || echo '$(srcdir)/'`libknot/yparser/yptrafo.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libknot/yparser/$(DEPDIR)/libknot_la-yptrafo.Tpo libknot/yparser/$(DEPDIR)/libknot_la-yptrafo.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libknot/yparser/yptrafo.c' object='libknot/yparser/libknot_la-yptrafo.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknot_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libknot/yparser/libknot_la-yptrafo.lo `test -f 'libknot/yparser/yptrafo.c' || echo '$(srcdir)/'`libknot/yparser/yptrafo.c + +knot/conf/libknotd_la-base.lo: knot/conf/base.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/conf/libknotd_la-base.lo -MD -MP -MF knot/conf/$(DEPDIR)/libknotd_la-base.Tpo -c -o knot/conf/libknotd_la-base.lo `test -f 'knot/conf/base.c' || echo '$(srcdir)/'`knot/conf/base.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/conf/$(DEPDIR)/libknotd_la-base.Tpo knot/conf/$(DEPDIR)/libknotd_la-base.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/conf/base.c' object='knot/conf/libknotd_la-base.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/conf/libknotd_la-base.lo `test -f 'knot/conf/base.c' || echo '$(srcdir)/'`knot/conf/base.c + +knot/conf/libknotd_la-conf.lo: knot/conf/conf.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/conf/libknotd_la-conf.lo -MD -MP -MF knot/conf/$(DEPDIR)/libknotd_la-conf.Tpo -c -o knot/conf/libknotd_la-conf.lo `test -f 'knot/conf/conf.c' || echo '$(srcdir)/'`knot/conf/conf.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/conf/$(DEPDIR)/libknotd_la-conf.Tpo knot/conf/$(DEPDIR)/libknotd_la-conf.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/conf/conf.c' object='knot/conf/libknotd_la-conf.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/conf/libknotd_la-conf.lo `test -f 'knot/conf/conf.c' || echo '$(srcdir)/'`knot/conf/conf.c + +knot/conf/libknotd_la-confdb.lo: knot/conf/confdb.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/conf/libknotd_la-confdb.lo -MD -MP -MF knot/conf/$(DEPDIR)/libknotd_la-confdb.Tpo -c -o knot/conf/libknotd_la-confdb.lo `test -f 'knot/conf/confdb.c' || echo '$(srcdir)/'`knot/conf/confdb.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/conf/$(DEPDIR)/libknotd_la-confdb.Tpo knot/conf/$(DEPDIR)/libknotd_la-confdb.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/conf/confdb.c' object='knot/conf/libknotd_la-confdb.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/conf/libknotd_la-confdb.lo `test -f 'knot/conf/confdb.c' || echo '$(srcdir)/'`knot/conf/confdb.c + +knot/conf/libknotd_la-confio.lo: knot/conf/confio.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/conf/libknotd_la-confio.lo -MD -MP -MF knot/conf/$(DEPDIR)/libknotd_la-confio.Tpo -c -o knot/conf/libknotd_la-confio.lo `test -f 'knot/conf/confio.c' || echo '$(srcdir)/'`knot/conf/confio.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/conf/$(DEPDIR)/libknotd_la-confio.Tpo knot/conf/$(DEPDIR)/libknotd_la-confio.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/conf/confio.c' object='knot/conf/libknotd_la-confio.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/conf/libknotd_la-confio.lo `test -f 'knot/conf/confio.c' || echo '$(srcdir)/'`knot/conf/confio.c + +knot/conf/libknotd_la-migration.lo: knot/conf/migration.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/conf/libknotd_la-migration.lo -MD -MP -MF knot/conf/$(DEPDIR)/libknotd_la-migration.Tpo -c -o knot/conf/libknotd_la-migration.lo `test -f 'knot/conf/migration.c' || echo '$(srcdir)/'`knot/conf/migration.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/conf/$(DEPDIR)/libknotd_la-migration.Tpo knot/conf/$(DEPDIR)/libknotd_la-migration.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/conf/migration.c' object='knot/conf/libknotd_la-migration.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/conf/libknotd_la-migration.lo `test -f 'knot/conf/migration.c' || echo '$(srcdir)/'`knot/conf/migration.c + +knot/conf/libknotd_la-module.lo: knot/conf/module.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/conf/libknotd_la-module.lo -MD -MP -MF knot/conf/$(DEPDIR)/libknotd_la-module.Tpo -c -o knot/conf/libknotd_la-module.lo `test -f 'knot/conf/module.c' || echo '$(srcdir)/'`knot/conf/module.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/conf/$(DEPDIR)/libknotd_la-module.Tpo knot/conf/$(DEPDIR)/libknotd_la-module.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/conf/module.c' object='knot/conf/libknotd_la-module.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/conf/libknotd_la-module.lo `test -f 'knot/conf/module.c' || echo '$(srcdir)/'`knot/conf/module.c + +knot/conf/libknotd_la-schema.lo: knot/conf/schema.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/conf/libknotd_la-schema.lo -MD -MP -MF knot/conf/$(DEPDIR)/libknotd_la-schema.Tpo -c -o knot/conf/libknotd_la-schema.lo `test -f 'knot/conf/schema.c' || echo '$(srcdir)/'`knot/conf/schema.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/conf/$(DEPDIR)/libknotd_la-schema.Tpo knot/conf/$(DEPDIR)/libknotd_la-schema.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/conf/schema.c' object='knot/conf/libknotd_la-schema.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/conf/libknotd_la-schema.lo `test -f 'knot/conf/schema.c' || echo '$(srcdir)/'`knot/conf/schema.c + +knot/conf/libknotd_la-tools.lo: knot/conf/tools.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/conf/libknotd_la-tools.lo -MD -MP -MF knot/conf/$(DEPDIR)/libknotd_la-tools.Tpo -c -o knot/conf/libknotd_la-tools.lo `test -f 'knot/conf/tools.c' || echo '$(srcdir)/'`knot/conf/tools.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/conf/$(DEPDIR)/libknotd_la-tools.Tpo knot/conf/$(DEPDIR)/libknotd_la-tools.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/conf/tools.c' object='knot/conf/libknotd_la-tools.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/conf/libknotd_la-tools.lo `test -f 'knot/conf/tools.c' || echo '$(srcdir)/'`knot/conf/tools.c + +knot/ctl/libknotd_la-commands.lo: knot/ctl/commands.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/ctl/libknotd_la-commands.lo -MD -MP -MF knot/ctl/$(DEPDIR)/libknotd_la-commands.Tpo -c -o knot/ctl/libknotd_la-commands.lo `test -f 'knot/ctl/commands.c' || echo '$(srcdir)/'`knot/ctl/commands.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/ctl/$(DEPDIR)/libknotd_la-commands.Tpo knot/ctl/$(DEPDIR)/libknotd_la-commands.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/ctl/commands.c' object='knot/ctl/libknotd_la-commands.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/ctl/libknotd_la-commands.lo `test -f 'knot/ctl/commands.c' || echo '$(srcdir)/'`knot/ctl/commands.c + +knot/ctl/libknotd_la-process.lo: knot/ctl/process.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/ctl/libknotd_la-process.lo -MD -MP -MF knot/ctl/$(DEPDIR)/libknotd_la-process.Tpo -c -o knot/ctl/libknotd_la-process.lo `test -f 'knot/ctl/process.c' || echo '$(srcdir)/'`knot/ctl/process.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/ctl/$(DEPDIR)/libknotd_la-process.Tpo knot/ctl/$(DEPDIR)/libknotd_la-process.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/ctl/process.c' object='knot/ctl/libknotd_la-process.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/ctl/libknotd_la-process.lo `test -f 'knot/ctl/process.c' || echo '$(srcdir)/'`knot/ctl/process.c + +knot/dnssec/libknotd_la-context.lo: knot/dnssec/context.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/dnssec/libknotd_la-context.lo -MD -MP -MF knot/dnssec/$(DEPDIR)/libknotd_la-context.Tpo -c -o knot/dnssec/libknotd_la-context.lo `test -f 'knot/dnssec/context.c' || echo '$(srcdir)/'`knot/dnssec/context.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/dnssec/$(DEPDIR)/libknotd_la-context.Tpo knot/dnssec/$(DEPDIR)/libknotd_la-context.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/dnssec/context.c' object='knot/dnssec/libknotd_la-context.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/dnssec/libknotd_la-context.lo `test -f 'knot/dnssec/context.c' || echo '$(srcdir)/'`knot/dnssec/context.c + +knot/dnssec/libknotd_la-ds_query.lo: knot/dnssec/ds_query.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/dnssec/libknotd_la-ds_query.lo -MD -MP -MF knot/dnssec/$(DEPDIR)/libknotd_la-ds_query.Tpo -c -o knot/dnssec/libknotd_la-ds_query.lo `test -f 'knot/dnssec/ds_query.c' || echo '$(srcdir)/'`knot/dnssec/ds_query.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/dnssec/$(DEPDIR)/libknotd_la-ds_query.Tpo knot/dnssec/$(DEPDIR)/libknotd_la-ds_query.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/dnssec/ds_query.c' object='knot/dnssec/libknotd_la-ds_query.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/dnssec/libknotd_la-ds_query.lo `test -f 'knot/dnssec/ds_query.c' || echo '$(srcdir)/'`knot/dnssec/ds_query.c + +knot/dnssec/kasp/libknotd_la-kasp_db.lo: knot/dnssec/kasp/kasp_db.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/dnssec/kasp/libknotd_la-kasp_db.lo -MD -MP -MF knot/dnssec/kasp/$(DEPDIR)/libknotd_la-kasp_db.Tpo -c -o knot/dnssec/kasp/libknotd_la-kasp_db.lo `test -f 'knot/dnssec/kasp/kasp_db.c' || echo '$(srcdir)/'`knot/dnssec/kasp/kasp_db.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/dnssec/kasp/$(DEPDIR)/libknotd_la-kasp_db.Tpo knot/dnssec/kasp/$(DEPDIR)/libknotd_la-kasp_db.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/dnssec/kasp/kasp_db.c' object='knot/dnssec/kasp/libknotd_la-kasp_db.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/dnssec/kasp/libknotd_la-kasp_db.lo `test -f 'knot/dnssec/kasp/kasp_db.c' || echo '$(srcdir)/'`knot/dnssec/kasp/kasp_db.c + +knot/dnssec/kasp/libknotd_la-kasp_zone.lo: knot/dnssec/kasp/kasp_zone.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/dnssec/kasp/libknotd_la-kasp_zone.lo -MD -MP -MF knot/dnssec/kasp/$(DEPDIR)/libknotd_la-kasp_zone.Tpo -c -o knot/dnssec/kasp/libknotd_la-kasp_zone.lo `test -f 'knot/dnssec/kasp/kasp_zone.c' || echo '$(srcdir)/'`knot/dnssec/kasp/kasp_zone.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/dnssec/kasp/$(DEPDIR)/libknotd_la-kasp_zone.Tpo knot/dnssec/kasp/$(DEPDIR)/libknotd_la-kasp_zone.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/dnssec/kasp/kasp_zone.c' object='knot/dnssec/kasp/libknotd_la-kasp_zone.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/dnssec/kasp/libknotd_la-kasp_zone.lo `test -f 'knot/dnssec/kasp/kasp_zone.c' || echo '$(srcdir)/'`knot/dnssec/kasp/kasp_zone.c + +knot/dnssec/kasp/libknotd_la-keystate.lo: knot/dnssec/kasp/keystate.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/dnssec/kasp/libknotd_la-keystate.lo -MD -MP -MF knot/dnssec/kasp/$(DEPDIR)/libknotd_la-keystate.Tpo -c -o knot/dnssec/kasp/libknotd_la-keystate.lo `test -f 'knot/dnssec/kasp/keystate.c' || echo '$(srcdir)/'`knot/dnssec/kasp/keystate.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/dnssec/kasp/$(DEPDIR)/libknotd_la-keystate.Tpo knot/dnssec/kasp/$(DEPDIR)/libknotd_la-keystate.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/dnssec/kasp/keystate.c' object='knot/dnssec/kasp/libknotd_la-keystate.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/dnssec/kasp/libknotd_la-keystate.lo `test -f 'knot/dnssec/kasp/keystate.c' || echo '$(srcdir)/'`knot/dnssec/kasp/keystate.c + +knot/dnssec/kasp/libknotd_la-keystore.lo: knot/dnssec/kasp/keystore.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/dnssec/kasp/libknotd_la-keystore.lo -MD -MP -MF knot/dnssec/kasp/$(DEPDIR)/libknotd_la-keystore.Tpo -c -o knot/dnssec/kasp/libknotd_la-keystore.lo `test -f 'knot/dnssec/kasp/keystore.c' || echo '$(srcdir)/'`knot/dnssec/kasp/keystore.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/dnssec/kasp/$(DEPDIR)/libknotd_la-keystore.Tpo knot/dnssec/kasp/$(DEPDIR)/libknotd_la-keystore.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/dnssec/kasp/keystore.c' object='knot/dnssec/kasp/libknotd_la-keystore.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/dnssec/kasp/libknotd_la-keystore.lo `test -f 'knot/dnssec/kasp/keystore.c' || echo '$(srcdir)/'`knot/dnssec/kasp/keystore.c + +knot/dnssec/libknotd_la-key-events.lo: knot/dnssec/key-events.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/dnssec/libknotd_la-key-events.lo -MD -MP -MF knot/dnssec/$(DEPDIR)/libknotd_la-key-events.Tpo -c -o knot/dnssec/libknotd_la-key-events.lo `test -f 'knot/dnssec/key-events.c' || echo '$(srcdir)/'`knot/dnssec/key-events.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/dnssec/$(DEPDIR)/libknotd_la-key-events.Tpo knot/dnssec/$(DEPDIR)/libknotd_la-key-events.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/dnssec/key-events.c' object='knot/dnssec/libknotd_la-key-events.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/dnssec/libknotd_la-key-events.lo `test -f 'knot/dnssec/key-events.c' || echo '$(srcdir)/'`knot/dnssec/key-events.c + +knot/dnssec/libknotd_la-nsec-chain.lo: knot/dnssec/nsec-chain.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/dnssec/libknotd_la-nsec-chain.lo -MD -MP -MF knot/dnssec/$(DEPDIR)/libknotd_la-nsec-chain.Tpo -c -o knot/dnssec/libknotd_la-nsec-chain.lo `test -f 'knot/dnssec/nsec-chain.c' || echo '$(srcdir)/'`knot/dnssec/nsec-chain.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/dnssec/$(DEPDIR)/libknotd_la-nsec-chain.Tpo knot/dnssec/$(DEPDIR)/libknotd_la-nsec-chain.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/dnssec/nsec-chain.c' object='knot/dnssec/libknotd_la-nsec-chain.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/dnssec/libknotd_la-nsec-chain.lo `test -f 'knot/dnssec/nsec-chain.c' || echo '$(srcdir)/'`knot/dnssec/nsec-chain.c + +knot/dnssec/libknotd_la-nsec3-chain.lo: knot/dnssec/nsec3-chain.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/dnssec/libknotd_la-nsec3-chain.lo -MD -MP -MF knot/dnssec/$(DEPDIR)/libknotd_la-nsec3-chain.Tpo -c -o knot/dnssec/libknotd_la-nsec3-chain.lo `test -f 'knot/dnssec/nsec3-chain.c' || echo '$(srcdir)/'`knot/dnssec/nsec3-chain.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/dnssec/$(DEPDIR)/libknotd_la-nsec3-chain.Tpo knot/dnssec/$(DEPDIR)/libknotd_la-nsec3-chain.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/dnssec/nsec3-chain.c' object='knot/dnssec/libknotd_la-nsec3-chain.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/dnssec/libknotd_la-nsec3-chain.lo `test -f 'knot/dnssec/nsec3-chain.c' || echo '$(srcdir)/'`knot/dnssec/nsec3-chain.c + +knot/dnssec/libknotd_la-policy.lo: knot/dnssec/policy.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/dnssec/libknotd_la-policy.lo -MD -MP -MF knot/dnssec/$(DEPDIR)/libknotd_la-policy.Tpo -c -o knot/dnssec/libknotd_la-policy.lo `test -f 'knot/dnssec/policy.c' || echo '$(srcdir)/'`knot/dnssec/policy.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/dnssec/$(DEPDIR)/libknotd_la-policy.Tpo knot/dnssec/$(DEPDIR)/libknotd_la-policy.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/dnssec/policy.c' object='knot/dnssec/libknotd_la-policy.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/dnssec/libknotd_la-policy.lo `test -f 'knot/dnssec/policy.c' || echo '$(srcdir)/'`knot/dnssec/policy.c + +knot/dnssec/libknotd_la-rrset-sign.lo: knot/dnssec/rrset-sign.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/dnssec/libknotd_la-rrset-sign.lo -MD -MP -MF knot/dnssec/$(DEPDIR)/libknotd_la-rrset-sign.Tpo -c -o knot/dnssec/libknotd_la-rrset-sign.lo `test -f 'knot/dnssec/rrset-sign.c' || echo '$(srcdir)/'`knot/dnssec/rrset-sign.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/dnssec/$(DEPDIR)/libknotd_la-rrset-sign.Tpo knot/dnssec/$(DEPDIR)/libknotd_la-rrset-sign.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/dnssec/rrset-sign.c' object='knot/dnssec/libknotd_la-rrset-sign.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/dnssec/libknotd_la-rrset-sign.lo `test -f 'knot/dnssec/rrset-sign.c' || echo '$(srcdir)/'`knot/dnssec/rrset-sign.c + +knot/dnssec/libknotd_la-zone-events.lo: knot/dnssec/zone-events.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/dnssec/libknotd_la-zone-events.lo -MD -MP -MF knot/dnssec/$(DEPDIR)/libknotd_la-zone-events.Tpo -c -o knot/dnssec/libknotd_la-zone-events.lo `test -f 'knot/dnssec/zone-events.c' || echo '$(srcdir)/'`knot/dnssec/zone-events.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/dnssec/$(DEPDIR)/libknotd_la-zone-events.Tpo knot/dnssec/$(DEPDIR)/libknotd_la-zone-events.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/dnssec/zone-events.c' object='knot/dnssec/libknotd_la-zone-events.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/dnssec/libknotd_la-zone-events.lo `test -f 'knot/dnssec/zone-events.c' || echo '$(srcdir)/'`knot/dnssec/zone-events.c + +knot/dnssec/libknotd_la-zone-keys.lo: knot/dnssec/zone-keys.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/dnssec/libknotd_la-zone-keys.lo -MD -MP -MF knot/dnssec/$(DEPDIR)/libknotd_la-zone-keys.Tpo -c -o knot/dnssec/libknotd_la-zone-keys.lo `test -f 'knot/dnssec/zone-keys.c' || echo '$(srcdir)/'`knot/dnssec/zone-keys.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/dnssec/$(DEPDIR)/libknotd_la-zone-keys.Tpo knot/dnssec/$(DEPDIR)/libknotd_la-zone-keys.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/dnssec/zone-keys.c' object='knot/dnssec/libknotd_la-zone-keys.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/dnssec/libknotd_la-zone-keys.lo `test -f 'knot/dnssec/zone-keys.c' || echo '$(srcdir)/'`knot/dnssec/zone-keys.c + +knot/dnssec/libknotd_la-zone-nsec.lo: knot/dnssec/zone-nsec.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/dnssec/libknotd_la-zone-nsec.lo -MD -MP -MF knot/dnssec/$(DEPDIR)/libknotd_la-zone-nsec.Tpo -c -o knot/dnssec/libknotd_la-zone-nsec.lo `test -f 'knot/dnssec/zone-nsec.c' || echo '$(srcdir)/'`knot/dnssec/zone-nsec.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/dnssec/$(DEPDIR)/libknotd_la-zone-nsec.Tpo knot/dnssec/$(DEPDIR)/libknotd_la-zone-nsec.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/dnssec/zone-nsec.c' object='knot/dnssec/libknotd_la-zone-nsec.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/dnssec/libknotd_la-zone-nsec.lo `test -f 'knot/dnssec/zone-nsec.c' || echo '$(srcdir)/'`knot/dnssec/zone-nsec.c + +knot/dnssec/libknotd_la-zone-sign.lo: knot/dnssec/zone-sign.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/dnssec/libknotd_la-zone-sign.lo -MD -MP -MF knot/dnssec/$(DEPDIR)/libknotd_la-zone-sign.Tpo -c -o knot/dnssec/libknotd_la-zone-sign.lo `test -f 'knot/dnssec/zone-sign.c' || echo '$(srcdir)/'`knot/dnssec/zone-sign.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/dnssec/$(DEPDIR)/libknotd_la-zone-sign.Tpo knot/dnssec/$(DEPDIR)/libknotd_la-zone-sign.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/dnssec/zone-sign.c' object='knot/dnssec/libknotd_la-zone-sign.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/dnssec/libknotd_la-zone-sign.lo `test -f 'knot/dnssec/zone-sign.c' || echo '$(srcdir)/'`knot/dnssec/zone-sign.c + +knot/events/libknotd_la-events.lo: knot/events/events.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/events/libknotd_la-events.lo -MD -MP -MF knot/events/$(DEPDIR)/libknotd_la-events.Tpo -c -o knot/events/libknotd_la-events.lo `test -f 'knot/events/events.c' || echo '$(srcdir)/'`knot/events/events.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/events/$(DEPDIR)/libknotd_la-events.Tpo knot/events/$(DEPDIR)/libknotd_la-events.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/events/events.c' object='knot/events/libknotd_la-events.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/events/libknotd_la-events.lo `test -f 'knot/events/events.c' || echo '$(srcdir)/'`knot/events/events.c + +knot/events/handlers/libknotd_la-dnssec.lo: knot/events/handlers/dnssec.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/events/handlers/libknotd_la-dnssec.lo -MD -MP -MF knot/events/handlers/$(DEPDIR)/libknotd_la-dnssec.Tpo -c -o knot/events/handlers/libknotd_la-dnssec.lo `test -f 'knot/events/handlers/dnssec.c' || echo '$(srcdir)/'`knot/events/handlers/dnssec.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/events/handlers/$(DEPDIR)/libknotd_la-dnssec.Tpo knot/events/handlers/$(DEPDIR)/libknotd_la-dnssec.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/events/handlers/dnssec.c' object='knot/events/handlers/libknotd_la-dnssec.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/events/handlers/libknotd_la-dnssec.lo `test -f 'knot/events/handlers/dnssec.c' || echo '$(srcdir)/'`knot/events/handlers/dnssec.c + +knot/events/handlers/libknotd_la-expire.lo: knot/events/handlers/expire.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/events/handlers/libknotd_la-expire.lo -MD -MP -MF knot/events/handlers/$(DEPDIR)/libknotd_la-expire.Tpo -c -o knot/events/handlers/libknotd_la-expire.lo `test -f 'knot/events/handlers/expire.c' || echo '$(srcdir)/'`knot/events/handlers/expire.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/events/handlers/$(DEPDIR)/libknotd_la-expire.Tpo knot/events/handlers/$(DEPDIR)/libknotd_la-expire.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/events/handlers/expire.c' object='knot/events/handlers/libknotd_la-expire.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/events/handlers/libknotd_la-expire.lo `test -f 'knot/events/handlers/expire.c' || echo '$(srcdir)/'`knot/events/handlers/expire.c + +knot/events/handlers/libknotd_la-flush.lo: knot/events/handlers/flush.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/events/handlers/libknotd_la-flush.lo -MD -MP -MF knot/events/handlers/$(DEPDIR)/libknotd_la-flush.Tpo -c -o knot/events/handlers/libknotd_la-flush.lo `test -f 'knot/events/handlers/flush.c' || echo '$(srcdir)/'`knot/events/handlers/flush.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/events/handlers/$(DEPDIR)/libknotd_la-flush.Tpo knot/events/handlers/$(DEPDIR)/libknotd_la-flush.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/events/handlers/flush.c' object='knot/events/handlers/libknotd_la-flush.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/events/handlers/libknotd_la-flush.lo `test -f 'knot/events/handlers/flush.c' || echo '$(srcdir)/'`knot/events/handlers/flush.c + +knot/events/handlers/libknotd_la-freeze_thaw.lo: knot/events/handlers/freeze_thaw.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/events/handlers/libknotd_la-freeze_thaw.lo -MD -MP -MF knot/events/handlers/$(DEPDIR)/libknotd_la-freeze_thaw.Tpo -c -o knot/events/handlers/libknotd_la-freeze_thaw.lo `test -f 'knot/events/handlers/freeze_thaw.c' || echo '$(srcdir)/'`knot/events/handlers/freeze_thaw.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/events/handlers/$(DEPDIR)/libknotd_la-freeze_thaw.Tpo knot/events/handlers/$(DEPDIR)/libknotd_la-freeze_thaw.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/events/handlers/freeze_thaw.c' object='knot/events/handlers/libknotd_la-freeze_thaw.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/events/handlers/libknotd_la-freeze_thaw.lo `test -f 'knot/events/handlers/freeze_thaw.c' || echo '$(srcdir)/'`knot/events/handlers/freeze_thaw.c + +knot/events/handlers/libknotd_la-load.lo: knot/events/handlers/load.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/events/handlers/libknotd_la-load.lo -MD -MP -MF knot/events/handlers/$(DEPDIR)/libknotd_la-load.Tpo -c -o knot/events/handlers/libknotd_la-load.lo `test -f 'knot/events/handlers/load.c' || echo '$(srcdir)/'`knot/events/handlers/load.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/events/handlers/$(DEPDIR)/libknotd_la-load.Tpo knot/events/handlers/$(DEPDIR)/libknotd_la-load.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/events/handlers/load.c' object='knot/events/handlers/libknotd_la-load.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/events/handlers/libknotd_la-load.lo `test -f 'knot/events/handlers/load.c' || echo '$(srcdir)/'`knot/events/handlers/load.c + +knot/events/handlers/libknotd_la-notify.lo: knot/events/handlers/notify.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/events/handlers/libknotd_la-notify.lo -MD -MP -MF knot/events/handlers/$(DEPDIR)/libknotd_la-notify.Tpo -c -o knot/events/handlers/libknotd_la-notify.lo `test -f 'knot/events/handlers/notify.c' || echo '$(srcdir)/'`knot/events/handlers/notify.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/events/handlers/$(DEPDIR)/libknotd_la-notify.Tpo knot/events/handlers/$(DEPDIR)/libknotd_la-notify.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/events/handlers/notify.c' object='knot/events/handlers/libknotd_la-notify.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/events/handlers/libknotd_la-notify.lo `test -f 'knot/events/handlers/notify.c' || echo '$(srcdir)/'`knot/events/handlers/notify.c + +knot/events/handlers/libknotd_la-nsec3resalt.lo: knot/events/handlers/nsec3resalt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/events/handlers/libknotd_la-nsec3resalt.lo -MD -MP -MF knot/events/handlers/$(DEPDIR)/libknotd_la-nsec3resalt.Tpo -c -o knot/events/handlers/libknotd_la-nsec3resalt.lo `test -f 'knot/events/handlers/nsec3resalt.c' || echo '$(srcdir)/'`knot/events/handlers/nsec3resalt.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/events/handlers/$(DEPDIR)/libknotd_la-nsec3resalt.Tpo knot/events/handlers/$(DEPDIR)/libknotd_la-nsec3resalt.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/events/handlers/nsec3resalt.c' object='knot/events/handlers/libknotd_la-nsec3resalt.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/events/handlers/libknotd_la-nsec3resalt.lo `test -f 'knot/events/handlers/nsec3resalt.c' || echo '$(srcdir)/'`knot/events/handlers/nsec3resalt.c + +knot/events/handlers/libknotd_la-refresh.lo: knot/events/handlers/refresh.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/events/handlers/libknotd_la-refresh.lo -MD -MP -MF knot/events/handlers/$(DEPDIR)/libknotd_la-refresh.Tpo -c -o knot/events/handlers/libknotd_la-refresh.lo `test -f 'knot/events/handlers/refresh.c' || echo '$(srcdir)/'`knot/events/handlers/refresh.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/events/handlers/$(DEPDIR)/libknotd_la-refresh.Tpo knot/events/handlers/$(DEPDIR)/libknotd_la-refresh.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/events/handlers/refresh.c' object='knot/events/handlers/libknotd_la-refresh.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/events/handlers/libknotd_la-refresh.lo `test -f 'knot/events/handlers/refresh.c' || echo '$(srcdir)/'`knot/events/handlers/refresh.c + +knot/events/handlers/libknotd_la-update.lo: knot/events/handlers/update.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/events/handlers/libknotd_la-update.lo -MD -MP -MF knot/events/handlers/$(DEPDIR)/libknotd_la-update.Tpo -c -o knot/events/handlers/libknotd_la-update.lo `test -f 'knot/events/handlers/update.c' || echo '$(srcdir)/'`knot/events/handlers/update.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/events/handlers/$(DEPDIR)/libknotd_la-update.Tpo knot/events/handlers/$(DEPDIR)/libknotd_la-update.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/events/handlers/update.c' object='knot/events/handlers/libknotd_la-update.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/events/handlers/libknotd_la-update.lo `test -f 'knot/events/handlers/update.c' || echo '$(srcdir)/'`knot/events/handlers/update.c + +knot/events/handlers/libknotd_la-parent_ds_query.lo: knot/events/handlers/parent_ds_query.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/events/handlers/libknotd_la-parent_ds_query.lo -MD -MP -MF knot/events/handlers/$(DEPDIR)/libknotd_la-parent_ds_query.Tpo -c -o knot/events/handlers/libknotd_la-parent_ds_query.lo `test -f 'knot/events/handlers/parent_ds_query.c' || echo '$(srcdir)/'`knot/events/handlers/parent_ds_query.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/events/handlers/$(DEPDIR)/libknotd_la-parent_ds_query.Tpo knot/events/handlers/$(DEPDIR)/libknotd_la-parent_ds_query.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/events/handlers/parent_ds_query.c' object='knot/events/handlers/libknotd_la-parent_ds_query.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/events/handlers/libknotd_la-parent_ds_query.lo `test -f 'knot/events/handlers/parent_ds_query.c' || echo '$(srcdir)/'`knot/events/handlers/parent_ds_query.c + +knot/events/libknotd_la-replan.lo: knot/events/replan.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/events/libknotd_la-replan.lo -MD -MP -MF knot/events/$(DEPDIR)/libknotd_la-replan.Tpo -c -o knot/events/libknotd_la-replan.lo `test -f 'knot/events/replan.c' || echo '$(srcdir)/'`knot/events/replan.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/events/$(DEPDIR)/libknotd_la-replan.Tpo knot/events/$(DEPDIR)/libknotd_la-replan.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/events/replan.c' object='knot/events/libknotd_la-replan.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/events/libknotd_la-replan.lo `test -f 'knot/events/replan.c' || echo '$(srcdir)/'`knot/events/replan.c + +knot/nameserver/libknotd_la-axfr.lo: knot/nameserver/axfr.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/nameserver/libknotd_la-axfr.lo -MD -MP -MF knot/nameserver/$(DEPDIR)/libknotd_la-axfr.Tpo -c -o knot/nameserver/libknotd_la-axfr.lo `test -f 'knot/nameserver/axfr.c' || echo '$(srcdir)/'`knot/nameserver/axfr.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/nameserver/$(DEPDIR)/libknotd_la-axfr.Tpo knot/nameserver/$(DEPDIR)/libknotd_la-axfr.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/nameserver/axfr.c' object='knot/nameserver/libknotd_la-axfr.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/nameserver/libknotd_la-axfr.lo `test -f 'knot/nameserver/axfr.c' || echo '$(srcdir)/'`knot/nameserver/axfr.c + +knot/nameserver/libknotd_la-chaos.lo: knot/nameserver/chaos.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/nameserver/libknotd_la-chaos.lo -MD -MP -MF knot/nameserver/$(DEPDIR)/libknotd_la-chaos.Tpo -c -o knot/nameserver/libknotd_la-chaos.lo `test -f 'knot/nameserver/chaos.c' || echo '$(srcdir)/'`knot/nameserver/chaos.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/nameserver/$(DEPDIR)/libknotd_la-chaos.Tpo knot/nameserver/$(DEPDIR)/libknotd_la-chaos.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/nameserver/chaos.c' object='knot/nameserver/libknotd_la-chaos.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/nameserver/libknotd_la-chaos.lo `test -f 'knot/nameserver/chaos.c' || echo '$(srcdir)/'`knot/nameserver/chaos.c + +knot/nameserver/libknotd_la-internet.lo: knot/nameserver/internet.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/nameserver/libknotd_la-internet.lo -MD -MP -MF knot/nameserver/$(DEPDIR)/libknotd_la-internet.Tpo -c -o knot/nameserver/libknotd_la-internet.lo `test -f 'knot/nameserver/internet.c' || echo '$(srcdir)/'`knot/nameserver/internet.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/nameserver/$(DEPDIR)/libknotd_la-internet.Tpo knot/nameserver/$(DEPDIR)/libknotd_la-internet.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/nameserver/internet.c' object='knot/nameserver/libknotd_la-internet.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/nameserver/libknotd_la-internet.lo `test -f 'knot/nameserver/internet.c' || echo '$(srcdir)/'`knot/nameserver/internet.c + +knot/nameserver/libknotd_la-ixfr.lo: knot/nameserver/ixfr.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/nameserver/libknotd_la-ixfr.lo -MD -MP -MF knot/nameserver/$(DEPDIR)/libknotd_la-ixfr.Tpo -c -o knot/nameserver/libknotd_la-ixfr.lo `test -f 'knot/nameserver/ixfr.c' || echo '$(srcdir)/'`knot/nameserver/ixfr.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/nameserver/$(DEPDIR)/libknotd_la-ixfr.Tpo knot/nameserver/$(DEPDIR)/libknotd_la-ixfr.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/nameserver/ixfr.c' object='knot/nameserver/libknotd_la-ixfr.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/nameserver/libknotd_la-ixfr.lo `test -f 'knot/nameserver/ixfr.c' || echo '$(srcdir)/'`knot/nameserver/ixfr.c + +knot/nameserver/libknotd_la-notify.lo: knot/nameserver/notify.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/nameserver/libknotd_la-notify.lo -MD -MP -MF knot/nameserver/$(DEPDIR)/libknotd_la-notify.Tpo -c -o knot/nameserver/libknotd_la-notify.lo `test -f 'knot/nameserver/notify.c' || echo '$(srcdir)/'`knot/nameserver/notify.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/nameserver/$(DEPDIR)/libknotd_la-notify.Tpo knot/nameserver/$(DEPDIR)/libknotd_la-notify.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/nameserver/notify.c' object='knot/nameserver/libknotd_la-notify.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/nameserver/libknotd_la-notify.lo `test -f 'knot/nameserver/notify.c' || echo '$(srcdir)/'`knot/nameserver/notify.c + +knot/nameserver/libknotd_la-nsec_proofs.lo: knot/nameserver/nsec_proofs.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/nameserver/libknotd_la-nsec_proofs.lo -MD -MP -MF knot/nameserver/$(DEPDIR)/libknotd_la-nsec_proofs.Tpo -c -o knot/nameserver/libknotd_la-nsec_proofs.lo `test -f 'knot/nameserver/nsec_proofs.c' || echo '$(srcdir)/'`knot/nameserver/nsec_proofs.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/nameserver/$(DEPDIR)/libknotd_la-nsec_proofs.Tpo knot/nameserver/$(DEPDIR)/libknotd_la-nsec_proofs.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/nameserver/nsec_proofs.c' object='knot/nameserver/libknotd_la-nsec_proofs.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/nameserver/libknotd_la-nsec_proofs.lo `test -f 'knot/nameserver/nsec_proofs.c' || echo '$(srcdir)/'`knot/nameserver/nsec_proofs.c + +knot/nameserver/libknotd_la-process_query.lo: knot/nameserver/process_query.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/nameserver/libknotd_la-process_query.lo -MD -MP -MF knot/nameserver/$(DEPDIR)/libknotd_la-process_query.Tpo -c -o knot/nameserver/libknotd_la-process_query.lo `test -f 'knot/nameserver/process_query.c' || echo '$(srcdir)/'`knot/nameserver/process_query.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/nameserver/$(DEPDIR)/libknotd_la-process_query.Tpo knot/nameserver/$(DEPDIR)/libknotd_la-process_query.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/nameserver/process_query.c' object='knot/nameserver/libknotd_la-process_query.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/nameserver/libknotd_la-process_query.lo `test -f 'knot/nameserver/process_query.c' || echo '$(srcdir)/'`knot/nameserver/process_query.c + +knot/nameserver/libknotd_la-query_module.lo: knot/nameserver/query_module.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/nameserver/libknotd_la-query_module.lo -MD -MP -MF knot/nameserver/$(DEPDIR)/libknotd_la-query_module.Tpo -c -o knot/nameserver/libknotd_la-query_module.lo `test -f 'knot/nameserver/query_module.c' || echo '$(srcdir)/'`knot/nameserver/query_module.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/nameserver/$(DEPDIR)/libknotd_la-query_module.Tpo knot/nameserver/$(DEPDIR)/libknotd_la-query_module.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/nameserver/query_module.c' object='knot/nameserver/libknotd_la-query_module.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/nameserver/libknotd_la-query_module.lo `test -f 'knot/nameserver/query_module.c' || echo '$(srcdir)/'`knot/nameserver/query_module.c + +knot/nameserver/libknotd_la-tsig_ctx.lo: knot/nameserver/tsig_ctx.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/nameserver/libknotd_la-tsig_ctx.lo -MD -MP -MF knot/nameserver/$(DEPDIR)/libknotd_la-tsig_ctx.Tpo -c -o knot/nameserver/libknotd_la-tsig_ctx.lo `test -f 'knot/nameserver/tsig_ctx.c' || echo '$(srcdir)/'`knot/nameserver/tsig_ctx.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/nameserver/$(DEPDIR)/libknotd_la-tsig_ctx.Tpo knot/nameserver/$(DEPDIR)/libknotd_la-tsig_ctx.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/nameserver/tsig_ctx.c' object='knot/nameserver/libknotd_la-tsig_ctx.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/nameserver/libknotd_la-tsig_ctx.lo `test -f 'knot/nameserver/tsig_ctx.c' || echo '$(srcdir)/'`knot/nameserver/tsig_ctx.c + +knot/nameserver/libknotd_la-update.lo: knot/nameserver/update.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/nameserver/libknotd_la-update.lo -MD -MP -MF knot/nameserver/$(DEPDIR)/libknotd_la-update.Tpo -c -o knot/nameserver/libknotd_la-update.lo `test -f 'knot/nameserver/update.c' || echo '$(srcdir)/'`knot/nameserver/update.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/nameserver/$(DEPDIR)/libknotd_la-update.Tpo knot/nameserver/$(DEPDIR)/libknotd_la-update.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/nameserver/update.c' object='knot/nameserver/libknotd_la-update.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/nameserver/libknotd_la-update.lo `test -f 'knot/nameserver/update.c' || echo '$(srcdir)/'`knot/nameserver/update.c + +knot/nameserver/libknotd_la-xfr.lo: knot/nameserver/xfr.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/nameserver/libknotd_la-xfr.lo -MD -MP -MF knot/nameserver/$(DEPDIR)/libknotd_la-xfr.Tpo -c -o knot/nameserver/libknotd_la-xfr.lo `test -f 'knot/nameserver/xfr.c' || echo '$(srcdir)/'`knot/nameserver/xfr.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/nameserver/$(DEPDIR)/libknotd_la-xfr.Tpo knot/nameserver/$(DEPDIR)/libknotd_la-xfr.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/nameserver/xfr.c' object='knot/nameserver/libknotd_la-xfr.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/nameserver/libknotd_la-xfr.lo `test -f 'knot/nameserver/xfr.c' || echo '$(srcdir)/'`knot/nameserver/xfr.c + +knot/query/libknotd_la-capture.lo: knot/query/capture.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/query/libknotd_la-capture.lo -MD -MP -MF knot/query/$(DEPDIR)/libknotd_la-capture.Tpo -c -o knot/query/libknotd_la-capture.lo `test -f 'knot/query/capture.c' || echo '$(srcdir)/'`knot/query/capture.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/query/$(DEPDIR)/libknotd_la-capture.Tpo knot/query/$(DEPDIR)/libknotd_la-capture.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/query/capture.c' object='knot/query/libknotd_la-capture.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/query/libknotd_la-capture.lo `test -f 'knot/query/capture.c' || echo '$(srcdir)/'`knot/query/capture.c + +knot/query/libknotd_la-query.lo: knot/query/query.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/query/libknotd_la-query.lo -MD -MP -MF knot/query/$(DEPDIR)/libknotd_la-query.Tpo -c -o knot/query/libknotd_la-query.lo `test -f 'knot/query/query.c' || echo '$(srcdir)/'`knot/query/query.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/query/$(DEPDIR)/libknotd_la-query.Tpo knot/query/$(DEPDIR)/libknotd_la-query.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/query/query.c' object='knot/query/libknotd_la-query.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/query/libknotd_la-query.lo `test -f 'knot/query/query.c' || echo '$(srcdir)/'`knot/query/query.c + +knot/query/libknotd_la-requestor.lo: knot/query/requestor.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/query/libknotd_la-requestor.lo -MD -MP -MF knot/query/$(DEPDIR)/libknotd_la-requestor.Tpo -c -o knot/query/libknotd_la-requestor.lo `test -f 'knot/query/requestor.c' || echo '$(srcdir)/'`knot/query/requestor.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/query/$(DEPDIR)/libknotd_la-requestor.Tpo knot/query/$(DEPDIR)/libknotd_la-requestor.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/query/requestor.c' object='knot/query/libknotd_la-requestor.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/query/libknotd_la-requestor.lo `test -f 'knot/query/requestor.c' || echo '$(srcdir)/'`knot/query/requestor.c + +knot/common/libknotd_la-evsched.lo: knot/common/evsched.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/common/libknotd_la-evsched.lo -MD -MP -MF knot/common/$(DEPDIR)/libknotd_la-evsched.Tpo -c -o knot/common/libknotd_la-evsched.lo `test -f 'knot/common/evsched.c' || echo '$(srcdir)/'`knot/common/evsched.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/common/$(DEPDIR)/libknotd_la-evsched.Tpo knot/common/$(DEPDIR)/libknotd_la-evsched.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/common/evsched.c' object='knot/common/libknotd_la-evsched.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/common/libknotd_la-evsched.lo `test -f 'knot/common/evsched.c' || echo '$(srcdir)/'`knot/common/evsched.c + +knot/common/libknotd_la-fdset.lo: knot/common/fdset.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/common/libknotd_la-fdset.lo -MD -MP -MF knot/common/$(DEPDIR)/libknotd_la-fdset.Tpo -c -o knot/common/libknotd_la-fdset.lo `test -f 'knot/common/fdset.c' || echo '$(srcdir)/'`knot/common/fdset.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/common/$(DEPDIR)/libknotd_la-fdset.Tpo knot/common/$(DEPDIR)/libknotd_la-fdset.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/common/fdset.c' object='knot/common/libknotd_la-fdset.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/common/libknotd_la-fdset.lo `test -f 'knot/common/fdset.c' || echo '$(srcdir)/'`knot/common/fdset.c + +knot/common/libknotd_la-log.lo: knot/common/log.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/common/libknotd_la-log.lo -MD -MP -MF knot/common/$(DEPDIR)/libknotd_la-log.Tpo -c -o knot/common/libknotd_la-log.lo `test -f 'knot/common/log.c' || echo '$(srcdir)/'`knot/common/log.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/common/$(DEPDIR)/libknotd_la-log.Tpo knot/common/$(DEPDIR)/libknotd_la-log.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/common/log.c' object='knot/common/libknotd_la-log.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/common/libknotd_la-log.lo `test -f 'knot/common/log.c' || echo '$(srcdir)/'`knot/common/log.c + +knot/common/libknotd_la-process.lo: knot/common/process.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/common/libknotd_la-process.lo -MD -MP -MF knot/common/$(DEPDIR)/libknotd_la-process.Tpo -c -o knot/common/libknotd_la-process.lo `test -f 'knot/common/process.c' || echo '$(srcdir)/'`knot/common/process.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/common/$(DEPDIR)/libknotd_la-process.Tpo knot/common/$(DEPDIR)/libknotd_la-process.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/common/process.c' object='knot/common/libknotd_la-process.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/common/libknotd_la-process.lo `test -f 'knot/common/process.c' || echo '$(srcdir)/'`knot/common/process.c + +knot/common/libknotd_la-ref.lo: knot/common/ref.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/common/libknotd_la-ref.lo -MD -MP -MF knot/common/$(DEPDIR)/libknotd_la-ref.Tpo -c -o knot/common/libknotd_la-ref.lo `test -f 'knot/common/ref.c' || echo '$(srcdir)/'`knot/common/ref.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/common/$(DEPDIR)/libknotd_la-ref.Tpo knot/common/$(DEPDIR)/libknotd_la-ref.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/common/ref.c' object='knot/common/libknotd_la-ref.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/common/libknotd_la-ref.lo `test -f 'knot/common/ref.c' || echo '$(srcdir)/'`knot/common/ref.c + +knot/common/libknotd_la-stats.lo: knot/common/stats.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/common/libknotd_la-stats.lo -MD -MP -MF knot/common/$(DEPDIR)/libknotd_la-stats.Tpo -c -o knot/common/libknotd_la-stats.lo `test -f 'knot/common/stats.c' || echo '$(srcdir)/'`knot/common/stats.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/common/$(DEPDIR)/libknotd_la-stats.Tpo knot/common/$(DEPDIR)/libknotd_la-stats.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/common/stats.c' object='knot/common/libknotd_la-stats.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/common/libknotd_la-stats.lo `test -f 'knot/common/stats.c' || echo '$(srcdir)/'`knot/common/stats.c + +knot/server/libknotd_la-dthreads.lo: knot/server/dthreads.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/server/libknotd_la-dthreads.lo -MD -MP -MF knot/server/$(DEPDIR)/libknotd_la-dthreads.Tpo -c -o knot/server/libknotd_la-dthreads.lo `test -f 'knot/server/dthreads.c' || echo '$(srcdir)/'`knot/server/dthreads.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/server/$(DEPDIR)/libknotd_la-dthreads.Tpo knot/server/$(DEPDIR)/libknotd_la-dthreads.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/server/dthreads.c' object='knot/server/libknotd_la-dthreads.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/server/libknotd_la-dthreads.lo `test -f 'knot/server/dthreads.c' || echo '$(srcdir)/'`knot/server/dthreads.c + +knot/journal/libknotd_la-chgset_ctx.lo: knot/journal/chgset_ctx.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/journal/libknotd_la-chgset_ctx.lo -MD -MP -MF knot/journal/$(DEPDIR)/libknotd_la-chgset_ctx.Tpo -c -o knot/journal/libknotd_la-chgset_ctx.lo `test -f 'knot/journal/chgset_ctx.c' || echo '$(srcdir)/'`knot/journal/chgset_ctx.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/journal/$(DEPDIR)/libknotd_la-chgset_ctx.Tpo knot/journal/$(DEPDIR)/libknotd_la-chgset_ctx.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/journal/chgset_ctx.c' object='knot/journal/libknotd_la-chgset_ctx.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/journal/libknotd_la-chgset_ctx.lo `test -f 'knot/journal/chgset_ctx.c' || echo '$(srcdir)/'`knot/journal/chgset_ctx.c + +knot/journal/libknotd_la-journal.lo: knot/journal/journal.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/journal/libknotd_la-journal.lo -MD -MP -MF knot/journal/$(DEPDIR)/libknotd_la-journal.Tpo -c -o knot/journal/libknotd_la-journal.lo `test -f 'knot/journal/journal.c' || echo '$(srcdir)/'`knot/journal/journal.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/journal/$(DEPDIR)/libknotd_la-journal.Tpo knot/journal/$(DEPDIR)/libknotd_la-journal.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/journal/journal.c' object='knot/journal/libknotd_la-journal.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/journal/libknotd_la-journal.lo `test -f 'knot/journal/journal.c' || echo '$(srcdir)/'`knot/journal/journal.c + +knot/journal/libknotd_la-serialization.lo: knot/journal/serialization.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/journal/libknotd_la-serialization.lo -MD -MP -MF knot/journal/$(DEPDIR)/libknotd_la-serialization.Tpo -c -o knot/journal/libknotd_la-serialization.lo `test -f 'knot/journal/serialization.c' || echo '$(srcdir)/'`knot/journal/serialization.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/journal/$(DEPDIR)/libknotd_la-serialization.Tpo knot/journal/$(DEPDIR)/libknotd_la-serialization.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/journal/serialization.c' object='knot/journal/libknotd_la-serialization.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/journal/libknotd_la-serialization.lo `test -f 'knot/journal/serialization.c' || echo '$(srcdir)/'`knot/journal/serialization.c + +knot/server/libknotd_la-server.lo: knot/server/server.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/server/libknotd_la-server.lo -MD -MP -MF knot/server/$(DEPDIR)/libknotd_la-server.Tpo -c -o knot/server/libknotd_la-server.lo `test -f 'knot/server/server.c' || echo '$(srcdir)/'`knot/server/server.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/server/$(DEPDIR)/libknotd_la-server.Tpo knot/server/$(DEPDIR)/libknotd_la-server.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/server/server.c' object='knot/server/libknotd_la-server.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/server/libknotd_la-server.lo `test -f 'knot/server/server.c' || echo '$(srcdir)/'`knot/server/server.c + +knot/server/libknotd_la-tcp-handler.lo: knot/server/tcp-handler.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/server/libknotd_la-tcp-handler.lo -MD -MP -MF knot/server/$(DEPDIR)/libknotd_la-tcp-handler.Tpo -c -o knot/server/libknotd_la-tcp-handler.lo `test -f 'knot/server/tcp-handler.c' || echo '$(srcdir)/'`knot/server/tcp-handler.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/server/$(DEPDIR)/libknotd_la-tcp-handler.Tpo knot/server/$(DEPDIR)/libknotd_la-tcp-handler.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/server/tcp-handler.c' object='knot/server/libknotd_la-tcp-handler.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/server/libknotd_la-tcp-handler.lo `test -f 'knot/server/tcp-handler.c' || echo '$(srcdir)/'`knot/server/tcp-handler.c + +knot/server/libknotd_la-udp-handler.lo: knot/server/udp-handler.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/server/libknotd_la-udp-handler.lo -MD -MP -MF knot/server/$(DEPDIR)/libknotd_la-udp-handler.Tpo -c -o knot/server/libknotd_la-udp-handler.lo `test -f 'knot/server/udp-handler.c' || echo '$(srcdir)/'`knot/server/udp-handler.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/server/$(DEPDIR)/libknotd_la-udp-handler.Tpo knot/server/$(DEPDIR)/libknotd_la-udp-handler.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/server/udp-handler.c' object='knot/server/libknotd_la-udp-handler.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/server/libknotd_la-udp-handler.lo `test -f 'knot/server/udp-handler.c' || echo '$(srcdir)/'`knot/server/udp-handler.c + +knot/updates/libknotd_la-acl.lo: knot/updates/acl.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/updates/libknotd_la-acl.lo -MD -MP -MF knot/updates/$(DEPDIR)/libknotd_la-acl.Tpo -c -o knot/updates/libknotd_la-acl.lo `test -f 'knot/updates/acl.c' || echo '$(srcdir)/'`knot/updates/acl.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/updates/$(DEPDIR)/libknotd_la-acl.Tpo knot/updates/$(DEPDIR)/libknotd_la-acl.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/updates/acl.c' object='knot/updates/libknotd_la-acl.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/updates/libknotd_la-acl.lo `test -f 'knot/updates/acl.c' || echo '$(srcdir)/'`knot/updates/acl.c + +knot/updates/libknotd_la-apply.lo: knot/updates/apply.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/updates/libknotd_la-apply.lo -MD -MP -MF knot/updates/$(DEPDIR)/libknotd_la-apply.Tpo -c -o knot/updates/libknotd_la-apply.lo `test -f 'knot/updates/apply.c' || echo '$(srcdir)/'`knot/updates/apply.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/updates/$(DEPDIR)/libknotd_la-apply.Tpo knot/updates/$(DEPDIR)/libknotd_la-apply.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/updates/apply.c' object='knot/updates/libknotd_la-apply.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/updates/libknotd_la-apply.lo `test -f 'knot/updates/apply.c' || echo '$(srcdir)/'`knot/updates/apply.c + +knot/updates/libknotd_la-changesets.lo: knot/updates/changesets.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/updates/libknotd_la-changesets.lo -MD -MP -MF knot/updates/$(DEPDIR)/libknotd_la-changesets.Tpo -c -o knot/updates/libknotd_la-changesets.lo `test -f 'knot/updates/changesets.c' || echo '$(srcdir)/'`knot/updates/changesets.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/updates/$(DEPDIR)/libknotd_la-changesets.Tpo knot/updates/$(DEPDIR)/libknotd_la-changesets.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/updates/changesets.c' object='knot/updates/libknotd_la-changesets.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/updates/libknotd_la-changesets.lo `test -f 'knot/updates/changesets.c' || echo '$(srcdir)/'`knot/updates/changesets.c + +knot/updates/libknotd_la-ddns.lo: knot/updates/ddns.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/updates/libknotd_la-ddns.lo -MD -MP -MF knot/updates/$(DEPDIR)/libknotd_la-ddns.Tpo -c -o knot/updates/libknotd_la-ddns.lo `test -f 'knot/updates/ddns.c' || echo '$(srcdir)/'`knot/updates/ddns.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/updates/$(DEPDIR)/libknotd_la-ddns.Tpo knot/updates/$(DEPDIR)/libknotd_la-ddns.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/updates/ddns.c' object='knot/updates/libknotd_la-ddns.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/updates/libknotd_la-ddns.lo `test -f 'knot/updates/ddns.c' || echo '$(srcdir)/'`knot/updates/ddns.c + +knot/updates/libknotd_la-zone-update.lo: knot/updates/zone-update.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/updates/libknotd_la-zone-update.lo -MD -MP -MF knot/updates/$(DEPDIR)/libknotd_la-zone-update.Tpo -c -o knot/updates/libknotd_la-zone-update.lo `test -f 'knot/updates/zone-update.c' || echo '$(srcdir)/'`knot/updates/zone-update.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/updates/$(DEPDIR)/libknotd_la-zone-update.Tpo knot/updates/$(DEPDIR)/libknotd_la-zone-update.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/updates/zone-update.c' object='knot/updates/libknotd_la-zone-update.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/updates/libknotd_la-zone-update.lo `test -f 'knot/updates/zone-update.c' || echo '$(srcdir)/'`knot/updates/zone-update.c + +knot/worker/libknotd_la-pool.lo: knot/worker/pool.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/worker/libknotd_la-pool.lo -MD -MP -MF knot/worker/$(DEPDIR)/libknotd_la-pool.Tpo -c -o knot/worker/libknotd_la-pool.lo `test -f 'knot/worker/pool.c' || echo '$(srcdir)/'`knot/worker/pool.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/worker/$(DEPDIR)/libknotd_la-pool.Tpo knot/worker/$(DEPDIR)/libknotd_la-pool.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/worker/pool.c' object='knot/worker/libknotd_la-pool.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/worker/libknotd_la-pool.lo `test -f 'knot/worker/pool.c' || echo '$(srcdir)/'`knot/worker/pool.c + +knot/worker/libknotd_la-queue.lo: knot/worker/queue.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/worker/libknotd_la-queue.lo -MD -MP -MF knot/worker/$(DEPDIR)/libknotd_la-queue.Tpo -c -o knot/worker/libknotd_la-queue.lo `test -f 'knot/worker/queue.c' || echo '$(srcdir)/'`knot/worker/queue.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/worker/$(DEPDIR)/libknotd_la-queue.Tpo knot/worker/$(DEPDIR)/libknotd_la-queue.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/worker/queue.c' object='knot/worker/libknotd_la-queue.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/worker/libknotd_la-queue.lo `test -f 'knot/worker/queue.c' || echo '$(srcdir)/'`knot/worker/queue.c + +knot/zone/libknotd_la-contents.lo: knot/zone/contents.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/zone/libknotd_la-contents.lo -MD -MP -MF knot/zone/$(DEPDIR)/libknotd_la-contents.Tpo -c -o knot/zone/libknotd_la-contents.lo `test -f 'knot/zone/contents.c' || echo '$(srcdir)/'`knot/zone/contents.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/zone/$(DEPDIR)/libknotd_la-contents.Tpo knot/zone/$(DEPDIR)/libknotd_la-contents.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/zone/contents.c' object='knot/zone/libknotd_la-contents.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/zone/libknotd_la-contents.lo `test -f 'knot/zone/contents.c' || echo '$(srcdir)/'`knot/zone/contents.c + +knot/zone/libknotd_la-node.lo: knot/zone/node.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/zone/libknotd_la-node.lo -MD -MP -MF knot/zone/$(DEPDIR)/libknotd_la-node.Tpo -c -o knot/zone/libknotd_la-node.lo `test -f 'knot/zone/node.c' || echo '$(srcdir)/'`knot/zone/node.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/zone/$(DEPDIR)/libknotd_la-node.Tpo knot/zone/$(DEPDIR)/libknotd_la-node.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/zone/node.c' object='knot/zone/libknotd_la-node.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/zone/libknotd_la-node.lo `test -f 'knot/zone/node.c' || echo '$(srcdir)/'`knot/zone/node.c + +knot/zone/libknotd_la-semantic-check.lo: knot/zone/semantic-check.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/zone/libknotd_la-semantic-check.lo -MD -MP -MF knot/zone/$(DEPDIR)/libknotd_la-semantic-check.Tpo -c -o knot/zone/libknotd_la-semantic-check.lo `test -f 'knot/zone/semantic-check.c' || echo '$(srcdir)/'`knot/zone/semantic-check.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/zone/$(DEPDIR)/libknotd_la-semantic-check.Tpo knot/zone/$(DEPDIR)/libknotd_la-semantic-check.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/zone/semantic-check.c' object='knot/zone/libknotd_la-semantic-check.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/zone/libknotd_la-semantic-check.lo `test -f 'knot/zone/semantic-check.c' || echo '$(srcdir)/'`knot/zone/semantic-check.c + +knot/zone/libknotd_la-serial.lo: knot/zone/serial.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/zone/libknotd_la-serial.lo -MD -MP -MF knot/zone/$(DEPDIR)/libknotd_la-serial.Tpo -c -o knot/zone/libknotd_la-serial.lo `test -f 'knot/zone/serial.c' || echo '$(srcdir)/'`knot/zone/serial.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/zone/$(DEPDIR)/libknotd_la-serial.Tpo knot/zone/$(DEPDIR)/libknotd_la-serial.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/zone/serial.c' object='knot/zone/libknotd_la-serial.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/zone/libknotd_la-serial.lo `test -f 'knot/zone/serial.c' || echo '$(srcdir)/'`knot/zone/serial.c + +knot/zone/libknotd_la-timers.lo: knot/zone/timers.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/zone/libknotd_la-timers.lo -MD -MP -MF knot/zone/$(DEPDIR)/libknotd_la-timers.Tpo -c -o knot/zone/libknotd_la-timers.lo `test -f 'knot/zone/timers.c' || echo '$(srcdir)/'`knot/zone/timers.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/zone/$(DEPDIR)/libknotd_la-timers.Tpo knot/zone/$(DEPDIR)/libknotd_la-timers.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/zone/timers.c' object='knot/zone/libknotd_la-timers.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/zone/libknotd_la-timers.lo `test -f 'knot/zone/timers.c' || echo '$(srcdir)/'`knot/zone/timers.c + +knot/zone/libknotd_la-zone-diff.lo: knot/zone/zone-diff.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/zone/libknotd_la-zone-diff.lo -MD -MP -MF knot/zone/$(DEPDIR)/libknotd_la-zone-diff.Tpo -c -o knot/zone/libknotd_la-zone-diff.lo `test -f 'knot/zone/zone-diff.c' || echo '$(srcdir)/'`knot/zone/zone-diff.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/zone/$(DEPDIR)/libknotd_la-zone-diff.Tpo knot/zone/$(DEPDIR)/libknotd_la-zone-diff.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/zone/zone-diff.c' object='knot/zone/libknotd_la-zone-diff.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/zone/libknotd_la-zone-diff.lo `test -f 'knot/zone/zone-diff.c' || echo '$(srcdir)/'`knot/zone/zone-diff.c + +knot/zone/libknotd_la-zone-dump.lo: knot/zone/zone-dump.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/zone/libknotd_la-zone-dump.lo -MD -MP -MF knot/zone/$(DEPDIR)/libknotd_la-zone-dump.Tpo -c -o knot/zone/libknotd_la-zone-dump.lo `test -f 'knot/zone/zone-dump.c' || echo '$(srcdir)/'`knot/zone/zone-dump.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/zone/$(DEPDIR)/libknotd_la-zone-dump.Tpo knot/zone/$(DEPDIR)/libknotd_la-zone-dump.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/zone/zone-dump.c' object='knot/zone/libknotd_la-zone-dump.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/zone/libknotd_la-zone-dump.lo `test -f 'knot/zone/zone-dump.c' || echo '$(srcdir)/'`knot/zone/zone-dump.c + +knot/zone/libknotd_la-zone-load.lo: knot/zone/zone-load.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/zone/libknotd_la-zone-load.lo -MD -MP -MF knot/zone/$(DEPDIR)/libknotd_la-zone-load.Tpo -c -o knot/zone/libknotd_la-zone-load.lo `test -f 'knot/zone/zone-load.c' || echo '$(srcdir)/'`knot/zone/zone-load.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/zone/$(DEPDIR)/libknotd_la-zone-load.Tpo knot/zone/$(DEPDIR)/libknotd_la-zone-load.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/zone/zone-load.c' object='knot/zone/libknotd_la-zone-load.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/zone/libknotd_la-zone-load.lo `test -f 'knot/zone/zone-load.c' || echo '$(srcdir)/'`knot/zone/zone-load.c + +knot/zone/libknotd_la-zone-tree.lo: knot/zone/zone-tree.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/zone/libknotd_la-zone-tree.lo -MD -MP -MF knot/zone/$(DEPDIR)/libknotd_la-zone-tree.Tpo -c -o knot/zone/libknotd_la-zone-tree.lo `test -f 'knot/zone/zone-tree.c' || echo '$(srcdir)/'`knot/zone/zone-tree.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/zone/$(DEPDIR)/libknotd_la-zone-tree.Tpo knot/zone/$(DEPDIR)/libknotd_la-zone-tree.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/zone/zone-tree.c' object='knot/zone/libknotd_la-zone-tree.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/zone/libknotd_la-zone-tree.lo `test -f 'knot/zone/zone-tree.c' || echo '$(srcdir)/'`knot/zone/zone-tree.c + +knot/zone/libknotd_la-zone.lo: knot/zone/zone.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/zone/libknotd_la-zone.lo -MD -MP -MF knot/zone/$(DEPDIR)/libknotd_la-zone.Tpo -c -o knot/zone/libknotd_la-zone.lo `test -f 'knot/zone/zone.c' || echo '$(srcdir)/'`knot/zone/zone.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/zone/$(DEPDIR)/libknotd_la-zone.Tpo knot/zone/$(DEPDIR)/libknotd_la-zone.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/zone/zone.c' object='knot/zone/libknotd_la-zone.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/zone/libknotd_la-zone.lo `test -f 'knot/zone/zone.c' || echo '$(srcdir)/'`knot/zone/zone.c + +knot/zone/libknotd_la-zonedb-load.lo: knot/zone/zonedb-load.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/zone/libknotd_la-zonedb-load.lo -MD -MP -MF knot/zone/$(DEPDIR)/libknotd_la-zonedb-load.Tpo -c -o knot/zone/libknotd_la-zonedb-load.lo `test -f 'knot/zone/zonedb-load.c' || echo '$(srcdir)/'`knot/zone/zonedb-load.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/zone/$(DEPDIR)/libknotd_la-zonedb-load.Tpo knot/zone/$(DEPDIR)/libknotd_la-zonedb-load.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/zone/zonedb-load.c' object='knot/zone/libknotd_la-zonedb-load.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/zone/libknotd_la-zonedb-load.lo `test -f 'knot/zone/zonedb-load.c' || echo '$(srcdir)/'`knot/zone/zonedb-load.c + +knot/zone/libknotd_la-zonedb.lo: knot/zone/zonedb.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/zone/libknotd_la-zonedb.lo -MD -MP -MF knot/zone/$(DEPDIR)/libknotd_la-zonedb.Tpo -c -o knot/zone/libknotd_la-zonedb.lo `test -f 'knot/zone/zonedb.c' || echo '$(srcdir)/'`knot/zone/zonedb.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/zone/$(DEPDIR)/libknotd_la-zonedb.Tpo knot/zone/$(DEPDIR)/libknotd_la-zonedb.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/zone/zonedb.c' object='knot/zone/libknotd_la-zonedb.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/zone/libknotd_la-zonedb.lo `test -f 'knot/zone/zonedb.c' || echo '$(srcdir)/'`knot/zone/zonedb.c + +knot/zone/libknotd_la-zonefile.lo: knot/zone/zonefile.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/zone/libknotd_la-zonefile.lo -MD -MP -MF knot/zone/$(DEPDIR)/libknotd_la-zonefile.Tpo -c -o knot/zone/libknotd_la-zonefile.lo `test -f 'knot/zone/zonefile.c' || echo '$(srcdir)/'`knot/zone/zonefile.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/zone/$(DEPDIR)/libknotd_la-zonefile.Tpo knot/zone/$(DEPDIR)/libknotd_la-zonefile.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/zone/zonefile.c' object='knot/zone/libknotd_la-zonefile.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/zone/libknotd_la-zonefile.lo `test -f 'knot/zone/zonefile.c' || echo '$(srcdir)/'`knot/zone/zonefile.c + +knot/modules/cookies/libknotd_la-cookies.lo: knot/modules/cookies/cookies.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/cookies/libknotd_la-cookies.lo -MD -MP -MF knot/modules/cookies/$(DEPDIR)/libknotd_la-cookies.Tpo -c -o knot/modules/cookies/libknotd_la-cookies.lo `test -f 'knot/modules/cookies/cookies.c' || echo '$(srcdir)/'`knot/modules/cookies/cookies.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/cookies/$(DEPDIR)/libknotd_la-cookies.Tpo knot/modules/cookies/$(DEPDIR)/libknotd_la-cookies.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/cookies/cookies.c' object='knot/modules/cookies/libknotd_la-cookies.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/cookies/libknotd_la-cookies.lo `test -f 'knot/modules/cookies/cookies.c' || echo '$(srcdir)/'`knot/modules/cookies/cookies.c + +knot/modules/dnsproxy/libknotd_la-dnsproxy.lo: knot/modules/dnsproxy/dnsproxy.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/dnsproxy/libknotd_la-dnsproxy.lo -MD -MP -MF knot/modules/dnsproxy/$(DEPDIR)/libknotd_la-dnsproxy.Tpo -c -o knot/modules/dnsproxy/libknotd_la-dnsproxy.lo `test -f 'knot/modules/dnsproxy/dnsproxy.c' || echo '$(srcdir)/'`knot/modules/dnsproxy/dnsproxy.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/dnsproxy/$(DEPDIR)/libknotd_la-dnsproxy.Tpo knot/modules/dnsproxy/$(DEPDIR)/libknotd_la-dnsproxy.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/dnsproxy/dnsproxy.c' object='knot/modules/dnsproxy/libknotd_la-dnsproxy.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/dnsproxy/libknotd_la-dnsproxy.lo `test -f 'knot/modules/dnsproxy/dnsproxy.c' || echo '$(srcdir)/'`knot/modules/dnsproxy/dnsproxy.c + +knot/modules/dnstap/libknotd_la-dnstap.lo: knot/modules/dnstap/dnstap.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/dnstap/libknotd_la-dnstap.lo -MD -MP -MF knot/modules/dnstap/$(DEPDIR)/libknotd_la-dnstap.Tpo -c -o knot/modules/dnstap/libknotd_la-dnstap.lo `test -f 'knot/modules/dnstap/dnstap.c' || echo '$(srcdir)/'`knot/modules/dnstap/dnstap.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/dnstap/$(DEPDIR)/libknotd_la-dnstap.Tpo knot/modules/dnstap/$(DEPDIR)/libknotd_la-dnstap.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/dnstap/dnstap.c' object='knot/modules/dnstap/libknotd_la-dnstap.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/dnstap/libknotd_la-dnstap.lo `test -f 'knot/modules/dnstap/dnstap.c' || echo '$(srcdir)/'`knot/modules/dnstap/dnstap.c + +knot/modules/geoip/libknotd_la-geoip.lo: knot/modules/geoip/geoip.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/geoip/libknotd_la-geoip.lo -MD -MP -MF knot/modules/geoip/$(DEPDIR)/libknotd_la-geoip.Tpo -c -o knot/modules/geoip/libknotd_la-geoip.lo `test -f 'knot/modules/geoip/geoip.c' || echo '$(srcdir)/'`knot/modules/geoip/geoip.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/geoip/$(DEPDIR)/libknotd_la-geoip.Tpo knot/modules/geoip/$(DEPDIR)/libknotd_la-geoip.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/geoip/geoip.c' object='knot/modules/geoip/libknotd_la-geoip.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/geoip/libknotd_la-geoip.lo `test -f 'knot/modules/geoip/geoip.c' || echo '$(srcdir)/'`knot/modules/geoip/geoip.c + +knot/modules/geoip/libknotd_la-geodb.lo: knot/modules/geoip/geodb.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/geoip/libknotd_la-geodb.lo -MD -MP -MF knot/modules/geoip/$(DEPDIR)/libknotd_la-geodb.Tpo -c -o knot/modules/geoip/libknotd_la-geodb.lo `test -f 'knot/modules/geoip/geodb.c' || echo '$(srcdir)/'`knot/modules/geoip/geodb.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/geoip/$(DEPDIR)/libknotd_la-geodb.Tpo knot/modules/geoip/$(DEPDIR)/libknotd_la-geodb.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/geoip/geodb.c' object='knot/modules/geoip/libknotd_la-geodb.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/geoip/libknotd_la-geodb.lo `test -f 'knot/modules/geoip/geodb.c' || echo '$(srcdir)/'`knot/modules/geoip/geodb.c + +knot/modules/noudp/libknotd_la-noudp.lo: knot/modules/noudp/noudp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/noudp/libknotd_la-noudp.lo -MD -MP -MF knot/modules/noudp/$(DEPDIR)/libknotd_la-noudp.Tpo -c -o knot/modules/noudp/libknotd_la-noudp.lo `test -f 'knot/modules/noudp/noudp.c' || echo '$(srcdir)/'`knot/modules/noudp/noudp.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/noudp/$(DEPDIR)/libknotd_la-noudp.Tpo knot/modules/noudp/$(DEPDIR)/libknotd_la-noudp.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/noudp/noudp.c' object='knot/modules/noudp/libknotd_la-noudp.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/noudp/libknotd_la-noudp.lo `test -f 'knot/modules/noudp/noudp.c' || echo '$(srcdir)/'`knot/modules/noudp/noudp.c + +knot/modules/onlinesign/libknotd_la-onlinesign.lo: knot/modules/onlinesign/onlinesign.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/onlinesign/libknotd_la-onlinesign.lo -MD -MP -MF knot/modules/onlinesign/$(DEPDIR)/libknotd_la-onlinesign.Tpo -c -o knot/modules/onlinesign/libknotd_la-onlinesign.lo `test -f 'knot/modules/onlinesign/onlinesign.c' || echo '$(srcdir)/'`knot/modules/onlinesign/onlinesign.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/onlinesign/$(DEPDIR)/libknotd_la-onlinesign.Tpo knot/modules/onlinesign/$(DEPDIR)/libknotd_la-onlinesign.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/onlinesign/onlinesign.c' object='knot/modules/onlinesign/libknotd_la-onlinesign.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/onlinesign/libknotd_la-onlinesign.lo `test -f 'knot/modules/onlinesign/onlinesign.c' || echo '$(srcdir)/'`knot/modules/onlinesign/onlinesign.c + +knot/modules/onlinesign/libknotd_la-nsec_next.lo: knot/modules/onlinesign/nsec_next.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/onlinesign/libknotd_la-nsec_next.lo -MD -MP -MF knot/modules/onlinesign/$(DEPDIR)/libknotd_la-nsec_next.Tpo -c -o knot/modules/onlinesign/libknotd_la-nsec_next.lo `test -f 'knot/modules/onlinesign/nsec_next.c' || echo '$(srcdir)/'`knot/modules/onlinesign/nsec_next.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/onlinesign/$(DEPDIR)/libknotd_la-nsec_next.Tpo knot/modules/onlinesign/$(DEPDIR)/libknotd_la-nsec_next.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/onlinesign/nsec_next.c' object='knot/modules/onlinesign/libknotd_la-nsec_next.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/onlinesign/libknotd_la-nsec_next.lo `test -f 'knot/modules/onlinesign/nsec_next.c' || echo '$(srcdir)/'`knot/modules/onlinesign/nsec_next.c + +knot/modules/queryacl/libknotd_la-queryacl.lo: knot/modules/queryacl/queryacl.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/queryacl/libknotd_la-queryacl.lo -MD -MP -MF knot/modules/queryacl/$(DEPDIR)/libknotd_la-queryacl.Tpo -c -o knot/modules/queryacl/libknotd_la-queryacl.lo `test -f 'knot/modules/queryacl/queryacl.c' || echo '$(srcdir)/'`knot/modules/queryacl/queryacl.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/queryacl/$(DEPDIR)/libknotd_la-queryacl.Tpo knot/modules/queryacl/$(DEPDIR)/libknotd_la-queryacl.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/queryacl/queryacl.c' object='knot/modules/queryacl/libknotd_la-queryacl.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/queryacl/libknotd_la-queryacl.lo `test -f 'knot/modules/queryacl/queryacl.c' || echo '$(srcdir)/'`knot/modules/queryacl/queryacl.c + +knot/modules/rrl/libknotd_la-rrl.lo: knot/modules/rrl/rrl.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/rrl/libknotd_la-rrl.lo -MD -MP -MF knot/modules/rrl/$(DEPDIR)/libknotd_la-rrl.Tpo -c -o knot/modules/rrl/libknotd_la-rrl.lo `test -f 'knot/modules/rrl/rrl.c' || echo '$(srcdir)/'`knot/modules/rrl/rrl.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/rrl/$(DEPDIR)/libknotd_la-rrl.Tpo knot/modules/rrl/$(DEPDIR)/libknotd_la-rrl.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/rrl/rrl.c' object='knot/modules/rrl/libknotd_la-rrl.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/rrl/libknotd_la-rrl.lo `test -f 'knot/modules/rrl/rrl.c' || echo '$(srcdir)/'`knot/modules/rrl/rrl.c + +knot/modules/rrl/libknotd_la-functions.lo: knot/modules/rrl/functions.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/rrl/libknotd_la-functions.lo -MD -MP -MF knot/modules/rrl/$(DEPDIR)/libknotd_la-functions.Tpo -c -o knot/modules/rrl/libknotd_la-functions.lo `test -f 'knot/modules/rrl/functions.c' || echo '$(srcdir)/'`knot/modules/rrl/functions.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/rrl/$(DEPDIR)/libknotd_la-functions.Tpo knot/modules/rrl/$(DEPDIR)/libknotd_la-functions.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/rrl/functions.c' object='knot/modules/rrl/libknotd_la-functions.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/rrl/libknotd_la-functions.lo `test -f 'knot/modules/rrl/functions.c' || echo '$(srcdir)/'`knot/modules/rrl/functions.c + +knot/modules/stats/libknotd_la-stats.lo: knot/modules/stats/stats.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/stats/libknotd_la-stats.lo -MD -MP -MF knot/modules/stats/$(DEPDIR)/libknotd_la-stats.Tpo -c -o knot/modules/stats/libknotd_la-stats.lo `test -f 'knot/modules/stats/stats.c' || echo '$(srcdir)/'`knot/modules/stats/stats.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/stats/$(DEPDIR)/libknotd_la-stats.Tpo knot/modules/stats/$(DEPDIR)/libknotd_la-stats.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/stats/stats.c' object='knot/modules/stats/libknotd_la-stats.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/stats/libknotd_la-stats.lo `test -f 'knot/modules/stats/stats.c' || echo '$(srcdir)/'`knot/modules/stats/stats.c + +knot/modules/synthrecord/libknotd_la-synthrecord.lo: knot/modules/synthrecord/synthrecord.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/synthrecord/libknotd_la-synthrecord.lo -MD -MP -MF knot/modules/synthrecord/$(DEPDIR)/libknotd_la-synthrecord.Tpo -c -o knot/modules/synthrecord/libknotd_la-synthrecord.lo `test -f 'knot/modules/synthrecord/synthrecord.c' || echo '$(srcdir)/'`knot/modules/synthrecord/synthrecord.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/synthrecord/$(DEPDIR)/libknotd_la-synthrecord.Tpo knot/modules/synthrecord/$(DEPDIR)/libknotd_la-synthrecord.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/synthrecord/synthrecord.c' object='knot/modules/synthrecord/libknotd_la-synthrecord.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/synthrecord/libknotd_la-synthrecord.lo `test -f 'knot/modules/synthrecord/synthrecord.c' || echo '$(srcdir)/'`knot/modules/synthrecord/synthrecord.c + +knot/modules/whoami/libknotd_la-whoami.lo: knot/modules/whoami/whoami.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT knot/modules/whoami/libknotd_la-whoami.lo -MD -MP -MF knot/modules/whoami/$(DEPDIR)/libknotd_la-whoami.Tpo -c -o knot/modules/whoami/libknotd_la-whoami.lo `test -f 'knot/modules/whoami/whoami.c' || echo '$(srcdir)/'`knot/modules/whoami/whoami.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) knot/modules/whoami/$(DEPDIR)/libknotd_la-whoami.Tpo knot/modules/whoami/$(DEPDIR)/libknotd_la-whoami.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='knot/modules/whoami/whoami.c' object='knot/modules/whoami/libknotd_la-whoami.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotd_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o knot/modules/whoami/libknotd_la-whoami.lo `test -f 'knot/modules/whoami/whoami.c' || echo '$(srcdir)/'`knot/modules/whoami/whoami.c + +utils/common/libknotus_la-cert.lo: utils/common/cert.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotus_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/common/libknotus_la-cert.lo -MD -MP -MF utils/common/$(DEPDIR)/libknotus_la-cert.Tpo -c -o utils/common/libknotus_la-cert.lo `test -f 'utils/common/cert.c' || echo '$(srcdir)/'`utils/common/cert.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/common/$(DEPDIR)/libknotus_la-cert.Tpo utils/common/$(DEPDIR)/libknotus_la-cert.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/common/cert.c' object='utils/common/libknotus_la-cert.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotus_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/common/libknotus_la-cert.lo `test -f 'utils/common/cert.c' || echo '$(srcdir)/'`utils/common/cert.c + +utils/common/libknotus_la-exec.lo: utils/common/exec.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotus_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/common/libknotus_la-exec.lo -MD -MP -MF utils/common/$(DEPDIR)/libknotus_la-exec.Tpo -c -o utils/common/libknotus_la-exec.lo `test -f 'utils/common/exec.c' || echo '$(srcdir)/'`utils/common/exec.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/common/$(DEPDIR)/libknotus_la-exec.Tpo utils/common/$(DEPDIR)/libknotus_la-exec.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/common/exec.c' object='utils/common/libknotus_la-exec.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotus_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/common/libknotus_la-exec.lo `test -f 'utils/common/exec.c' || echo '$(srcdir)/'`utils/common/exec.c + +utils/common/libknotus_la-hex.lo: utils/common/hex.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotus_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/common/libknotus_la-hex.lo -MD -MP -MF utils/common/$(DEPDIR)/libknotus_la-hex.Tpo -c -o utils/common/libknotus_la-hex.lo `test -f 'utils/common/hex.c' || echo '$(srcdir)/'`utils/common/hex.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/common/$(DEPDIR)/libknotus_la-hex.Tpo utils/common/$(DEPDIR)/libknotus_la-hex.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/common/hex.c' object='utils/common/libknotus_la-hex.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotus_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/common/libknotus_la-hex.lo `test -f 'utils/common/hex.c' || echo '$(srcdir)/'`utils/common/hex.c + +utils/common/libknotus_la-lookup.lo: utils/common/lookup.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotus_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/common/libknotus_la-lookup.lo -MD -MP -MF utils/common/$(DEPDIR)/libknotus_la-lookup.Tpo -c -o utils/common/libknotus_la-lookup.lo `test -f 'utils/common/lookup.c' || echo '$(srcdir)/'`utils/common/lookup.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/common/$(DEPDIR)/libknotus_la-lookup.Tpo utils/common/$(DEPDIR)/libknotus_la-lookup.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/common/lookup.c' object='utils/common/libknotus_la-lookup.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotus_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/common/libknotus_la-lookup.lo `test -f 'utils/common/lookup.c' || echo '$(srcdir)/'`utils/common/lookup.c + +utils/common/libknotus_la-msg.lo: utils/common/msg.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotus_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/common/libknotus_la-msg.lo -MD -MP -MF utils/common/$(DEPDIR)/libknotus_la-msg.Tpo -c -o utils/common/libknotus_la-msg.lo `test -f 'utils/common/msg.c' || echo '$(srcdir)/'`utils/common/msg.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/common/$(DEPDIR)/libknotus_la-msg.Tpo utils/common/$(DEPDIR)/libknotus_la-msg.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/common/msg.c' object='utils/common/libknotus_la-msg.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotus_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/common/libknotus_la-msg.lo `test -f 'utils/common/msg.c' || echo '$(srcdir)/'`utils/common/msg.c + +utils/common/libknotus_la-netio.lo: utils/common/netio.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotus_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/common/libknotus_la-netio.lo -MD -MP -MF utils/common/$(DEPDIR)/libknotus_la-netio.Tpo -c -o utils/common/libknotus_la-netio.lo `test -f 'utils/common/netio.c' || echo '$(srcdir)/'`utils/common/netio.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/common/$(DEPDIR)/libknotus_la-netio.Tpo utils/common/$(DEPDIR)/libknotus_la-netio.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/common/netio.c' object='utils/common/libknotus_la-netio.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotus_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/common/libknotus_la-netio.lo `test -f 'utils/common/netio.c' || echo '$(srcdir)/'`utils/common/netio.c + +utils/common/libknotus_la-params.lo: utils/common/params.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotus_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/common/libknotus_la-params.lo -MD -MP -MF utils/common/$(DEPDIR)/libknotus_la-params.Tpo -c -o utils/common/libknotus_la-params.lo `test -f 'utils/common/params.c' || echo '$(srcdir)/'`utils/common/params.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/common/$(DEPDIR)/libknotus_la-params.Tpo utils/common/$(DEPDIR)/libknotus_la-params.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/common/params.c' object='utils/common/libknotus_la-params.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotus_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/common/libknotus_la-params.lo `test -f 'utils/common/params.c' || echo '$(srcdir)/'`utils/common/params.c + +utils/common/libknotus_la-resolv.lo: utils/common/resolv.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotus_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/common/libknotus_la-resolv.lo -MD -MP -MF utils/common/$(DEPDIR)/libknotus_la-resolv.Tpo -c -o utils/common/libknotus_la-resolv.lo `test -f 'utils/common/resolv.c' || echo '$(srcdir)/'`utils/common/resolv.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/common/$(DEPDIR)/libknotus_la-resolv.Tpo utils/common/$(DEPDIR)/libknotus_la-resolv.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/common/resolv.c' object='utils/common/libknotus_la-resolv.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotus_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/common/libknotus_la-resolv.lo `test -f 'utils/common/resolv.c' || echo '$(srcdir)/'`utils/common/resolv.c + +utils/common/libknotus_la-sign.lo: utils/common/sign.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotus_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/common/libknotus_la-sign.lo -MD -MP -MF utils/common/$(DEPDIR)/libknotus_la-sign.Tpo -c -o utils/common/libknotus_la-sign.lo `test -f 'utils/common/sign.c' || echo '$(srcdir)/'`utils/common/sign.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/common/$(DEPDIR)/libknotus_la-sign.Tpo utils/common/$(DEPDIR)/libknotus_la-sign.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/common/sign.c' object='utils/common/libknotus_la-sign.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotus_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/common/libknotus_la-sign.lo `test -f 'utils/common/sign.c' || echo '$(srcdir)/'`utils/common/sign.c + +utils/common/libknotus_la-tls.lo: utils/common/tls.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotus_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/common/libknotus_la-tls.lo -MD -MP -MF utils/common/$(DEPDIR)/libknotus_la-tls.Tpo -c -o utils/common/libknotus_la-tls.lo `test -f 'utils/common/tls.c' || echo '$(srcdir)/'`utils/common/tls.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/common/$(DEPDIR)/libknotus_la-tls.Tpo utils/common/$(DEPDIR)/libknotus_la-tls.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/common/tls.c' object='utils/common/libknotus_la-tls.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotus_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/common/libknotus_la-tls.lo `test -f 'utils/common/tls.c' || echo '$(srcdir)/'`utils/common/tls.c + +utils/common/libknotus_la-token.lo: utils/common/token.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotus_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/common/libknotus_la-token.lo -MD -MP -MF utils/common/$(DEPDIR)/libknotus_la-token.Tpo -c -o utils/common/libknotus_la-token.lo `test -f 'utils/common/token.c' || echo '$(srcdir)/'`utils/common/token.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/common/$(DEPDIR)/libknotus_la-token.Tpo utils/common/$(DEPDIR)/libknotus_la-token.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/common/token.c' object='utils/common/libknotus_la-token.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libknotus_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/common/libknotus_la-token.lo `test -f 'utils/common/token.c' || echo '$(srcdir)/'`utils/common/token.c + +libdnssec/shared/libshared_la-bignum.lo: libdnssec/shared/bignum.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshared_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/shared/libshared_la-bignum.lo -MD -MP -MF libdnssec/shared/$(DEPDIR)/libshared_la-bignum.Tpo -c -o libdnssec/shared/libshared_la-bignum.lo `test -f 'libdnssec/shared/bignum.c' || echo '$(srcdir)/'`libdnssec/shared/bignum.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/shared/$(DEPDIR)/libshared_la-bignum.Tpo libdnssec/shared/$(DEPDIR)/libshared_la-bignum.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/shared/bignum.c' object='libdnssec/shared/libshared_la-bignum.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshared_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/shared/libshared_la-bignum.lo `test -f 'libdnssec/shared/bignum.c' || echo '$(srcdir)/'`libdnssec/shared/bignum.c + +libdnssec/shared/libshared_la-dname.lo: libdnssec/shared/dname.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshared_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/shared/libshared_la-dname.lo -MD -MP -MF libdnssec/shared/$(DEPDIR)/libshared_la-dname.Tpo -c -o libdnssec/shared/libshared_la-dname.lo `test -f 'libdnssec/shared/dname.c' || echo '$(srcdir)/'`libdnssec/shared/dname.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/shared/$(DEPDIR)/libshared_la-dname.Tpo libdnssec/shared/$(DEPDIR)/libshared_la-dname.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/shared/dname.c' object='libdnssec/shared/libshared_la-dname.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshared_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/shared/libshared_la-dname.lo `test -f 'libdnssec/shared/dname.c' || echo '$(srcdir)/'`libdnssec/shared/dname.c + +libdnssec/shared/libshared_la-fs.lo: libdnssec/shared/fs.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshared_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/shared/libshared_la-fs.lo -MD -MP -MF libdnssec/shared/$(DEPDIR)/libshared_la-fs.Tpo -c -o libdnssec/shared/libshared_la-fs.lo `test -f 'libdnssec/shared/fs.c' || echo '$(srcdir)/'`libdnssec/shared/fs.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/shared/$(DEPDIR)/libshared_la-fs.Tpo libdnssec/shared/$(DEPDIR)/libshared_la-fs.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/shared/fs.c' object='libdnssec/shared/libshared_la-fs.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshared_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/shared/libshared_la-fs.lo `test -f 'libdnssec/shared/fs.c' || echo '$(srcdir)/'`libdnssec/shared/fs.c + +libdnssec/shared/libshared_la-hex.lo: libdnssec/shared/hex.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshared_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/shared/libshared_la-hex.lo -MD -MP -MF libdnssec/shared/$(DEPDIR)/libshared_la-hex.Tpo -c -o libdnssec/shared/libshared_la-hex.lo `test -f 'libdnssec/shared/hex.c' || echo '$(srcdir)/'`libdnssec/shared/hex.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/shared/$(DEPDIR)/libshared_la-hex.Tpo libdnssec/shared/$(DEPDIR)/libshared_la-hex.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/shared/hex.c' object='libdnssec/shared/libshared_la-hex.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshared_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/shared/libshared_la-hex.lo `test -f 'libdnssec/shared/hex.c' || echo '$(srcdir)/'`libdnssec/shared/hex.c + +libdnssec/shared/libshared_la-keyid_gnutls.lo: libdnssec/shared/keyid_gnutls.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshared_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/shared/libshared_la-keyid_gnutls.lo -MD -MP -MF libdnssec/shared/$(DEPDIR)/libshared_la-keyid_gnutls.Tpo -c -o libdnssec/shared/libshared_la-keyid_gnutls.lo `test -f 'libdnssec/shared/keyid_gnutls.c' || echo '$(srcdir)/'`libdnssec/shared/keyid_gnutls.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/shared/$(DEPDIR)/libshared_la-keyid_gnutls.Tpo libdnssec/shared/$(DEPDIR)/libshared_la-keyid_gnutls.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/shared/keyid_gnutls.c' object='libdnssec/shared/libshared_la-keyid_gnutls.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshared_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/shared/libshared_la-keyid_gnutls.lo `test -f 'libdnssec/shared/keyid_gnutls.c' || echo '$(srcdir)/'`libdnssec/shared/keyid_gnutls.c + +libdnssec/shared/libshared_la-pem.lo: libdnssec/shared/pem.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshared_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libdnssec/shared/libshared_la-pem.lo -MD -MP -MF libdnssec/shared/$(DEPDIR)/libshared_la-pem.Tpo -c -o libdnssec/shared/libshared_la-pem.lo `test -f 'libdnssec/shared/pem.c' || echo '$(srcdir)/'`libdnssec/shared/pem.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libdnssec/shared/$(DEPDIR)/libshared_la-pem.Tpo libdnssec/shared/$(DEPDIR)/libshared_la-pem.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libdnssec/shared/pem.c' object='libdnssec/shared/libshared_la-pem.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libshared_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libdnssec/shared/libshared_la-pem.lo `test -f 'libdnssec/shared/pem.c' || echo '$(srcdir)/'`libdnssec/shared/pem.c + +libzscanner/libzscanner_la-error.lo: libzscanner/error.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libzscanner_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libzscanner/libzscanner_la-error.lo -MD -MP -MF libzscanner/$(DEPDIR)/libzscanner_la-error.Tpo -c -o libzscanner/libzscanner_la-error.lo `test -f 'libzscanner/error.c' || echo '$(srcdir)/'`libzscanner/error.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libzscanner/$(DEPDIR)/libzscanner_la-error.Tpo libzscanner/$(DEPDIR)/libzscanner_la-error.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libzscanner/error.c' object='libzscanner/libzscanner_la-error.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libzscanner_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libzscanner/libzscanner_la-error.lo `test -f 'libzscanner/error.c' || echo '$(srcdir)/'`libzscanner/error.c + +libzscanner/libzscanner_la-functions.lo: libzscanner/functions.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libzscanner_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libzscanner/libzscanner_la-functions.lo -MD -MP -MF libzscanner/$(DEPDIR)/libzscanner_la-functions.Tpo -c -o libzscanner/libzscanner_la-functions.lo `test -f 'libzscanner/functions.c' || echo '$(srcdir)/'`libzscanner/functions.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libzscanner/$(DEPDIR)/libzscanner_la-functions.Tpo libzscanner/$(DEPDIR)/libzscanner_la-functions.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libzscanner/functions.c' object='libzscanner/libzscanner_la-functions.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libzscanner_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libzscanner/libzscanner_la-functions.lo `test -f 'libzscanner/functions.c' || echo '$(srcdir)/'`libzscanner/functions.c + +libzscanner/libzscanner_la-scanner.lo: libzscanner/scanner.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libzscanner_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libzscanner/libzscanner_la-scanner.lo -MD -MP -MF libzscanner/$(DEPDIR)/libzscanner_la-scanner.Tpo -c -o libzscanner/libzscanner_la-scanner.lo `test -f 'libzscanner/scanner.c' || echo '$(srcdir)/'`libzscanner/scanner.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libzscanner/$(DEPDIR)/libzscanner_la-scanner.Tpo libzscanner/$(DEPDIR)/libzscanner_la-scanner.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libzscanner/scanner.c' object='libzscanner/libzscanner_la-scanner.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libzscanner_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libzscanner/libzscanner_la-scanner.lo `test -f 'libzscanner/scanner.c' || echo '$(srcdir)/'`libzscanner/scanner.c + +utils/kdig/kdig-kdig_exec.o: utils/kdig/kdig_exec.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kdig_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/kdig/kdig-kdig_exec.o -MD -MP -MF utils/kdig/$(DEPDIR)/kdig-kdig_exec.Tpo -c -o utils/kdig/kdig-kdig_exec.o `test -f 'utils/kdig/kdig_exec.c' || echo '$(srcdir)/'`utils/kdig/kdig_exec.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/kdig/$(DEPDIR)/kdig-kdig_exec.Tpo utils/kdig/$(DEPDIR)/kdig-kdig_exec.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/kdig/kdig_exec.c' object='utils/kdig/kdig-kdig_exec.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kdig_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/kdig/kdig-kdig_exec.o `test -f 'utils/kdig/kdig_exec.c' || echo '$(srcdir)/'`utils/kdig/kdig_exec.c + +utils/kdig/kdig-kdig_exec.obj: utils/kdig/kdig_exec.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kdig_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/kdig/kdig-kdig_exec.obj -MD -MP -MF utils/kdig/$(DEPDIR)/kdig-kdig_exec.Tpo -c -o utils/kdig/kdig-kdig_exec.obj `if test -f 'utils/kdig/kdig_exec.c'; then $(CYGPATH_W) 'utils/kdig/kdig_exec.c'; else $(CYGPATH_W) '$(srcdir)/utils/kdig/kdig_exec.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/kdig/$(DEPDIR)/kdig-kdig_exec.Tpo utils/kdig/$(DEPDIR)/kdig-kdig_exec.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/kdig/kdig_exec.c' object='utils/kdig/kdig-kdig_exec.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kdig_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/kdig/kdig-kdig_exec.obj `if test -f 'utils/kdig/kdig_exec.c'; then $(CYGPATH_W) 'utils/kdig/kdig_exec.c'; else $(CYGPATH_W) '$(srcdir)/utils/kdig/kdig_exec.c'; fi` + +utils/kdig/kdig-kdig_main.o: utils/kdig/kdig_main.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kdig_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/kdig/kdig-kdig_main.o -MD -MP -MF utils/kdig/$(DEPDIR)/kdig-kdig_main.Tpo -c -o utils/kdig/kdig-kdig_main.o `test -f 'utils/kdig/kdig_main.c' || echo '$(srcdir)/'`utils/kdig/kdig_main.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/kdig/$(DEPDIR)/kdig-kdig_main.Tpo utils/kdig/$(DEPDIR)/kdig-kdig_main.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/kdig/kdig_main.c' object='utils/kdig/kdig-kdig_main.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kdig_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/kdig/kdig-kdig_main.o `test -f 'utils/kdig/kdig_main.c' || echo '$(srcdir)/'`utils/kdig/kdig_main.c + +utils/kdig/kdig-kdig_main.obj: utils/kdig/kdig_main.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kdig_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/kdig/kdig-kdig_main.obj -MD -MP -MF utils/kdig/$(DEPDIR)/kdig-kdig_main.Tpo -c -o utils/kdig/kdig-kdig_main.obj `if test -f 'utils/kdig/kdig_main.c'; then $(CYGPATH_W) 'utils/kdig/kdig_main.c'; else $(CYGPATH_W) '$(srcdir)/utils/kdig/kdig_main.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/kdig/$(DEPDIR)/kdig-kdig_main.Tpo utils/kdig/$(DEPDIR)/kdig-kdig_main.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/kdig/kdig_main.c' object='utils/kdig/kdig-kdig_main.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kdig_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/kdig/kdig-kdig_main.obj `if test -f 'utils/kdig/kdig_main.c'; then $(CYGPATH_W) 'utils/kdig/kdig_main.c'; else $(CYGPATH_W) '$(srcdir)/utils/kdig/kdig_main.c'; fi` + +utils/kdig/kdig-kdig_params.o: utils/kdig/kdig_params.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kdig_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/kdig/kdig-kdig_params.o -MD -MP -MF utils/kdig/$(DEPDIR)/kdig-kdig_params.Tpo -c -o utils/kdig/kdig-kdig_params.o `test -f 'utils/kdig/kdig_params.c' || echo '$(srcdir)/'`utils/kdig/kdig_params.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/kdig/$(DEPDIR)/kdig-kdig_params.Tpo utils/kdig/$(DEPDIR)/kdig-kdig_params.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/kdig/kdig_params.c' object='utils/kdig/kdig-kdig_params.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kdig_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/kdig/kdig-kdig_params.o `test -f 'utils/kdig/kdig_params.c' || echo '$(srcdir)/'`utils/kdig/kdig_params.c + +utils/kdig/kdig-kdig_params.obj: utils/kdig/kdig_params.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kdig_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/kdig/kdig-kdig_params.obj -MD -MP -MF utils/kdig/$(DEPDIR)/kdig-kdig_params.Tpo -c -o utils/kdig/kdig-kdig_params.obj `if test -f 'utils/kdig/kdig_params.c'; then $(CYGPATH_W) 'utils/kdig/kdig_params.c'; else $(CYGPATH_W) '$(srcdir)/utils/kdig/kdig_params.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/kdig/$(DEPDIR)/kdig-kdig_params.Tpo utils/kdig/$(DEPDIR)/kdig-kdig_params.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/kdig/kdig_params.c' object='utils/kdig/kdig-kdig_params.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kdig_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/kdig/kdig-kdig_params.obj `if test -f 'utils/kdig/kdig_params.c'; then $(CYGPATH_W) 'utils/kdig/kdig_params.c'; else $(CYGPATH_W) '$(srcdir)/utils/kdig/kdig_params.c'; fi` + +utils/keymgr/keymgr-bind_privkey.o: utils/keymgr/bind_privkey.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(keymgr_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/keymgr/keymgr-bind_privkey.o -MD -MP -MF utils/keymgr/$(DEPDIR)/keymgr-bind_privkey.Tpo -c -o utils/keymgr/keymgr-bind_privkey.o `test -f 'utils/keymgr/bind_privkey.c' || echo '$(srcdir)/'`utils/keymgr/bind_privkey.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/keymgr/$(DEPDIR)/keymgr-bind_privkey.Tpo utils/keymgr/$(DEPDIR)/keymgr-bind_privkey.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/keymgr/bind_privkey.c' object='utils/keymgr/keymgr-bind_privkey.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(keymgr_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/keymgr/keymgr-bind_privkey.o `test -f 'utils/keymgr/bind_privkey.c' || echo '$(srcdir)/'`utils/keymgr/bind_privkey.c + +utils/keymgr/keymgr-bind_privkey.obj: utils/keymgr/bind_privkey.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(keymgr_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/keymgr/keymgr-bind_privkey.obj -MD -MP -MF utils/keymgr/$(DEPDIR)/keymgr-bind_privkey.Tpo -c -o utils/keymgr/keymgr-bind_privkey.obj `if test -f 'utils/keymgr/bind_privkey.c'; then $(CYGPATH_W) 'utils/keymgr/bind_privkey.c'; else $(CYGPATH_W) '$(srcdir)/utils/keymgr/bind_privkey.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/keymgr/$(DEPDIR)/keymgr-bind_privkey.Tpo utils/keymgr/$(DEPDIR)/keymgr-bind_privkey.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/keymgr/bind_privkey.c' object='utils/keymgr/keymgr-bind_privkey.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(keymgr_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/keymgr/keymgr-bind_privkey.obj `if test -f 'utils/keymgr/bind_privkey.c'; then $(CYGPATH_W) 'utils/keymgr/bind_privkey.c'; else $(CYGPATH_W) '$(srcdir)/utils/keymgr/bind_privkey.c'; fi` + +utils/keymgr/keymgr-functions.o: utils/keymgr/functions.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(keymgr_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/keymgr/keymgr-functions.o -MD -MP -MF utils/keymgr/$(DEPDIR)/keymgr-functions.Tpo -c -o utils/keymgr/keymgr-functions.o `test -f 'utils/keymgr/functions.c' || echo '$(srcdir)/'`utils/keymgr/functions.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/keymgr/$(DEPDIR)/keymgr-functions.Tpo utils/keymgr/$(DEPDIR)/keymgr-functions.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/keymgr/functions.c' object='utils/keymgr/keymgr-functions.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(keymgr_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/keymgr/keymgr-functions.o `test -f 'utils/keymgr/functions.c' || echo '$(srcdir)/'`utils/keymgr/functions.c + +utils/keymgr/keymgr-functions.obj: utils/keymgr/functions.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(keymgr_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/keymgr/keymgr-functions.obj -MD -MP -MF utils/keymgr/$(DEPDIR)/keymgr-functions.Tpo -c -o utils/keymgr/keymgr-functions.obj `if test -f 'utils/keymgr/functions.c'; then $(CYGPATH_W) 'utils/keymgr/functions.c'; else $(CYGPATH_W) '$(srcdir)/utils/keymgr/functions.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/keymgr/$(DEPDIR)/keymgr-functions.Tpo utils/keymgr/$(DEPDIR)/keymgr-functions.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/keymgr/functions.c' object='utils/keymgr/keymgr-functions.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(keymgr_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/keymgr/keymgr-functions.obj `if test -f 'utils/keymgr/functions.c'; then $(CYGPATH_W) 'utils/keymgr/functions.c'; else $(CYGPATH_W) '$(srcdir)/utils/keymgr/functions.c'; fi` + +utils/keymgr/keymgr-main.o: utils/keymgr/main.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(keymgr_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/keymgr/keymgr-main.o -MD -MP -MF utils/keymgr/$(DEPDIR)/keymgr-main.Tpo -c -o utils/keymgr/keymgr-main.o `test -f 'utils/keymgr/main.c' || echo '$(srcdir)/'`utils/keymgr/main.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/keymgr/$(DEPDIR)/keymgr-main.Tpo utils/keymgr/$(DEPDIR)/keymgr-main.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/keymgr/main.c' object='utils/keymgr/keymgr-main.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(keymgr_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/keymgr/keymgr-main.o `test -f 'utils/keymgr/main.c' || echo '$(srcdir)/'`utils/keymgr/main.c + +utils/keymgr/keymgr-main.obj: utils/keymgr/main.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(keymgr_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/keymgr/keymgr-main.obj -MD -MP -MF utils/keymgr/$(DEPDIR)/keymgr-main.Tpo -c -o utils/keymgr/keymgr-main.obj `if test -f 'utils/keymgr/main.c'; then $(CYGPATH_W) 'utils/keymgr/main.c'; else $(CYGPATH_W) '$(srcdir)/utils/keymgr/main.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/keymgr/$(DEPDIR)/keymgr-main.Tpo utils/keymgr/$(DEPDIR)/keymgr-main.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/keymgr/main.c' object='utils/keymgr/keymgr-main.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(keymgr_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/keymgr/keymgr-main.obj `if test -f 'utils/keymgr/main.c'; then $(CYGPATH_W) 'utils/keymgr/main.c'; else $(CYGPATH_W) '$(srcdir)/utils/keymgr/main.c'; fi` + +utils/kdig/khost-kdig_exec.o: utils/kdig/kdig_exec.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(khost_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/kdig/khost-kdig_exec.o -MD -MP -MF utils/kdig/$(DEPDIR)/khost-kdig_exec.Tpo -c -o utils/kdig/khost-kdig_exec.o `test -f 'utils/kdig/kdig_exec.c' || echo '$(srcdir)/'`utils/kdig/kdig_exec.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/kdig/$(DEPDIR)/khost-kdig_exec.Tpo utils/kdig/$(DEPDIR)/khost-kdig_exec.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/kdig/kdig_exec.c' object='utils/kdig/khost-kdig_exec.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(khost_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/kdig/khost-kdig_exec.o `test -f 'utils/kdig/kdig_exec.c' || echo '$(srcdir)/'`utils/kdig/kdig_exec.c + +utils/kdig/khost-kdig_exec.obj: utils/kdig/kdig_exec.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(khost_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/kdig/khost-kdig_exec.obj -MD -MP -MF utils/kdig/$(DEPDIR)/khost-kdig_exec.Tpo -c -o utils/kdig/khost-kdig_exec.obj `if test -f 'utils/kdig/kdig_exec.c'; then $(CYGPATH_W) 'utils/kdig/kdig_exec.c'; else $(CYGPATH_W) '$(srcdir)/utils/kdig/kdig_exec.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/kdig/$(DEPDIR)/khost-kdig_exec.Tpo utils/kdig/$(DEPDIR)/khost-kdig_exec.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/kdig/kdig_exec.c' object='utils/kdig/khost-kdig_exec.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(khost_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/kdig/khost-kdig_exec.obj `if test -f 'utils/kdig/kdig_exec.c'; then $(CYGPATH_W) 'utils/kdig/kdig_exec.c'; else $(CYGPATH_W) '$(srcdir)/utils/kdig/kdig_exec.c'; fi` + +utils/kdig/khost-kdig_params.o: utils/kdig/kdig_params.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(khost_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/kdig/khost-kdig_params.o -MD -MP -MF utils/kdig/$(DEPDIR)/khost-kdig_params.Tpo -c -o utils/kdig/khost-kdig_params.o `test -f 'utils/kdig/kdig_params.c' || echo '$(srcdir)/'`utils/kdig/kdig_params.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/kdig/$(DEPDIR)/khost-kdig_params.Tpo utils/kdig/$(DEPDIR)/khost-kdig_params.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/kdig/kdig_params.c' object='utils/kdig/khost-kdig_params.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(khost_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/kdig/khost-kdig_params.o `test -f 'utils/kdig/kdig_params.c' || echo '$(srcdir)/'`utils/kdig/kdig_params.c + +utils/kdig/khost-kdig_params.obj: utils/kdig/kdig_params.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(khost_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/kdig/khost-kdig_params.obj -MD -MP -MF utils/kdig/$(DEPDIR)/khost-kdig_params.Tpo -c -o utils/kdig/khost-kdig_params.obj `if test -f 'utils/kdig/kdig_params.c'; then $(CYGPATH_W) 'utils/kdig/kdig_params.c'; else $(CYGPATH_W) '$(srcdir)/utils/kdig/kdig_params.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/kdig/$(DEPDIR)/khost-kdig_params.Tpo utils/kdig/$(DEPDIR)/khost-kdig_params.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/kdig/kdig_params.c' object='utils/kdig/khost-kdig_params.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(khost_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/kdig/khost-kdig_params.obj `if test -f 'utils/kdig/kdig_params.c'; then $(CYGPATH_W) 'utils/kdig/kdig_params.c'; else $(CYGPATH_W) '$(srcdir)/utils/kdig/kdig_params.c'; fi` + +utils/khost/khost-khost_main.o: utils/khost/khost_main.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(khost_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/khost/khost-khost_main.o -MD -MP -MF utils/khost/$(DEPDIR)/khost-khost_main.Tpo -c -o utils/khost/khost-khost_main.o `test -f 'utils/khost/khost_main.c' || echo '$(srcdir)/'`utils/khost/khost_main.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/khost/$(DEPDIR)/khost-khost_main.Tpo utils/khost/$(DEPDIR)/khost-khost_main.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/khost/khost_main.c' object='utils/khost/khost-khost_main.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(khost_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/khost/khost-khost_main.o `test -f 'utils/khost/khost_main.c' || echo '$(srcdir)/'`utils/khost/khost_main.c + +utils/khost/khost-khost_main.obj: utils/khost/khost_main.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(khost_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/khost/khost-khost_main.obj -MD -MP -MF utils/khost/$(DEPDIR)/khost-khost_main.Tpo -c -o utils/khost/khost-khost_main.obj `if test -f 'utils/khost/khost_main.c'; then $(CYGPATH_W) 'utils/khost/khost_main.c'; else $(CYGPATH_W) '$(srcdir)/utils/khost/khost_main.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/khost/$(DEPDIR)/khost-khost_main.Tpo utils/khost/$(DEPDIR)/khost-khost_main.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/khost/khost_main.c' object='utils/khost/khost-khost_main.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(khost_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/khost/khost-khost_main.obj `if test -f 'utils/khost/khost_main.c'; then $(CYGPATH_W) 'utils/khost/khost_main.c'; else $(CYGPATH_W) '$(srcdir)/utils/khost/khost_main.c'; fi` + +utils/khost/khost-khost_params.o: utils/khost/khost_params.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(khost_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/khost/khost-khost_params.o -MD -MP -MF utils/khost/$(DEPDIR)/khost-khost_params.Tpo -c -o utils/khost/khost-khost_params.o `test -f 'utils/khost/khost_params.c' || echo '$(srcdir)/'`utils/khost/khost_params.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/khost/$(DEPDIR)/khost-khost_params.Tpo utils/khost/$(DEPDIR)/khost-khost_params.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/khost/khost_params.c' object='utils/khost/khost-khost_params.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(khost_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/khost/khost-khost_params.o `test -f 'utils/khost/khost_params.c' || echo '$(srcdir)/'`utils/khost/khost_params.c + +utils/khost/khost-khost_params.obj: utils/khost/khost_params.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(khost_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/khost/khost-khost_params.obj -MD -MP -MF utils/khost/$(DEPDIR)/khost-khost_params.Tpo -c -o utils/khost/khost-khost_params.obj `if test -f 'utils/khost/khost_params.c'; then $(CYGPATH_W) 'utils/khost/khost_params.c'; else $(CYGPATH_W) '$(srcdir)/utils/khost/khost_params.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/khost/$(DEPDIR)/khost-khost_params.Tpo utils/khost/$(DEPDIR)/khost-khost_params.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/khost/khost_params.c' object='utils/khost/khost-khost_params.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(khost_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/khost/khost-khost_params.obj `if test -f 'utils/khost/khost_params.c'; then $(CYGPATH_W) 'utils/khost/khost_params.c'; else $(CYGPATH_W) '$(srcdir)/utils/khost/khost_params.c'; fi` + +utils/kjournalprint/kjournalprint-main.o: utils/kjournalprint/main.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kjournalprint_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/kjournalprint/kjournalprint-main.o -MD -MP -MF utils/kjournalprint/$(DEPDIR)/kjournalprint-main.Tpo -c -o utils/kjournalprint/kjournalprint-main.o `test -f 'utils/kjournalprint/main.c' || echo '$(srcdir)/'`utils/kjournalprint/main.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/kjournalprint/$(DEPDIR)/kjournalprint-main.Tpo utils/kjournalprint/$(DEPDIR)/kjournalprint-main.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/kjournalprint/main.c' object='utils/kjournalprint/kjournalprint-main.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kjournalprint_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/kjournalprint/kjournalprint-main.o `test -f 'utils/kjournalprint/main.c' || echo '$(srcdir)/'`utils/kjournalprint/main.c + +utils/kjournalprint/kjournalprint-main.obj: utils/kjournalprint/main.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kjournalprint_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/kjournalprint/kjournalprint-main.obj -MD -MP -MF utils/kjournalprint/$(DEPDIR)/kjournalprint-main.Tpo -c -o utils/kjournalprint/kjournalprint-main.obj `if test -f 'utils/kjournalprint/main.c'; then $(CYGPATH_W) 'utils/kjournalprint/main.c'; else $(CYGPATH_W) '$(srcdir)/utils/kjournalprint/main.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/kjournalprint/$(DEPDIR)/kjournalprint-main.Tpo utils/kjournalprint/$(DEPDIR)/kjournalprint-main.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/kjournalprint/main.c' object='utils/kjournalprint/kjournalprint-main.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kjournalprint_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/kjournalprint/kjournalprint-main.obj `if test -f 'utils/kjournalprint/main.c'; then $(CYGPATH_W) 'utils/kjournalprint/main.c'; else $(CYGPATH_W) '$(srcdir)/utils/kjournalprint/main.c'; fi` + +utils/knotc/knotc-commands.o: utils/knotc/commands.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotc_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/knotc/knotc-commands.o -MD -MP -MF utils/knotc/$(DEPDIR)/knotc-commands.Tpo -c -o utils/knotc/knotc-commands.o `test -f 'utils/knotc/commands.c' || echo '$(srcdir)/'`utils/knotc/commands.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/knotc/$(DEPDIR)/knotc-commands.Tpo utils/knotc/$(DEPDIR)/knotc-commands.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/knotc/commands.c' object='utils/knotc/knotc-commands.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotc_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/knotc/knotc-commands.o `test -f 'utils/knotc/commands.c' || echo '$(srcdir)/'`utils/knotc/commands.c + +utils/knotc/knotc-commands.obj: utils/knotc/commands.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotc_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/knotc/knotc-commands.obj -MD -MP -MF utils/knotc/$(DEPDIR)/knotc-commands.Tpo -c -o utils/knotc/knotc-commands.obj `if test -f 'utils/knotc/commands.c'; then $(CYGPATH_W) 'utils/knotc/commands.c'; else $(CYGPATH_W) '$(srcdir)/utils/knotc/commands.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/knotc/$(DEPDIR)/knotc-commands.Tpo utils/knotc/$(DEPDIR)/knotc-commands.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/knotc/commands.c' object='utils/knotc/knotc-commands.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotc_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/knotc/knotc-commands.obj `if test -f 'utils/knotc/commands.c'; then $(CYGPATH_W) 'utils/knotc/commands.c'; else $(CYGPATH_W) '$(srcdir)/utils/knotc/commands.c'; fi` + +utils/knotc/knotc-estimator.o: utils/knotc/estimator.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotc_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/knotc/knotc-estimator.o -MD -MP -MF utils/knotc/$(DEPDIR)/knotc-estimator.Tpo -c -o utils/knotc/knotc-estimator.o `test -f 'utils/knotc/estimator.c' || echo '$(srcdir)/'`utils/knotc/estimator.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/knotc/$(DEPDIR)/knotc-estimator.Tpo utils/knotc/$(DEPDIR)/knotc-estimator.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/knotc/estimator.c' object='utils/knotc/knotc-estimator.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotc_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/knotc/knotc-estimator.o `test -f 'utils/knotc/estimator.c' || echo '$(srcdir)/'`utils/knotc/estimator.c + +utils/knotc/knotc-estimator.obj: utils/knotc/estimator.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotc_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/knotc/knotc-estimator.obj -MD -MP -MF utils/knotc/$(DEPDIR)/knotc-estimator.Tpo -c -o utils/knotc/knotc-estimator.obj `if test -f 'utils/knotc/estimator.c'; then $(CYGPATH_W) 'utils/knotc/estimator.c'; else $(CYGPATH_W) '$(srcdir)/utils/knotc/estimator.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/knotc/$(DEPDIR)/knotc-estimator.Tpo utils/knotc/$(DEPDIR)/knotc-estimator.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/knotc/estimator.c' object='utils/knotc/knotc-estimator.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotc_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/knotc/knotc-estimator.obj `if test -f 'utils/knotc/estimator.c'; then $(CYGPATH_W) 'utils/knotc/estimator.c'; else $(CYGPATH_W) '$(srcdir)/utils/knotc/estimator.c'; fi` + +utils/knotc/knotc-interactive.o: utils/knotc/interactive.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotc_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/knotc/knotc-interactive.o -MD -MP -MF utils/knotc/$(DEPDIR)/knotc-interactive.Tpo -c -o utils/knotc/knotc-interactive.o `test -f 'utils/knotc/interactive.c' || echo '$(srcdir)/'`utils/knotc/interactive.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/knotc/$(DEPDIR)/knotc-interactive.Tpo utils/knotc/$(DEPDIR)/knotc-interactive.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/knotc/interactive.c' object='utils/knotc/knotc-interactive.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotc_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/knotc/knotc-interactive.o `test -f 'utils/knotc/interactive.c' || echo '$(srcdir)/'`utils/knotc/interactive.c + +utils/knotc/knotc-interactive.obj: utils/knotc/interactive.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotc_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/knotc/knotc-interactive.obj -MD -MP -MF utils/knotc/$(DEPDIR)/knotc-interactive.Tpo -c -o utils/knotc/knotc-interactive.obj `if test -f 'utils/knotc/interactive.c'; then $(CYGPATH_W) 'utils/knotc/interactive.c'; else $(CYGPATH_W) '$(srcdir)/utils/knotc/interactive.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/knotc/$(DEPDIR)/knotc-interactive.Tpo utils/knotc/$(DEPDIR)/knotc-interactive.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/knotc/interactive.c' object='utils/knotc/knotc-interactive.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotc_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/knotc/knotc-interactive.obj `if test -f 'utils/knotc/interactive.c'; then $(CYGPATH_W) 'utils/knotc/interactive.c'; else $(CYGPATH_W) '$(srcdir)/utils/knotc/interactive.c'; fi` + +utils/knotc/knotc-process.o: utils/knotc/process.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotc_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/knotc/knotc-process.o -MD -MP -MF utils/knotc/$(DEPDIR)/knotc-process.Tpo -c -o utils/knotc/knotc-process.o `test -f 'utils/knotc/process.c' || echo '$(srcdir)/'`utils/knotc/process.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/knotc/$(DEPDIR)/knotc-process.Tpo utils/knotc/$(DEPDIR)/knotc-process.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/knotc/process.c' object='utils/knotc/knotc-process.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotc_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/knotc/knotc-process.o `test -f 'utils/knotc/process.c' || echo '$(srcdir)/'`utils/knotc/process.c + +utils/knotc/knotc-process.obj: utils/knotc/process.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotc_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/knotc/knotc-process.obj -MD -MP -MF utils/knotc/$(DEPDIR)/knotc-process.Tpo -c -o utils/knotc/knotc-process.obj `if test -f 'utils/knotc/process.c'; then $(CYGPATH_W) 'utils/knotc/process.c'; else $(CYGPATH_W) '$(srcdir)/utils/knotc/process.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/knotc/$(DEPDIR)/knotc-process.Tpo utils/knotc/$(DEPDIR)/knotc-process.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/knotc/process.c' object='utils/knotc/knotc-process.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotc_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/knotc/knotc-process.obj `if test -f 'utils/knotc/process.c'; then $(CYGPATH_W) 'utils/knotc/process.c'; else $(CYGPATH_W) '$(srcdir)/utils/knotc/process.c'; fi` + +utils/knotc/knotc-main.o: utils/knotc/main.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotc_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/knotc/knotc-main.o -MD -MP -MF utils/knotc/$(DEPDIR)/knotc-main.Tpo -c -o utils/knotc/knotc-main.o `test -f 'utils/knotc/main.c' || echo '$(srcdir)/'`utils/knotc/main.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/knotc/$(DEPDIR)/knotc-main.Tpo utils/knotc/$(DEPDIR)/knotc-main.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/knotc/main.c' object='utils/knotc/knotc-main.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotc_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/knotc/knotc-main.o `test -f 'utils/knotc/main.c' || echo '$(srcdir)/'`utils/knotc/main.c + +utils/knotc/knotc-main.obj: utils/knotc/main.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotc_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/knotc/knotc-main.obj -MD -MP -MF utils/knotc/$(DEPDIR)/knotc-main.Tpo -c -o utils/knotc/knotc-main.obj `if test -f 'utils/knotc/main.c'; then $(CYGPATH_W) 'utils/knotc/main.c'; else $(CYGPATH_W) '$(srcdir)/utils/knotc/main.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/knotc/$(DEPDIR)/knotc-main.Tpo utils/knotc/$(DEPDIR)/knotc-main.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/knotc/main.c' object='utils/knotc/knotc-main.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotc_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/knotc/knotc-main.obj `if test -f 'utils/knotc/main.c'; then $(CYGPATH_W) 'utils/knotc/main.c'; else $(CYGPATH_W) '$(srcdir)/utils/knotc/main.c'; fi` + +utils/knotd/knotd-main.o: utils/knotd/main.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/knotd/knotd-main.o -MD -MP -MF utils/knotd/$(DEPDIR)/knotd-main.Tpo -c -o utils/knotd/knotd-main.o `test -f 'utils/knotd/main.c' || echo '$(srcdir)/'`utils/knotd/main.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/knotd/$(DEPDIR)/knotd-main.Tpo utils/knotd/$(DEPDIR)/knotd-main.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/knotd/main.c' object='utils/knotd/knotd-main.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/knotd/knotd-main.o `test -f 'utils/knotd/main.c' || echo '$(srcdir)/'`utils/knotd/main.c + +utils/knotd/knotd-main.obj: utils/knotd/main.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/knotd/knotd-main.obj -MD -MP -MF utils/knotd/$(DEPDIR)/knotd-main.Tpo -c -o utils/knotd/knotd-main.obj `if test -f 'utils/knotd/main.c'; then $(CYGPATH_W) 'utils/knotd/main.c'; else $(CYGPATH_W) '$(srcdir)/utils/knotd/main.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/knotd/$(DEPDIR)/knotd-main.Tpo utils/knotd/$(DEPDIR)/knotd-main.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/knotd/main.c' object='utils/knotd/knotd-main.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knotd_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/knotd/knotd-main.obj `if test -f 'utils/knotd/main.c'; then $(CYGPATH_W) 'utils/knotd/main.c'; else $(CYGPATH_W) '$(srcdir)/utils/knotd/main.c'; fi` + +utils/knsec3hash/knsec3hash-knsec3hash.o: utils/knsec3hash/knsec3hash.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knsec3hash_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/knsec3hash/knsec3hash-knsec3hash.o -MD -MP -MF utils/knsec3hash/$(DEPDIR)/knsec3hash-knsec3hash.Tpo -c -o utils/knsec3hash/knsec3hash-knsec3hash.o `test -f 'utils/knsec3hash/knsec3hash.c' || echo '$(srcdir)/'`utils/knsec3hash/knsec3hash.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/knsec3hash/$(DEPDIR)/knsec3hash-knsec3hash.Tpo utils/knsec3hash/$(DEPDIR)/knsec3hash-knsec3hash.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/knsec3hash/knsec3hash.c' object='utils/knsec3hash/knsec3hash-knsec3hash.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knsec3hash_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/knsec3hash/knsec3hash-knsec3hash.o `test -f 'utils/knsec3hash/knsec3hash.c' || echo '$(srcdir)/'`utils/knsec3hash/knsec3hash.c + +utils/knsec3hash/knsec3hash-knsec3hash.obj: utils/knsec3hash/knsec3hash.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knsec3hash_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/knsec3hash/knsec3hash-knsec3hash.obj -MD -MP -MF utils/knsec3hash/$(DEPDIR)/knsec3hash-knsec3hash.Tpo -c -o utils/knsec3hash/knsec3hash-knsec3hash.obj `if test -f 'utils/knsec3hash/knsec3hash.c'; then $(CYGPATH_W) 'utils/knsec3hash/knsec3hash.c'; else $(CYGPATH_W) '$(srcdir)/utils/knsec3hash/knsec3hash.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/knsec3hash/$(DEPDIR)/knsec3hash-knsec3hash.Tpo utils/knsec3hash/$(DEPDIR)/knsec3hash-knsec3hash.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/knsec3hash/knsec3hash.c' object='utils/knsec3hash/knsec3hash-knsec3hash.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knsec3hash_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/knsec3hash/knsec3hash-knsec3hash.obj `if test -f 'utils/knsec3hash/knsec3hash.c'; then $(CYGPATH_W) 'utils/knsec3hash/knsec3hash.c'; else $(CYGPATH_W) '$(srcdir)/utils/knsec3hash/knsec3hash.c'; fi` + +utils/knsupdate/knsupdate-knsupdate_exec.o: utils/knsupdate/knsupdate_exec.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knsupdate_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/knsupdate/knsupdate-knsupdate_exec.o -MD -MP -MF utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_exec.Tpo -c -o utils/knsupdate/knsupdate-knsupdate_exec.o `test -f 'utils/knsupdate/knsupdate_exec.c' || echo '$(srcdir)/'`utils/knsupdate/knsupdate_exec.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_exec.Tpo utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_exec.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/knsupdate/knsupdate_exec.c' object='utils/knsupdate/knsupdate-knsupdate_exec.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knsupdate_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/knsupdate/knsupdate-knsupdate_exec.o `test -f 'utils/knsupdate/knsupdate_exec.c' || echo '$(srcdir)/'`utils/knsupdate/knsupdate_exec.c + +utils/knsupdate/knsupdate-knsupdate_exec.obj: utils/knsupdate/knsupdate_exec.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knsupdate_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/knsupdate/knsupdate-knsupdate_exec.obj -MD -MP -MF utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_exec.Tpo -c -o utils/knsupdate/knsupdate-knsupdate_exec.obj `if test -f 'utils/knsupdate/knsupdate_exec.c'; then $(CYGPATH_W) 'utils/knsupdate/knsupdate_exec.c'; else $(CYGPATH_W) '$(srcdir)/utils/knsupdate/knsupdate_exec.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_exec.Tpo utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_exec.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/knsupdate/knsupdate_exec.c' object='utils/knsupdate/knsupdate-knsupdate_exec.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knsupdate_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/knsupdate/knsupdate-knsupdate_exec.obj `if test -f 'utils/knsupdate/knsupdate_exec.c'; then $(CYGPATH_W) 'utils/knsupdate/knsupdate_exec.c'; else $(CYGPATH_W) '$(srcdir)/utils/knsupdate/knsupdate_exec.c'; fi` + +utils/knsupdate/knsupdate-knsupdate_main.o: utils/knsupdate/knsupdate_main.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knsupdate_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/knsupdate/knsupdate-knsupdate_main.o -MD -MP -MF utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_main.Tpo -c -o utils/knsupdate/knsupdate-knsupdate_main.o `test -f 'utils/knsupdate/knsupdate_main.c' || echo '$(srcdir)/'`utils/knsupdate/knsupdate_main.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_main.Tpo utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_main.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/knsupdate/knsupdate_main.c' object='utils/knsupdate/knsupdate-knsupdate_main.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knsupdate_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/knsupdate/knsupdate-knsupdate_main.o `test -f 'utils/knsupdate/knsupdate_main.c' || echo '$(srcdir)/'`utils/knsupdate/knsupdate_main.c + +utils/knsupdate/knsupdate-knsupdate_main.obj: utils/knsupdate/knsupdate_main.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knsupdate_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/knsupdate/knsupdate-knsupdate_main.obj -MD -MP -MF utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_main.Tpo -c -o utils/knsupdate/knsupdate-knsupdate_main.obj `if test -f 'utils/knsupdate/knsupdate_main.c'; then $(CYGPATH_W) 'utils/knsupdate/knsupdate_main.c'; else $(CYGPATH_W) '$(srcdir)/utils/knsupdate/knsupdate_main.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_main.Tpo utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_main.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/knsupdate/knsupdate_main.c' object='utils/knsupdate/knsupdate-knsupdate_main.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knsupdate_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/knsupdate/knsupdate-knsupdate_main.obj `if test -f 'utils/knsupdate/knsupdate_main.c'; then $(CYGPATH_W) 'utils/knsupdate/knsupdate_main.c'; else $(CYGPATH_W) '$(srcdir)/utils/knsupdate/knsupdate_main.c'; fi` + +utils/knsupdate/knsupdate-knsupdate_params.o: utils/knsupdate/knsupdate_params.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knsupdate_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/knsupdate/knsupdate-knsupdate_params.o -MD -MP -MF utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_params.Tpo -c -o utils/knsupdate/knsupdate-knsupdate_params.o `test -f 'utils/knsupdate/knsupdate_params.c' || echo '$(srcdir)/'`utils/knsupdate/knsupdate_params.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_params.Tpo utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_params.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/knsupdate/knsupdate_params.c' object='utils/knsupdate/knsupdate-knsupdate_params.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knsupdate_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/knsupdate/knsupdate-knsupdate_params.o `test -f 'utils/knsupdate/knsupdate_params.c' || echo '$(srcdir)/'`utils/knsupdate/knsupdate_params.c + +utils/knsupdate/knsupdate-knsupdate_params.obj: utils/knsupdate/knsupdate_params.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knsupdate_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/knsupdate/knsupdate-knsupdate_params.obj -MD -MP -MF utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_params.Tpo -c -o utils/knsupdate/knsupdate-knsupdate_params.obj `if test -f 'utils/knsupdate/knsupdate_params.c'; then $(CYGPATH_W) 'utils/knsupdate/knsupdate_params.c'; else $(CYGPATH_W) '$(srcdir)/utils/knsupdate/knsupdate_params.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_params.Tpo utils/knsupdate/$(DEPDIR)/knsupdate-knsupdate_params.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/knsupdate/knsupdate_params.c' object='utils/knsupdate/knsupdate-knsupdate_params.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(knsupdate_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/knsupdate/knsupdate-knsupdate_params.obj `if test -f 'utils/knsupdate/knsupdate_params.c'; then $(CYGPATH_W) 'utils/knsupdate/knsupdate_params.c'; else $(CYGPATH_W) '$(srcdir)/utils/knsupdate/knsupdate_params.c'; fi` + +utils/kzonecheck/kzonecheck-main.o: utils/kzonecheck/main.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kzonecheck_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/kzonecheck/kzonecheck-main.o -MD -MP -MF utils/kzonecheck/$(DEPDIR)/kzonecheck-main.Tpo -c -o utils/kzonecheck/kzonecheck-main.o `test -f 'utils/kzonecheck/main.c' || echo '$(srcdir)/'`utils/kzonecheck/main.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/kzonecheck/$(DEPDIR)/kzonecheck-main.Tpo utils/kzonecheck/$(DEPDIR)/kzonecheck-main.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/kzonecheck/main.c' object='utils/kzonecheck/kzonecheck-main.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kzonecheck_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/kzonecheck/kzonecheck-main.o `test -f 'utils/kzonecheck/main.c' || echo '$(srcdir)/'`utils/kzonecheck/main.c + +utils/kzonecheck/kzonecheck-main.obj: utils/kzonecheck/main.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kzonecheck_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/kzonecheck/kzonecheck-main.obj -MD -MP -MF utils/kzonecheck/$(DEPDIR)/kzonecheck-main.Tpo -c -o utils/kzonecheck/kzonecheck-main.obj `if test -f 'utils/kzonecheck/main.c'; then $(CYGPATH_W) 'utils/kzonecheck/main.c'; else $(CYGPATH_W) '$(srcdir)/utils/kzonecheck/main.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/kzonecheck/$(DEPDIR)/kzonecheck-main.Tpo utils/kzonecheck/$(DEPDIR)/kzonecheck-main.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/kzonecheck/main.c' object='utils/kzonecheck/kzonecheck-main.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kzonecheck_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/kzonecheck/kzonecheck-main.obj `if test -f 'utils/kzonecheck/main.c'; then $(CYGPATH_W) 'utils/kzonecheck/main.c'; else $(CYGPATH_W) '$(srcdir)/utils/kzonecheck/main.c'; fi` + +utils/kzonecheck/kzonecheck-zone_check.o: utils/kzonecheck/zone_check.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kzonecheck_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/kzonecheck/kzonecheck-zone_check.o -MD -MP -MF utils/kzonecheck/$(DEPDIR)/kzonecheck-zone_check.Tpo -c -o utils/kzonecheck/kzonecheck-zone_check.o `test -f 'utils/kzonecheck/zone_check.c' || echo '$(srcdir)/'`utils/kzonecheck/zone_check.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/kzonecheck/$(DEPDIR)/kzonecheck-zone_check.Tpo utils/kzonecheck/$(DEPDIR)/kzonecheck-zone_check.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/kzonecheck/zone_check.c' object='utils/kzonecheck/kzonecheck-zone_check.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kzonecheck_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/kzonecheck/kzonecheck-zone_check.o `test -f 'utils/kzonecheck/zone_check.c' || echo '$(srcdir)/'`utils/kzonecheck/zone_check.c + +utils/kzonecheck/kzonecheck-zone_check.obj: utils/kzonecheck/zone_check.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kzonecheck_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT utils/kzonecheck/kzonecheck-zone_check.obj -MD -MP -MF utils/kzonecheck/$(DEPDIR)/kzonecheck-zone_check.Tpo -c -o utils/kzonecheck/kzonecheck-zone_check.obj `if test -f 'utils/kzonecheck/zone_check.c'; then $(CYGPATH_W) 'utils/kzonecheck/zone_check.c'; else $(CYGPATH_W) '$(srcdir)/utils/kzonecheck/zone_check.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) utils/kzonecheck/$(DEPDIR)/kzonecheck-zone_check.Tpo utils/kzonecheck/$(DEPDIR)/kzonecheck-zone_check.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='utils/kzonecheck/zone_check.c' object='utils/kzonecheck/kzonecheck-zone_check.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(kzonecheck_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o utils/kzonecheck/kzonecheck-zone_check.obj `if test -f 'utils/kzonecheck/zone_check.c'; then $(CYGPATH_W) 'utils/kzonecheck/zone_check.c'; else $(CYGPATH_W) '$(srcdir)/utils/kzonecheck/zone_check.c'; fi` + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + -rm -rf contrib/.libs contrib/_libs + -rm -rf contrib/dnstap/.libs contrib/dnstap/_libs + -rm -rf contrib/lmdb/.libs contrib/lmdb/_libs + -rm -rf contrib/openbsd/.libs contrib/openbsd/_libs + -rm -rf contrib/qp-trie/.libs contrib/qp-trie/_libs + -rm -rf contrib/ucw/.libs contrib/ucw/_libs + -rm -rf knot/common/.libs knot/common/_libs + -rm -rf knot/conf/.libs knot/conf/_libs + -rm -rf knot/ctl/.libs knot/ctl/_libs + -rm -rf knot/dnssec/.libs knot/dnssec/_libs + -rm -rf knot/dnssec/kasp/.libs knot/dnssec/kasp/_libs + -rm -rf knot/events/.libs knot/events/_libs + -rm -rf knot/events/handlers/.libs knot/events/handlers/_libs + -rm -rf knot/journal/.libs knot/journal/_libs + -rm -rf knot/modules/.libs knot/modules/_libs + -rm -rf knot/modules/cookies/.libs knot/modules/cookies/_libs + -rm -rf knot/modules/dnsproxy/.libs knot/modules/dnsproxy/_libs + -rm -rf knot/modules/dnstap/.libs knot/modules/dnstap/_libs + -rm -rf knot/modules/geoip/.libs knot/modules/geoip/_libs + -rm -rf knot/modules/noudp/.libs knot/modules/noudp/_libs + -rm -rf knot/modules/onlinesign/.libs knot/modules/onlinesign/_libs + -rm -rf knot/modules/queryacl/.libs knot/modules/queryacl/_libs + -rm -rf knot/modules/rrl/.libs knot/modules/rrl/_libs + -rm -rf knot/modules/stats/.libs knot/modules/stats/_libs + -rm -rf knot/modules/synthrecord/.libs knot/modules/synthrecord/_libs + -rm -rf knot/modules/whoami/.libs knot/modules/whoami/_libs + -rm -rf knot/nameserver/.libs knot/nameserver/_libs + -rm -rf knot/query/.libs knot/query/_libs + -rm -rf knot/server/.libs knot/server/_libs + -rm -rf knot/updates/.libs knot/updates/_libs + -rm -rf knot/worker/.libs knot/worker/_libs + -rm -rf knot/zone/.libs knot/zone/_libs + -rm -rf libdnssec/.libs libdnssec/_libs + -rm -rf libdnssec/contrib/.libs libdnssec/contrib/_libs + -rm -rf libdnssec/key/.libs libdnssec/key/_libs + -rm -rf libdnssec/keystore/.libs libdnssec/keystore/_libs + -rm -rf libdnssec/list/.libs libdnssec/list/_libs + -rm -rf libdnssec/nsec/.libs libdnssec/nsec/_libs + -rm -rf libdnssec/p11/.libs libdnssec/p11/_libs + -rm -rf libdnssec/shared/.libs libdnssec/shared/_libs + -rm -rf libdnssec/sign/.libs libdnssec/sign/_libs + -rm -rf libknot/.libs libknot/_libs + -rm -rf libknot/control/.libs libknot/control/_libs + -rm -rf libknot/db/.libs libknot/db/_libs + -rm -rf libknot/packet/.libs libknot/packet/_libs + -rm -rf libknot/rrtype/.libs libknot/rrtype/_libs + -rm -rf libknot/yparser/.libs libknot/yparser/_libs + -rm -rf libzscanner/.libs libzscanner/_libs + -rm -rf utils/common/.libs utils/common/_libs +install-pkgconfigDATA: $(pkgconfig_DATA) + @$(NORMAL_INSTALL) + @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(pkgconfigdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pkgconfigdir)" || 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_DATA) $$files '$(DESTDIR)$(pkgconfigdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(pkgconfigdir)" || exit $$?; \ + done + +uninstall-pkgconfigDATA: + @$(NORMAL_UNINSTALL) + @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(pkgconfigdir)'; $(am__uninstall_files_from_dir) +install-include_libdnssecHEADERS: $(include_libdnssec_HEADERS) + @$(NORMAL_INSTALL) + @list='$(include_libdnssec_HEADERS)'; test -n "$(include_libdnssecdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(include_libdnssecdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(include_libdnssecdir)" || 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)$(include_libdnssecdir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(include_libdnssecdir)" || exit $$?; \ + done + +uninstall-include_libdnssecHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(include_libdnssec_HEADERS)'; test -n "$(include_libdnssecdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(include_libdnssecdir)'; $(am__uninstall_files_from_dir) +install-include_libknotdHEADERS: $(include_libknotd_HEADERS) + @$(NORMAL_INSTALL) + @list='$(include_libknotd_HEADERS)'; test -n "$(include_libknotddir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(include_libknotddir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(include_libknotddir)" || 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)$(include_libknotddir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(include_libknotddir)" || exit $$?; \ + done + +uninstall-include_libknotdHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(include_libknotd_HEADERS)'; test -n "$(include_libknotddir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(include_libknotddir)'; $(am__uninstall_files_from_dir) +install-include_libzscannerHEADERS: $(include_libzscanner_HEADERS) + @$(NORMAL_INSTALL) + @list='$(include_libzscanner_HEADERS)'; test -n "$(include_libzscannerdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(include_libzscannerdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(include_libzscannerdir)" || 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)$(include_libzscannerdir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(include_libzscannerdir)" || exit $$?; \ + done + +uninstall-include_libzscannerHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(include_libzscanner_HEADERS)'; test -n "$(include_libzscannerdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(include_libzscannerdir)'; $(am__uninstall_files_from_dir) +install-nobase_include_libknotHEADERS: $(nobase_include_libknot_HEADERS) + @$(NORMAL_INSTALL) + @list='$(nobase_include_libknot_HEADERS)'; test -n "$(include_libknotdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(include_libknotdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(include_libknotdir)" || exit 1; \ + fi; \ + $(am__nobase_list) | while read dir files; do \ + xfiles=; for file in $$files; do \ + if test -f "$$file"; then xfiles="$$xfiles $$file"; \ + else xfiles="$$xfiles $(srcdir)/$$file"; fi; done; \ + test -z "$$xfiles" || { \ + test "x$$dir" = x. || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(include_libknotdir)/$$dir'"; \ + $(MKDIR_P) "$(DESTDIR)$(include_libknotdir)/$$dir"; }; \ + echo " $(INSTALL_HEADER) $$xfiles '$(DESTDIR)$(include_libknotdir)/$$dir'"; \ + $(INSTALL_HEADER) $$xfiles "$(DESTDIR)$(include_libknotdir)/$$dir" || exit $$?; }; \ + done + +uninstall-nobase_include_libknotHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(nobase_include_libknot_HEADERS)'; test -n "$(include_libknotdir)" || list=; \ + $(am__nobase_strip_setup); files=`$(am__nobase_strip)`; \ + dir='$(DESTDIR)$(include_libknotdir)'; $(am__uninstall_files_from_dir) + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) check-am +all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(DATA) $(HEADERS) \ + config.h +install-binPROGRAMS: install-libLTLIBRARIES + +installdirs: + for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(pkglibdir)" "$(DESTDIR)$(bindir)" "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(pkgconfigdir)" "$(DESTDIR)$(include_libdnssecdir)" "$(DESTDIR)$(include_libknotddir)" "$(DESTDIR)$(include_libzscannerdir)" "$(DESTDIR)$(include_libknotdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + -rm -f contrib/$(DEPDIR)/$(am__dirstamp) + -rm -f contrib/$(am__dirstamp) + -rm -f contrib/dnstap/$(DEPDIR)/$(am__dirstamp) + -rm -f contrib/dnstap/$(am__dirstamp) + -rm -f contrib/lmdb/$(DEPDIR)/$(am__dirstamp) + -rm -f contrib/lmdb/$(am__dirstamp) + -rm -f contrib/openbsd/$(DEPDIR)/$(am__dirstamp) + -rm -f contrib/openbsd/$(am__dirstamp) + -rm -f contrib/qp-trie/$(DEPDIR)/$(am__dirstamp) + -rm -f contrib/qp-trie/$(am__dirstamp) + -rm -f contrib/ucw/$(DEPDIR)/$(am__dirstamp) + -rm -f contrib/ucw/$(am__dirstamp) + -rm -f knot/common/$(DEPDIR)/$(am__dirstamp) + -rm -f knot/common/$(am__dirstamp) + -rm -f knot/conf/$(DEPDIR)/$(am__dirstamp) + -rm -f knot/conf/$(am__dirstamp) + -rm -f knot/ctl/$(DEPDIR)/$(am__dirstamp) + -rm -f knot/ctl/$(am__dirstamp) + -rm -f knot/dnssec/$(DEPDIR)/$(am__dirstamp) + -rm -f knot/dnssec/$(am__dirstamp) + -rm -f knot/dnssec/kasp/$(DEPDIR)/$(am__dirstamp) + -rm -f knot/dnssec/kasp/$(am__dirstamp) + -rm -f knot/events/$(DEPDIR)/$(am__dirstamp) + -rm -f knot/events/$(am__dirstamp) + -rm -f knot/events/handlers/$(DEPDIR)/$(am__dirstamp) + -rm -f knot/events/handlers/$(am__dirstamp) + -rm -f knot/journal/$(DEPDIR)/$(am__dirstamp) + -rm -f knot/journal/$(am__dirstamp) + -rm -f knot/modules/$(am__dirstamp) + -rm -f knot/modules/cookies/$(DEPDIR)/$(am__dirstamp) + -rm -f knot/modules/cookies/$(am__dirstamp) + -rm -f knot/modules/dnsproxy/$(DEPDIR)/$(am__dirstamp) + -rm -f knot/modules/dnsproxy/$(am__dirstamp) + -rm -f knot/modules/dnstap/$(DEPDIR)/$(am__dirstamp) + -rm -f knot/modules/dnstap/$(am__dirstamp) + -rm -f knot/modules/geoip/$(DEPDIR)/$(am__dirstamp) + -rm -f knot/modules/geoip/$(am__dirstamp) + -rm -f knot/modules/noudp/$(DEPDIR)/$(am__dirstamp) + -rm -f knot/modules/noudp/$(am__dirstamp) + -rm -f knot/modules/onlinesign/$(DEPDIR)/$(am__dirstamp) + -rm -f knot/modules/onlinesign/$(am__dirstamp) + -rm -f knot/modules/queryacl/$(DEPDIR)/$(am__dirstamp) + -rm -f knot/modules/queryacl/$(am__dirstamp) + -rm -f knot/modules/rrl/$(DEPDIR)/$(am__dirstamp) + -rm -f knot/modules/rrl/$(am__dirstamp) + -rm -f knot/modules/stats/$(DEPDIR)/$(am__dirstamp) + -rm -f knot/modules/stats/$(am__dirstamp) + -rm -f knot/modules/synthrecord/$(DEPDIR)/$(am__dirstamp) + -rm -f knot/modules/synthrecord/$(am__dirstamp) + -rm -f knot/modules/whoami/$(DEPDIR)/$(am__dirstamp) + -rm -f knot/modules/whoami/$(am__dirstamp) + -rm -f knot/nameserver/$(DEPDIR)/$(am__dirstamp) + -rm -f knot/nameserver/$(am__dirstamp) + -rm -f knot/query/$(DEPDIR)/$(am__dirstamp) + -rm -f knot/query/$(am__dirstamp) + -rm -f knot/server/$(DEPDIR)/$(am__dirstamp) + -rm -f knot/server/$(am__dirstamp) + -rm -f knot/updates/$(DEPDIR)/$(am__dirstamp) + -rm -f knot/updates/$(am__dirstamp) + -rm -f knot/worker/$(DEPDIR)/$(am__dirstamp) + -rm -f knot/worker/$(am__dirstamp) + -rm -f knot/zone/$(DEPDIR)/$(am__dirstamp) + -rm -f knot/zone/$(am__dirstamp) + -rm -f libdnssec/$(DEPDIR)/$(am__dirstamp) + -rm -f libdnssec/$(am__dirstamp) + -rm -f libdnssec/contrib/$(DEPDIR)/$(am__dirstamp) + -rm -f libdnssec/contrib/$(am__dirstamp) + -rm -f libdnssec/key/$(DEPDIR)/$(am__dirstamp) + -rm -f libdnssec/key/$(am__dirstamp) + -rm -f libdnssec/keystore/$(DEPDIR)/$(am__dirstamp) + -rm -f libdnssec/keystore/$(am__dirstamp) + -rm -f libdnssec/list/$(DEPDIR)/$(am__dirstamp) + -rm -f libdnssec/list/$(am__dirstamp) + -rm -f libdnssec/nsec/$(DEPDIR)/$(am__dirstamp) + -rm -f libdnssec/nsec/$(am__dirstamp) + -rm -f libdnssec/p11/$(DEPDIR)/$(am__dirstamp) + -rm -f libdnssec/p11/$(am__dirstamp) + -rm -f libdnssec/shared/$(DEPDIR)/$(am__dirstamp) + -rm -f libdnssec/shared/$(am__dirstamp) + -rm -f libdnssec/sign/$(DEPDIR)/$(am__dirstamp) + -rm -f libdnssec/sign/$(am__dirstamp) + -rm -f libknot/$(DEPDIR)/$(am__dirstamp) + -rm -f libknot/$(am__dirstamp) + -rm -f libknot/control/$(DEPDIR)/$(am__dirstamp) + -rm -f libknot/control/$(am__dirstamp) + -rm -f libknot/db/$(DEPDIR)/$(am__dirstamp) + -rm -f libknot/db/$(am__dirstamp) + -rm -f libknot/packet/$(DEPDIR)/$(am__dirstamp) + -rm -f libknot/packet/$(am__dirstamp) + -rm -f libknot/rrtype/$(DEPDIR)/$(am__dirstamp) + -rm -f libknot/rrtype/$(am__dirstamp) + -rm -f libknot/yparser/$(DEPDIR)/$(am__dirstamp) + -rm -f libknot/yparser/$(am__dirstamp) + -rm -f libzscanner/$(DEPDIR)/$(am__dirstamp) + -rm -f libzscanner/$(am__dirstamp) + -rm -f utils/common/$(DEPDIR)/$(am__dirstamp) + -rm -f utils/common/$(am__dirstamp) + -rm -f utils/kdig/$(DEPDIR)/$(am__dirstamp) + -rm -f utils/kdig/$(am__dirstamp) + -rm -f utils/keymgr/$(DEPDIR)/$(am__dirstamp) + -rm -f utils/keymgr/$(am__dirstamp) + -rm -f utils/khost/$(DEPDIR)/$(am__dirstamp) + -rm -f utils/khost/$(am__dirstamp) + -rm -f utils/kjournalprint/$(DEPDIR)/$(am__dirstamp) + -rm -f utils/kjournalprint/$(am__dirstamp) + -rm -f utils/knotc/$(DEPDIR)/$(am__dirstamp) + -rm -f utils/knotc/$(am__dirstamp) + -rm -f utils/knotd/$(DEPDIR)/$(am__dirstamp) + -rm -f utils/knotd/$(am__dirstamp) + -rm -f utils/knsec3hash/$(DEPDIR)/$(am__dirstamp) + -rm -f utils/knsec3hash/$(am__dirstamp) + -rm -f utils/knsupdate/$(DEPDIR)/$(am__dirstamp) + -rm -f utils/knsupdate/$(am__dirstamp) + -rm -f utils/kzonecheck/$(DEPDIR)/$(am__dirstamp) + -rm -f utils/kzonecheck/$(am__dirstamp) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) +@HAVE_DAEMON_FALSE@install-data-hook: +clean: clean-am + +clean-am: clean-binPROGRAMS clean-generic clean-libLTLIBRARIES \ + clean-libtool clean-noinstLTLIBRARIES clean-pkglibLTLIBRARIES \ + clean-sbinPROGRAMS mostlyclean-am + +distclean: distclean-am + -rm -rf contrib/$(DEPDIR) contrib/dnstap/$(DEPDIR) contrib/lmdb/$(DEPDIR) contrib/openbsd/$(DEPDIR) contrib/qp-trie/$(DEPDIR) contrib/ucw/$(DEPDIR) knot/common/$(DEPDIR) knot/conf/$(DEPDIR) knot/ctl/$(DEPDIR) knot/dnssec/$(DEPDIR) knot/dnssec/kasp/$(DEPDIR) knot/events/$(DEPDIR) knot/events/handlers/$(DEPDIR) knot/journal/$(DEPDIR) knot/modules/cookies/$(DEPDIR) knot/modules/dnsproxy/$(DEPDIR) knot/modules/dnstap/$(DEPDIR) knot/modules/geoip/$(DEPDIR) knot/modules/noudp/$(DEPDIR) knot/modules/onlinesign/$(DEPDIR) knot/modules/queryacl/$(DEPDIR) knot/modules/rrl/$(DEPDIR) knot/modules/stats/$(DEPDIR) knot/modules/synthrecord/$(DEPDIR) knot/modules/whoami/$(DEPDIR) knot/nameserver/$(DEPDIR) knot/query/$(DEPDIR) knot/server/$(DEPDIR) knot/updates/$(DEPDIR) knot/worker/$(DEPDIR) knot/zone/$(DEPDIR) libdnssec/$(DEPDIR) libdnssec/contrib/$(DEPDIR) libdnssec/key/$(DEPDIR) libdnssec/keystore/$(DEPDIR) libdnssec/list/$(DEPDIR) libdnssec/nsec/$(DEPDIR) libdnssec/p11/$(DEPDIR) libdnssec/shared/$(DEPDIR) libdnssec/sign/$(DEPDIR) libknot/$(DEPDIR) libknot/control/$(DEPDIR) libknot/db/$(DEPDIR) libknot/packet/$(DEPDIR) libknot/rrtype/$(DEPDIR) libknot/yparser/$(DEPDIR) libzscanner/$(DEPDIR) utils/common/$(DEPDIR) utils/kdig/$(DEPDIR) utils/keymgr/$(DEPDIR) utils/khost/$(DEPDIR) utils/kjournalprint/$(DEPDIR) utils/knotc/$(DEPDIR) utils/knotd/$(DEPDIR) utils/knsec3hash/$(DEPDIR) utils/knsupdate/$(DEPDIR) utils/kzonecheck/$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-hdr distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-include_libdnssecHEADERS \ + install-include_libknotdHEADERS \ + install-include_libzscannerHEADERS \ + install-nobase_include_libknotHEADERS install-pkgconfigDATA + @$(NORMAL_INSTALL) + $(MAKE) $(AM_MAKEFLAGS) install-data-hook +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-binPROGRAMS install-libLTLIBRARIES \ + install-pkglibLTLIBRARIES install-sbinPROGRAMS + +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: installcheck-binPROGRAMS installcheck-sbinPROGRAMS + +maintainer-clean: maintainer-clean-am + -rm -rf contrib/$(DEPDIR) contrib/dnstap/$(DEPDIR) contrib/lmdb/$(DEPDIR) contrib/openbsd/$(DEPDIR) contrib/qp-trie/$(DEPDIR) contrib/ucw/$(DEPDIR) knot/common/$(DEPDIR) knot/conf/$(DEPDIR) knot/ctl/$(DEPDIR) knot/dnssec/$(DEPDIR) knot/dnssec/kasp/$(DEPDIR) knot/events/$(DEPDIR) knot/events/handlers/$(DEPDIR) knot/journal/$(DEPDIR) knot/modules/cookies/$(DEPDIR) knot/modules/dnsproxy/$(DEPDIR) knot/modules/dnstap/$(DEPDIR) knot/modules/geoip/$(DEPDIR) knot/modules/noudp/$(DEPDIR) knot/modules/onlinesign/$(DEPDIR) knot/modules/queryacl/$(DEPDIR) knot/modules/rrl/$(DEPDIR) knot/modules/stats/$(DEPDIR) knot/modules/synthrecord/$(DEPDIR) knot/modules/whoami/$(DEPDIR) knot/nameserver/$(DEPDIR) knot/query/$(DEPDIR) knot/server/$(DEPDIR) knot/updates/$(DEPDIR) knot/worker/$(DEPDIR) knot/zone/$(DEPDIR) libdnssec/$(DEPDIR) libdnssec/contrib/$(DEPDIR) libdnssec/key/$(DEPDIR) libdnssec/keystore/$(DEPDIR) libdnssec/list/$(DEPDIR) libdnssec/nsec/$(DEPDIR) libdnssec/p11/$(DEPDIR) libdnssec/shared/$(DEPDIR) libdnssec/sign/$(DEPDIR) libknot/$(DEPDIR) libknot/control/$(DEPDIR) libknot/db/$(DEPDIR) libknot/packet/$(DEPDIR) libknot/rrtype/$(DEPDIR) libknot/yparser/$(DEPDIR) libzscanner/$(DEPDIR) utils/common/$(DEPDIR) utils/kdig/$(DEPDIR) utils/keymgr/$(DEPDIR) utils/khost/$(DEPDIR) utils/kjournalprint/$(DEPDIR) utils/knotc/$(DEPDIR) utils/knotd/$(DEPDIR) utils/knsec3hash/$(DEPDIR) utils/knsupdate/$(DEPDIR) utils/kzonecheck/$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-binPROGRAMS uninstall-include_libdnssecHEADERS \ + uninstall-include_libknotdHEADERS \ + uninstall-include_libzscannerHEADERS uninstall-libLTLIBRARIES \ + uninstall-nobase_include_libknotHEADERS \ + uninstall-pkgconfigDATA uninstall-pkglibLTLIBRARIES \ + uninstall-sbinPROGRAMS + +.MAKE: all check install install-am install-data-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean \ + clean-binPROGRAMS clean-generic clean-libLTLIBRARIES \ + clean-libtool clean-noinstLTLIBRARIES clean-pkglibLTLIBRARIES \ + clean-sbinPROGRAMS cscopelist-am ctags ctags-am distclean \ + distclean-compile distclean-generic distclean-hdr \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-binPROGRAMS \ + install-data install-data-am install-data-hook install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-include_libdnssecHEADERS \ + install-include_libknotdHEADERS \ + install-include_libzscannerHEADERS install-info \ + install-info-am install-libLTLIBRARIES install-man \ + install-nobase_include_libknotHEADERS install-pdf \ + install-pdf-am install-pkgconfigDATA install-pkglibLTLIBRARIES \ + install-ps install-ps-am install-sbinPROGRAMS install-strip \ + installcheck installcheck-am installcheck-binPROGRAMS \ + installcheck-sbinPROGRAMS installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am uninstall-binPROGRAMS \ + uninstall-include_libdnssecHEADERS \ + uninstall-include_libknotdHEADERS \ + uninstall-include_libzscannerHEADERS uninstall-libLTLIBRARIES \ + uninstall-nobase_include_libknotHEADERS \ + uninstall-pkgconfigDATA uninstall-pkglibLTLIBRARIES \ + uninstall-sbinPROGRAMS + +.PRECIOUS: Makefile + + +@HAVE_LIBDNSTAP_TRUE@.proto.pb-c.c: +@HAVE_LIBDNSTAP_TRUE@ $(AM_V_GEN)@PROTOC_C@ --c_out=. -I$(srcdir) $< + +@HAVE_LIBDNSTAP_TRUE@.proto.pb-c.h: +@HAVE_LIBDNSTAP_TRUE@ $(AM_V_GEN)@PROTOC_C@ --c_out=. -I$(srcdir) $< + +@FAST_PARSER_TRUE@libzscanner/scanner.c: libzscanner/scanner.c.g2 +@FAST_PARSER_TRUE@ @cp $(srcdir)/$@.g2 $@ +@FAST_PARSER_TRUE@ @echo "NOTE: Compilation of scanner.c can take several minutes!" +@FAST_PARSER_FALSE@libzscanner/scanner.c: libzscanner/scanner.c.t0 +@FAST_PARSER_FALSE@ @cp $(srcdir)/$@.t0 $@ + +# Create storage and run-time directories +@HAVE_DAEMON_TRUE@install-data-hook: +@HAVE_DAEMON_TRUE@ $(INSTALL) -d $(DESTDIR)/@config_dir@ +@HAVE_DAEMON_TRUE@ $(INSTALL) -d $(DESTDIR)/@run_dir@ +@HAVE_DAEMON_TRUE@ $(INSTALL) -d $(DESTDIR)/@storage_dir@ + +# 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/config.h.in b/src/config.h.in new file mode 100644 index 0000000..f0c8b56 --- /dev/null +++ b/src/config.h.in @@ -0,0 +1,208 @@ +/* src/config.h.in. Generated from configure.ac by autoheader. */ + +/* Passed CFLAGS from environment */ +#undef CONFIGURE_CFLAGS + +/* Params passed to configure */ +#undef CONFIGURE_PARAMS + +/* Configure summary */ +#undef CONFIGURE_SUMMARY + +/* Configuration DB mapsize. */ +#undef CONF_MAPSIZE + +/* POSIX capabilities available */ +#undef ENABLE_CAP_NG + +/* PKCS #11 support available */ +#undef ENABLE_PKCS11 + +/* Use recvmmsg(). */ +#undef ENABLE_RECVMMSG + +/* Use SO_REUSEPORT. */ +#undef ENABLE_REUSEPORT + +/* Use systemd integration. */ +#undef ENABLE_SYSTEMD + +/* Define to 1 if you have the `accept4' function. */ +#undef HAVE_ACCEPT4 + +/* Define to 1 if you have the <arpa/nameser.h> header file. */ +#undef HAVE_ARPA_NAMESER_H + +/* Define to 1 if you have '__atomic' functions. */ +#undef HAVE_ATOMIC + +/* Define to 1 if you have the `clock_gettime' function. */ +#undef HAVE_CLOCK_GETTIME + +/* Define if FreeBSD-like cpuset_t exists. */ +#undef HAVE_CPUSET_BSD + +/* Define if Linux-like cpu_set_t exists. */ +#undef HAVE_CPUSET_LINUX + +/* Define if cpuset_t and cpuset(3) exists. */ +#undef HAVE_CPUSET_NETBSD + +/* Define to 1 if you have the <dlfcn.h> header file. */ +#undef HAVE_DLFCN_H + +/* GnuTLS ED25519 support available */ +#undef HAVE_ED25519 + +/* Define to 1 if you have the `fgetln' function. */ +#undef HAVE_FGETLN + +/* Define to 1 if you have the `getline' function. */ +#undef HAVE_GETLINE + +/* Define to 1 if you have the `initgroups' function. */ +#undef HAVE_INITGROUPS + +/* Define to 1 if you have the <inttypes.h> header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the <lmdb.h> header file. */ +#undef HAVE_LMDB_H + +/* Define to 1 if you have the `malloc_trim' function. */ +#undef HAVE_MALLOC_TRIM + +/* Define to 1 to enable MaxMind DB. */ +#undef HAVE_MAXMINDDB + +/* Define to 1 if you have the <memory.h> header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the <netdb.h> header file. */ +#undef HAVE_NETDB_H + +/* Define to 1 if you have the <netinet/in.h> header file. */ +#undef HAVE_NETINET_IN_H + +/* Define to 1 if you have the <pthread_np.h> header file. */ +#undef HAVE_PTHREAD_NP_H + +/* Define to 1 if you have the `pthread_setaffinity_np' function. */ +#undef HAVE_PTHREAD_SETAFFINITY_NP + +/* Define to 1 if you have the <resolv.h> header file. */ +#undef HAVE_RESOLV_H + +/* Define to 1 if you have the `setgroups' function. */ +#undef HAVE_SETGROUPS + +/* gnutls_privkey_sign_data2 available */ +#undef HAVE_SIGN_DATA2 + +/* Define to 1 if you have the <stdint.h> header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the <stdlib.h> header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the <strings.h> header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the <string.h> header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the `strlcat' function. */ +#undef HAVE_STRLCAT + +/* Define to 1 if you have the `strlcpy' function. */ +#undef HAVE_STRLCPY + +/* Define to 1 if you have the `sysctlbyname' function. */ +#undef HAVE_SYSCTLBYNAME + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the <sys/types.h> header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the <sys/uio.h> header file. */ +#undef HAVE_SYS_UIO_H + +/* Define to 1 if you have the <unistd.h> header file. */ +#undef HAVE_UNISTD_H + +/* Define to 1 or 0, depending whether the compiler supports simple visibility + declarations. */ +#undef HAVE_VISIBILITY + +/* Define to 1 to enable IDN support */ +#undef LIBIDN + +/* Define to proper libidn header */ +#undef LIBIDN_HEADER + +/* Define to the sub-directory where libtool stores uninstalled libraries. */ +#undef LT_OBJDIR + +/* Name of package */ +#undef PACKAGE + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the home page for this package. */ +#undef PACKAGE_URL + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Define to 1 to enable dnstap support for kdig */ +#undef USE_DNSTAP + +/* Enable extensions on AIX 3, Interix. */ +#ifndef _ALL_SOURCE +# undef _ALL_SOURCE +#endif +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# undef _GNU_SOURCE +#endif +/* Enable threading extensions on Solaris. */ +#ifndef _POSIX_PTHREAD_SEMANTICS +# undef _POSIX_PTHREAD_SEMANTICS +#endif +/* Enable extensions on HP NonStop. */ +#ifndef _TANDEM_SOURCE +# undef _TANDEM_SOURCE +#endif +/* Enable general extensions on Solaris. */ +#ifndef __EXTENSIONS__ +# undef __EXTENSIONS__ +#endif + + +/* Version number of package */ +#undef VERSION + +/* Define to 1 if on MINIX. */ +#undef _MINIX + +/* Define to 2 if the system does not provide POSIX.1 features except with + this defined. */ +#undef _POSIX_1_SOURCE + +/* Define to 1 if you need to in order for `stat' and other things to work. */ +#undef _POSIX_SOURCE diff --git a/src/contrib/Makefile.inc b/src/contrib/Makefile.inc new file mode 100644 index 0000000..a6b6576 --- /dev/null +++ b/src/contrib/Makefile.inc @@ -0,0 +1,103 @@ +noinst_LTLIBRARIES += libcontrib.la + +libcontrib_la_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAG_VISIBILITY) +libcontrib_la_LDFLAGS = $(AM_LDFLAGS) $(LDFLAG_EXCLUDE_LIBS) +if !HAVE_LMDB +libcontrib_la_LDFLAGS += $(pthread_LIBS) +endif !HAVE_LMDB + +EXTRA_DIST += \ + contrib/licenses/0BSD \ + contrib/licenses/BSD-3-Clause \ + contrib/licenses/LGPL-2.0 \ + contrib/licenses/OLDAP-2.8 \ + contrib/lmdb/LICENSE \ + contrib/openbsd/LICENSE \ + contrib/ucw/LICENSE \ + contrib/dnstap/dnstap.proto + +libcontrib_la_SOURCES = \ + contrib/asan.h \ + contrib/base32hex.c \ + contrib/base32hex.h \ + contrib/base64.c \ + contrib/base64.h \ + contrib/ctype.h \ + contrib/dynarray.h \ + contrib/files.c \ + contrib/files.h \ + contrib/getline.c \ + contrib/getline.h \ + contrib/macros.h \ + contrib/mempattern.c \ + contrib/mempattern.h \ + contrib/net.c \ + contrib/net.h \ + contrib/qp-trie/trie.c \ + contrib/qp-trie/trie.h \ + contrib/sockaddr.c \ + contrib/sockaddr.h \ + contrib/string.c \ + contrib/string.h \ + contrib/strtonum.h \ + contrib/time.c \ + contrib/time.h \ + contrib/tolower.h \ + contrib/trim.h \ + contrib/wire_ctx.h \ + contrib/openbsd/siphash.c \ + contrib/openbsd/siphash.h \ + contrib/openbsd/strlcat.c \ + contrib/openbsd/strlcat.h \ + contrib/openbsd/strlcpy.c \ + contrib/openbsd/strlcpy.h \ + contrib/ucw/array-sort.h \ + contrib/ucw/binsearch.h \ + contrib/ucw/heap.c \ + contrib/ucw/heap.h \ + contrib/ucw/lists.c \ + contrib/ucw/lists.h \ + contrib/ucw/mempool.c \ + contrib/ucw/mempool.h + +if !HAVE_LMDB +libcontrib_la_SOURCES += \ + contrib/lmdb/lmdb.h \ + contrib/lmdb/mdb.c \ + contrib/lmdb/midl.c \ + contrib/lmdb/midl.h +endif !HAVE_LMDB + +if HAVE_LIBDNSTAP +noinst_LTLIBRARIES += libdnstap.la + +libdnstap_la_CPPFLAGS = $(AM_CPPFLAGS) $(DNSTAP_CFLAGS) +libdnstap_la_LDFLAGS = $(AM_LDFLAGS) $(DNSTAP_LIBS) + +SUFFIXES = .proto .pb-c.c .pb-c.h + +.proto.pb-c.c: + $(AM_V_GEN)@PROTOC_C@ --c_out=. -I$(srcdir) $< + +.proto.pb-c.h: + $(AM_V_GEN)@PROTOC_C@ --c_out=. -I$(srcdir) $< + +libdnstap_la_SOURCES = \ + contrib/dnstap/convert.c \ + contrib/dnstap/convert.h \ + contrib/dnstap/dnstap.c \ + contrib/dnstap/dnstap.h \ + contrib/dnstap/message.c \ + contrib/dnstap/message.h \ + contrib/dnstap/reader.c \ + contrib/dnstap/reader.h \ + contrib/dnstap/writer.c \ + contrib/dnstap/writer.h + +nodist_libdnstap_la_SOURCES = \ + contrib/dnstap/dnstap.pb-c.c \ + contrib/dnstap/dnstap.pb-c.h + +BUILT_SOURCES += $(nodist_libdnstap_la_SOURCES) +CLEANFILES += $(nodist_libdnstap_la_SOURCES) +endif HAVE_LIBDNSTAP diff --git a/src/contrib/asan.h b/src/contrib/asan.h new file mode 100644 index 0000000..112f81e --- /dev/null +++ b/src/contrib/asan.h @@ -0,0 +1,37 @@ +/* Copyright (C) 2015 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +/* + * see sanitizer/asan_interface.h in compiler-rt (LLVM) + */ +#ifndef __has_feature + #define __has_feature(feature) 0 +#endif +#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__) + void __asan_poison_memory_region(void const volatile *addr, size_t size); + void __asan_unpoison_memory_region(void const volatile *addr, size_t size); + #define ASAN_POISON_MEMORY_REGION(addr, size) \ + __asan_poison_memory_region((addr), (size)) + #define ASAN_UNPOISON_MEMORY_REGION(addr, size) \ + __asan_unpoison_memory_region((addr), (size)) +#else + #define ASAN_POISON_MEMORY_REGION(addr, size) \ + ((void)(addr), (void)(size)) + #define ASAN_UNPOISON_MEMORY_REGION(addr, size) \ + ((void)(addr), (void)(size)) +#endif diff --git a/src/contrib/base32hex.c b/src/contrib/base32hex.c new file mode 100644 index 0000000..56fb427 --- /dev/null +++ b/src/contrib/base32hex.c @@ -0,0 +1,353 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "contrib/base32hex.h" +#include "libknot/errcode.h" + +#include <stdlib.h> +#include <stdint.h> + +/*! \brief Maximal length of binary input to Base32hex encoding. */ +#define MAX_BIN_DATA_LEN ((INT32_MAX / 8) * 5) + +/*! \brief Base32hex padding character. */ +static const uint8_t base32hex_pad = '='; +/*! \brief Base32hex alphabet. */ +static const uint8_t base32hex_enc[] = "0123456789abcdefghijklmnopqrstuv"; + +/*! \brief Indicates bad Base32hex character. */ +#define KO 255 +/*! \brief Indicates Base32hex padding character. */ +#define PD 32 + +/*! \brief Transformation and validation table for decoding Base32hex. */ +static const uint8_t base32hex_dec[256] = { + [ 0] = KO, [ 43] = KO, ['V'] = 31, [129] = KO, [172] = KO, [215] = KO, + [ 1] = KO, [ 44] = KO, ['W'] = KO, [130] = KO, [173] = KO, [216] = KO, + [ 2] = KO, [ 45] = KO, ['X'] = KO, [131] = KO, [174] = KO, [217] = KO, + [ 3] = KO, [ 46] = KO, ['Y'] = KO, [132] = KO, [175] = KO, [218] = KO, + [ 4] = KO, [ 47] = KO, ['Z'] = KO, [133] = KO, [176] = KO, [219] = KO, + [ 5] = KO, ['0'] = 0, [ 91] = KO, [134] = KO, [177] = KO, [220] = KO, + [ 6] = KO, ['1'] = 1, [ 92] = KO, [135] = KO, [178] = KO, [221] = KO, + [ 7] = KO, ['2'] = 2, [ 93] = KO, [136] = KO, [179] = KO, [222] = KO, + [ 8] = KO, ['3'] = 3, [ 94] = KO, [137] = KO, [180] = KO, [223] = KO, + [ 9] = KO, ['4'] = 4, [ 95] = KO, [138] = KO, [181] = KO, [224] = KO, + [ 10] = KO, ['5'] = 5, [ 96] = KO, [139] = KO, [182] = KO, [225] = KO, + [ 11] = KO, ['6'] = 6, ['a'] = 10, [140] = KO, [183] = KO, [226] = KO, + [ 12] = KO, ['7'] = 7, ['b'] = 11, [141] = KO, [184] = KO, [227] = KO, + [ 13] = KO, ['8'] = 8, ['c'] = 12, [142] = KO, [185] = KO, [228] = KO, + [ 14] = KO, ['9'] = 9, ['d'] = 13, [143] = KO, [186] = KO, [229] = KO, + [ 15] = KO, [ 58] = KO, ['e'] = 14, [144] = KO, [187] = KO, [230] = KO, + [ 16] = KO, [ 59] = KO, ['f'] = 15, [145] = KO, [188] = KO, [231] = KO, + [ 17] = KO, [ 60] = KO, ['g'] = 16, [146] = KO, [189] = KO, [232] = KO, + [ 18] = KO, ['='] = PD, ['h'] = 17, [147] = KO, [190] = KO, [233] = KO, + [ 19] = KO, [ 62] = KO, ['i'] = 18, [148] = KO, [191] = KO, [234] = KO, + [ 20] = KO, [ 63] = KO, ['j'] = 19, [149] = KO, [192] = KO, [235] = KO, + [ 21] = KO, [ 64] = KO, ['k'] = 20, [150] = KO, [193] = KO, [236] = KO, + [ 22] = KO, ['A'] = 10, ['l'] = 21, [151] = KO, [194] = KO, [237] = KO, + [ 23] = KO, ['B'] = 11, ['m'] = 22, [152] = KO, [195] = KO, [238] = KO, + [ 24] = KO, ['C'] = 12, ['n'] = 23, [153] = KO, [196] = KO, [239] = KO, + [ 25] = KO, ['D'] = 13, ['o'] = 24, [154] = KO, [197] = KO, [240] = KO, + [ 26] = KO, ['E'] = 14, ['p'] = 25, [155] = KO, [198] = KO, [241] = KO, + [ 27] = KO, ['F'] = 15, ['q'] = 26, [156] = KO, [199] = KO, [242] = KO, + [ 28] = KO, ['G'] = 16, ['r'] = 27, [157] = KO, [200] = KO, [243] = KO, + [ 29] = KO, ['H'] = 17, ['s'] = 28, [158] = KO, [201] = KO, [244] = KO, + [ 30] = KO, ['I'] = 18, ['t'] = 29, [159] = KO, [202] = KO, [245] = KO, + [ 31] = KO, ['J'] = 19, ['u'] = 30, [160] = KO, [203] = KO, [246] = KO, + [ 32] = KO, ['K'] = 20, ['v'] = 31, [161] = KO, [204] = KO, [247] = KO, + [ 33] = KO, ['L'] = 21, ['w'] = KO, [162] = KO, [205] = KO, [248] = KO, + [ 34] = KO, ['M'] = 22, ['x'] = KO, [163] = KO, [206] = KO, [249] = KO, + [ 35] = KO, ['N'] = 23, ['y'] = KO, [164] = KO, [207] = KO, [250] = KO, + [ 36] = KO, ['O'] = 24, ['z'] = KO, [165] = KO, [208] = KO, [251] = KO, + [ 37] = KO, ['P'] = 25, [123] = KO, [166] = KO, [209] = KO, [252] = KO, + [ 38] = KO, ['Q'] = 26, [124] = KO, [167] = KO, [210] = KO, [253] = KO, + [ 39] = KO, ['R'] = 27, [125] = KO, [168] = KO, [211] = KO, [254] = KO, + [ 40] = KO, ['S'] = 28, [126] = KO, [169] = KO, [212] = KO, [255] = KO, + [ 41] = KO, ['T'] = 29, [127] = KO, [170] = KO, [213] = KO, + [ 42] = KO, ['U'] = 30, [128] = KO, [171] = KO, [214] = KO, +}; + +int32_t base32hex_encode(const uint8_t *in, + const uint32_t in_len, + uint8_t *out, + const uint32_t out_len) +{ + // Checking inputs. + if (in == NULL || out == NULL) { + return KNOT_EINVAL; + } + if (in_len > MAX_BIN_DATA_LEN || out_len < ((in_len + 4) / 5) * 8) { + return KNOT_ERANGE; + } + + uint8_t rest_len = in_len % 5; + const uint8_t *stop = in + in_len - rest_len; + uint8_t *text = out; + + // Encoding loop takes 5 bytes and creates 8 characters. + while (in < stop) { + text[0] = base32hex_enc[in[0] >> 3]; + text[1] = base32hex_enc[(in[0] & 0x07) << 2 | in[1] >> 6]; + text[2] = base32hex_enc[(in[1] & 0x3E) >> 1]; + text[3] = base32hex_enc[(in[1] & 0x01) << 4 | in[2] >> 4]; + text[4] = base32hex_enc[(in[2] & 0x0F) << 1 | in[3] >> 7]; + text[5] = base32hex_enc[(in[3] & 0x7C) >> 2]; + text[6] = base32hex_enc[(in[3] & 0x03) << 3 | in[4] >> 5]; + text[7] = base32hex_enc[in[4] & 0x1F]; + text += 8; + in += 5; + } + + // Processing of padding, if any. + switch (rest_len) { + case 4: + text[0] = base32hex_enc[in[0] >> 3]; + text[1] = base32hex_enc[(in[0] & 0x07) << 2 | in[1] >> 6]; + text[2] = base32hex_enc[(in[1] & 0x3E) >> 1]; + text[3] = base32hex_enc[(in[1] & 0x01) << 4 | in[2] >> 4]; + text[4] = base32hex_enc[(in[2] & 0x0F) << 1 | in[3] >> 7]; + text[5] = base32hex_enc[(in[3] & 0x7C) >> 2]; + text[6] = base32hex_enc[(in[3] & 0x03) << 3]; + text[7] = base32hex_pad; + text += 8; + break; + case 3: + text[0] = base32hex_enc[in[0] >> 3]; + text[1] = base32hex_enc[(in[0] & 0x07) << 2 | in[1] >> 6]; + text[2] = base32hex_enc[(in[1] & 0x3E) >> 1]; + text[3] = base32hex_enc[(in[1] & 0x01) << 4 | in[2] >> 4]; + text[4] = base32hex_enc[(in[2] & 0x0F) << 1]; + text[5] = base32hex_pad; + text[6] = base32hex_pad; + text[7] = base32hex_pad; + text += 8; + break; + case 2: + text[0] = base32hex_enc[in[0] >> 3]; + text[1] = base32hex_enc[(in[0] & 0x07) << 2 | in[1] >> 6]; + text[2] = base32hex_enc[(in[1] & 0x3E) >> 1]; + text[3] = base32hex_enc[(in[1] & 0x01) << 4]; + text[4] = base32hex_pad; + text[5] = base32hex_pad; + text[6] = base32hex_pad; + text[7] = base32hex_pad; + text += 8; + break; + case 1: + text[0] = base32hex_enc[in[0] >> 3]; + text[1] = base32hex_enc[(in[0] & 0x07) << 2]; + text[2] = base32hex_pad; + text[3] = base32hex_pad; + text[4] = base32hex_pad; + text[5] = base32hex_pad; + text[6] = base32hex_pad; + text[7] = base32hex_pad; + text += 8; + break; + } + + return (text - out); +} + +int32_t base32hex_encode_alloc(const uint8_t *in, + const uint32_t in_len, + uint8_t **out) +{ + // Checking inputs. + if (out == NULL) { + return KNOT_EINVAL; + } + if (in_len > MAX_BIN_DATA_LEN) { + return KNOT_ERANGE; + } + + // Compute output buffer length. + uint32_t out_len = ((in_len + 4) / 5) * 8; + + // Allocate output buffer. + *out = malloc(out_len); + if (*out == NULL) { + return KNOT_ENOMEM; + } + + // Encode data. + int32_t ret = base32hex_encode(in, in_len, *out, out_len); + if (ret < 0) { + free(*out); + *out = NULL; + } + + return ret; +} + +int32_t base32hex_decode(const uint8_t *in, + const uint32_t in_len, + uint8_t *out, + const uint32_t out_len) +{ + // Checking inputs. + if (in == NULL || out == NULL) { + return KNOT_EINVAL; + } + if (in_len > INT32_MAX || out_len < ((in_len + 7) / 8) * 5) { + return KNOT_ERANGE; + } + if ((in_len % 8) != 0) { + return KNOT_BASE32HEX_ESIZE; + } + + const uint8_t *stop = in + in_len; + uint8_t *bin = out; + uint8_t pad_len = 0; + uint8_t c1, c2, c3, c4, c5, c6, c7, c8; + + // Decoding loop takes 8 characters and creates 5 bytes. + while (in < stop) { + // Filling and transforming 8 Base32hex chars. + c1 = base32hex_dec[in[0]]; + c2 = base32hex_dec[in[1]]; + c3 = base32hex_dec[in[2]]; + c4 = base32hex_dec[in[3]]; + c5 = base32hex_dec[in[4]]; + c6 = base32hex_dec[in[5]]; + c7 = base32hex_dec[in[6]]; + c8 = base32hex_dec[in[7]]; + + // Check 8. char if is bad or padding. + if (c8 >= PD) { + if (c8 == PD && pad_len == 0) { + pad_len = 1; + } else { + return KNOT_BASE32HEX_ECHAR; + } + } + + // Check 7. char if is bad or padding (if so, 6. must be too). + if (c7 >= PD) { + if (c7 == PD && c6 == PD && pad_len == 1) { + pad_len = 3; + } else { + return KNOT_BASE32HEX_ECHAR; + } + } + + // Check 6. char if is bad or padding. + if (c6 >= PD) { + if (!(c6 == PD && pad_len == 3)) { + return KNOT_BASE32HEX_ECHAR; + } + } + + // Check 5. char if is bad or padding. + if (c5 >= PD) { + if (c5 == PD && pad_len == 3) { + pad_len = 4; + } else { + return KNOT_BASE32HEX_ECHAR; + } + } + + // Check 4. char if is bad or padding (if so, 3. must be too). + if (c4 >= PD) { + if (c4 == PD && c3 == PD && pad_len == 4) { + pad_len = 6; + } else { + return KNOT_BASE32HEX_ECHAR; + } + } + + // Check 3. char if is bad or padding. + if (c3 >= PD) { + if (!(c3 == PD && pad_len == 6)) { + return KNOT_BASE32HEX_ECHAR; + } + } + + // 1. and 2. chars must not be padding. + if (c2 >= PD || c1 >= PD) { + return KNOT_BASE32HEX_ECHAR; + } + + // Computing of output data based on padding length. + switch (pad_len) { + case 0: + bin[4] = (c7 << 5) + c8; + // FALLTHROUGH + case 1: + bin[3] = (c5 << 7) + (c6 << 2) + (c7 >> 3); + // FALLTHROUGH + case 3: + bin[2] = (c4 << 4) + (c5 >> 1); + // FALLTHROUGH + case 4: + bin[1] = (c2 << 6) + (c3 << 1) + (c4 >> 4); + // FALLTHROUGH + case 6: + bin[0] = (c1 << 3) + (c2 >> 2); + } + + // Update output end. + switch (pad_len) { + case 0: + bin += 5; + break; + case 1: + bin += 4; + break; + case 3: + bin += 3; + break; + case 4: + bin += 2; + break; + case 6: + bin += 1; + break; + } + + in += 8; + } + + return (bin - out); +} + +int32_t base32hex_decode_alloc(const uint8_t *in, + const uint32_t in_len, + uint8_t **out) +{ + // Checking inputs. + if (out == NULL) { + return KNOT_EINVAL; + } + + // Compute output buffer length. + uint32_t out_len = ((in_len + 7) / 8) * 5; + + // Allocate output buffer. + *out = malloc(out_len); + if (*out == NULL) { + return KNOT_ENOMEM; + } + + // Decode data. + int32_t ret = base32hex_decode(in, in_len, *out, out_len); + if (ret < 0) { + free(*out); + *out = NULL; + } + + return ret; +} diff --git a/src/contrib/base32hex.h b/src/contrib/base32hex.h new file mode 100644 index 0000000..027acd8 --- /dev/null +++ b/src/contrib/base32hex.h @@ -0,0 +1,103 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \brief Base32hex implementation (RFC 4648). + * + * \note Input Base32hex string can contain a-v characters. These characters + * are considered as A-V equivalent. Lower-case variant is used for encoding! + */ + +#pragma once + +#include <stdint.h> + +/*! + * \brief Encodes binary data using Base32hex. + * + * \note Output data buffer contains Base32hex text string which isn't + * terminated with '\0'! + * + * \param in Input binary data. + * \param in_len Length of input data. + * \param out Output data buffer. + * \param out_len Size of output buffer. + * + * \retval >=0 length of output string. + * \retval KNOT_E* if error. + */ +int32_t base32hex_encode(const uint8_t *in, + const uint32_t in_len, + uint8_t *out, + const uint32_t out_len); + +/*! + * \brief Encodes binary data using Base32hex and output stores to own buffer. + * + * \note Output data buffer contains Base32hex text string which isn't + * terminated with '\0'! + * + * \note Output buffer should be deallocated after use. + * + * \param in Input binary data. + * \param in_len Length of input data. + * \param out Output data buffer. + * + * \retval >=0 length of output string. + * \retval KNOT_E* if error. + */ +int32_t base32hex_encode_alloc(const uint8_t *in, + const uint32_t in_len, + uint8_t **out); + +/*! + * \brief Decodes text data using Base32hex. + * + * \note Input data needn't be terminated with '\0'. + * + * \note Input data must be continuous Base32hex string! + * + * \param in Input text data. + * \param in_len Length of input string. + * \param out Output data buffer. + * \param out_len Size of output buffer. + * + * \retval >=0 length of output data. + * \retval KNOT_E* if error. + */ +int32_t base32hex_decode(const uint8_t *in, + const uint32_t in_len, + uint8_t *out, + const uint32_t out_len); + +/*! + * \brief Decodes text data using Base32hex and output stores to own buffer. + * + * \note Input data needn't be terminated with '\0'. + * + * \note Input data must be continuous Base32hex string! + * + * \note Output buffer should be deallocated after use. + * + * \param in Input text data. + * \param in_len Length of input string. + * \param out Output data buffer. + * + * \retval >=0 length of output data. + * \retval KNOT_E* if error. + */ +int32_t base32hex_decode_alloc(const uint8_t *in, + const uint32_t in_len, + uint8_t **out); diff --git a/src/contrib/base64.c b/src/contrib/base64.c new file mode 100644 index 0000000..a0d83a4 --- /dev/null +++ b/src/contrib/base64.c @@ -0,0 +1,272 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "contrib/base64.h" +#include "libknot/errcode.h" + +#include <stdlib.h> +#include <stdint.h> + +/*! \brief Maximal length of binary input to Base64 encoding. */ +#define MAX_BIN_DATA_LEN ((INT32_MAX / 4) * 3) + +/*! \brief Base64 padding character. */ +static const uint8_t base64_pad = '='; +/*! \brief Base64 alphabet. */ +static const uint8_t base64_enc[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +/*! \brief Indicates bad Base64 character. */ +#define KO 255 +/*! \brief Indicates Base64 padding character. */ +#define PD 64 + +/*! \brief Transformation and validation table for decoding Base64. */ +static const uint8_t base64_dec[256] = { + [ 0] = KO, ['+'] = 62, ['V'] = 21, [129] = KO, [172] = KO, [215] = KO, + [ 1] = KO, [ 44] = KO, ['W'] = 22, [130] = KO, [173] = KO, [216] = KO, + [ 2] = KO, [ 45] = KO, ['X'] = 23, [131] = KO, [174] = KO, [217] = KO, + [ 3] = KO, [ 46] = KO, ['Y'] = 24, [132] = KO, [175] = KO, [218] = KO, + [ 4] = KO, ['/'] = 63, ['Z'] = 25, [133] = KO, [176] = KO, [219] = KO, + [ 5] = KO, ['0'] = 52, [ 91] = KO, [134] = KO, [177] = KO, [220] = KO, + [ 6] = KO, ['1'] = 53, [ 92] = KO, [135] = KO, [178] = KO, [221] = KO, + [ 7] = KO, ['2'] = 54, [ 93] = KO, [136] = KO, [179] = KO, [222] = KO, + [ 8] = KO, ['3'] = 55, [ 94] = KO, [137] = KO, [180] = KO, [223] = KO, + [ 9] = KO, ['4'] = 56, [ 95] = KO, [138] = KO, [181] = KO, [224] = KO, + [ 10] = KO, ['5'] = 57, [ 96] = KO, [139] = KO, [182] = KO, [225] = KO, + [ 11] = KO, ['6'] = 58, ['a'] = 26, [140] = KO, [183] = KO, [226] = KO, + [ 12] = KO, ['7'] = 59, ['b'] = 27, [141] = KO, [184] = KO, [227] = KO, + [ 13] = KO, ['8'] = 60, ['c'] = 28, [142] = KO, [185] = KO, [228] = KO, + [ 14] = KO, ['9'] = 61, ['d'] = 29, [143] = KO, [186] = KO, [229] = KO, + [ 15] = KO, [ 58] = KO, ['e'] = 30, [144] = KO, [187] = KO, [230] = KO, + [ 16] = KO, [ 59] = KO, ['f'] = 31, [145] = KO, [188] = KO, [231] = KO, + [ 17] = KO, [ 60] = KO, ['g'] = 32, [146] = KO, [189] = KO, [232] = KO, + [ 18] = KO, ['='] = PD, ['h'] = 33, [147] = KO, [190] = KO, [233] = KO, + [ 19] = KO, [ 62] = KO, ['i'] = 34, [148] = KO, [191] = KO, [234] = KO, + [ 20] = KO, [ 63] = KO, ['j'] = 35, [149] = KO, [192] = KO, [235] = KO, + [ 21] = KO, [ 64] = KO, ['k'] = 36, [150] = KO, [193] = KO, [236] = KO, + [ 22] = KO, ['A'] = 0, ['l'] = 37, [151] = KO, [194] = KO, [237] = KO, + [ 23] = KO, ['B'] = 1, ['m'] = 38, [152] = KO, [195] = KO, [238] = KO, + [ 24] = KO, ['C'] = 2, ['n'] = 39, [153] = KO, [196] = KO, [239] = KO, + [ 25] = KO, ['D'] = 3, ['o'] = 40, [154] = KO, [197] = KO, [240] = KO, + [ 26] = KO, ['E'] = 4, ['p'] = 41, [155] = KO, [198] = KO, [241] = KO, + [ 27] = KO, ['F'] = 5, ['q'] = 42, [156] = KO, [199] = KO, [242] = KO, + [ 28] = KO, ['G'] = 6, ['r'] = 43, [157] = KO, [200] = KO, [243] = KO, + [ 29] = KO, ['H'] = 7, ['s'] = 44, [158] = KO, [201] = KO, [244] = KO, + [ 30] = KO, ['I'] = 8, ['t'] = 45, [159] = KO, [202] = KO, [245] = KO, + [ 31] = KO, ['J'] = 9, ['u'] = 46, [160] = KO, [203] = KO, [246] = KO, + [ 32] = KO, ['K'] = 10, ['v'] = 47, [161] = KO, [204] = KO, [247] = KO, + [ 33] = KO, ['L'] = 11, ['w'] = 48, [162] = KO, [205] = KO, [248] = KO, + [ 34] = KO, ['M'] = 12, ['x'] = 49, [163] = KO, [206] = KO, [249] = KO, + [ 35] = KO, ['N'] = 13, ['y'] = 50, [164] = KO, [207] = KO, [250] = KO, + [ 36] = KO, ['O'] = 14, ['z'] = 51, [165] = KO, [208] = KO, [251] = KO, + [ 37] = KO, ['P'] = 15, [123] = KO, [166] = KO, [209] = KO, [252] = KO, + [ 38] = KO, ['Q'] = 16, [124] = KO, [167] = KO, [210] = KO, [253] = KO, + [ 39] = KO, ['R'] = 17, [125] = KO, [168] = KO, [211] = KO, [254] = KO, + [ 40] = KO, ['S'] = 18, [126] = KO, [169] = KO, [212] = KO, [255] = KO, + [ 41] = KO, ['T'] = 19, [127] = KO, [170] = KO, [213] = KO, + [ 42] = KO, ['U'] = 20, [128] = KO, [171] = KO, [214] = KO, +}; + +int32_t base64_encode(const uint8_t *in, + const uint32_t in_len, + uint8_t *out, + const uint32_t out_len) +{ + // Checking inputs. + if (in == NULL || out == NULL) { + return KNOT_EINVAL; + } + if (in_len > MAX_BIN_DATA_LEN || out_len < ((in_len + 2) / 3) * 4) { + return KNOT_ERANGE; + } + + uint8_t rest_len = in_len % 3; + const uint8_t *stop = in + in_len - rest_len; + uint8_t *text = out; + + // Encoding loop takes 3 bytes and creates 4 characters. + while (in < stop) { + text[0] = base64_enc[in[0] >> 2]; + text[1] = base64_enc[(in[0] & 0x03) << 4 | in[1] >> 4]; + text[2] = base64_enc[(in[1] & 0x0F) << 2 | in[2] >> 6]; + text[3] = base64_enc[in[2] & 0x3F]; + text += 4; + in += 3; + } + + // Processing of padding, if any. + switch (rest_len) { + case 2: + text[0] = base64_enc[in[0] >> 2]; + text[1] = base64_enc[(in[0] & 0x03) << 4 | in[1] >> 4]; + text[2] = base64_enc[(in[1] & 0x0F) << 2]; + text[3] = base64_pad; + text += 4; + break; + case 1: + text[0] = base64_enc[in[0] >> 2]; + text[1] = base64_enc[(in[0] & 0x03) << 4]; + text[2] = base64_pad; + text[3] = base64_pad; + text += 4; + break; + } + + return (text - out); +} + +int32_t base64_encode_alloc(const uint8_t *in, + const uint32_t in_len, + uint8_t **out) +{ + // Checking inputs. + if (out == NULL) { + return KNOT_EINVAL; + } + if (in_len > MAX_BIN_DATA_LEN) { + return KNOT_ERANGE; + } + + // Compute output buffer length. + uint32_t out_len = ((in_len + 2) / 3) * 4; + + // Allocate output buffer. + *out = malloc(out_len); + if (*out == NULL) { + return KNOT_ENOMEM; + } + + // Encode data. + int32_t ret = base64_encode(in, in_len, *out, out_len); + if (ret < 0) { + free(*out); + *out = NULL; + } + + return ret; +} + +int32_t base64_decode(const uint8_t *in, + const uint32_t in_len, + uint8_t *out, + const uint32_t out_len) +{ + // Checking inputs. + if (in == NULL || out == NULL) { + return KNOT_EINVAL; + } + if (in_len > INT32_MAX || out_len < ((in_len + 3) / 4) * 3) { + return KNOT_ERANGE; + } + if ((in_len % 4) != 0) { + return KNOT_BASE64_ESIZE; + } + + const uint8_t *stop = in + in_len; + uint8_t *bin = out; + uint8_t pad_len = 0; + uint8_t c1, c2, c3, c4; + + // Decoding loop takes 4 characters and creates 3 bytes. + while (in < stop) { + // Filling and transforming 4 Base64 chars. + c1 = base64_dec[in[0]]; + c2 = base64_dec[in[1]]; + c3 = base64_dec[in[2]]; + c4 = base64_dec[in[3]]; + + // Check 4. char if is bad or padding. + if (c4 >= PD) { + if (c4 == PD && pad_len == 0) { + pad_len = 1; + } else { + return KNOT_BASE64_ECHAR; + } + } + + // Check 3. char if is bad or padding. + if (c3 >= PD) { + if (c3 == PD && pad_len == 1) { + pad_len = 2; + } else { + return KNOT_BASE64_ECHAR; + } + } + + // Check 1. and 2. chars if are not padding. + if (c2 >= PD || c1 >= PD) { + return KNOT_BASE64_ECHAR; + } + + // Computing of output data based on padding length. + switch (pad_len) { + case 0: + bin[2] = (c3 << 6) + c4; + // FALLTHROUGH + case 1: + bin[1] = (c2 << 4) + (c3 >> 2); + // FALLTHROUGH + case 2: + bin[0] = (c1 << 2) + (c2 >> 4); + } + + // Update output end. + switch (pad_len) { + case 0: + bin += 3; + break; + case 1: + bin += 2; + break; + case 2: + bin += 1; + break; + } + + in += 4; + } + + return (bin - out); +} + +int32_t base64_decode_alloc(const uint8_t *in, + const uint32_t in_len, + uint8_t **out) +{ + // Checking inputs. + if (out == NULL) { + return KNOT_EINVAL; + } + + // Compute output buffer length. + uint32_t out_len = ((in_len + 3) / 4) * 3; + + // Allocate output buffer. + *out = malloc(out_len); + if (*out == NULL) { + return KNOT_ENOMEM; + } + + // Decode data. + int32_t ret = base64_decode(in, in_len, *out, out_len); + if (ret < 0) { + free(*out); + *out = NULL; + } + + return ret; +} diff --git a/src/contrib/base64.h b/src/contrib/base64.h new file mode 100644 index 0000000..611574e --- /dev/null +++ b/src/contrib/base64.h @@ -0,0 +1,102 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \brief Base64 implementation (RFC 4648). + */ + +#pragma once + +#include <stdint.h> + +/*! + * \brief Encodes binary data using Base64. + * + * \note Output data buffer contains Base64 text string which isn't + * terminated with '\0'! + * + * \param in Input binary data. + * \param in_len Length of input data. + * \param out Output data buffer. + * \param out_len Size of output buffer. + * + * \retval >=0 length of output string. + * \retval KNOT_E* if error. + */ +int32_t base64_encode(const uint8_t *in, + const uint32_t in_len, + uint8_t *out, + const uint32_t out_len); + +/*! + * \brief Encodes binary data using Base64 and output stores to own buffer. + * + * \note Output data buffer contains Base64 text string which isn't + * terminated with '\0'! + * + * \note Output buffer should be deallocated after use. + * + * \param in Input binary data. + * \param in_len Length of input data. + * \param out Output data buffer. + * + * \retval >=0 length of output string. + * \retval KNOT_E* if error. + */ +int32_t base64_encode_alloc(const uint8_t *in, + const uint32_t in_len, + uint8_t **out); + +/*! + * \brief Decodes text data using Base64. + * + * \note Input data needn't be terminated with '\0'. + * + * \note Input data must be continuous Base64 string! + * + * \param in Input text data. + * \param in_len Length of input string. + * \param out Output data buffer. + * \param out_len Size of output buffer. + * + * \retval >=0 length of output data. + * \retval KNOT_E* if error. + */ +int32_t base64_decode(const uint8_t *in, + const uint32_t in_len, + uint8_t *out, + const uint32_t out_len); + +/*! + * \brief Decodes text data using Base64 and output stores to own buffer. + * + * \note Input data needn't be terminated with '\0'. + * + * \note Input data must be continuous Base64 string! + * + * \note Output buffer should be deallocated after use. + * + * \param in Input text data. + * \param in_len Length of input string. + * \param out Output data buffer. + * + * \retval >=0 length of output data. + * \retval KNOT_E* if error. + */ +int32_t base64_decode_alloc(const uint8_t *in, + const uint32_t in_len, + uint8_t **out); + +/*! @} */ diff --git a/src/contrib/ctype.h b/src/contrib/ctype.h new file mode 100644 index 0000000..93b85db --- /dev/null +++ b/src/contrib/ctype.h @@ -0,0 +1,192 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \brief Locale-independent ctype functions. + */ + +#pragma once + +#include <ctype.h> +#include <stdbool.h> +#include <stdint.h> + +enum { + CT_DIGIT = 1 << 0, + CT_UPPER = 1 << 1, + CT_LOWER = 1 << 2, + CT_XDIGT = 1 << 3, + CT_PUNCT = 1 << 4, + CT_PRINT = 1 << 5, + CT_SPACE = 1 << 6, +}; + +static const uint8_t char_mask[256] = { + // 0 - 8 + ['\t'] = CT_SPACE, + ['\n'] = CT_SPACE, + ['\v'] = CT_SPACE, + ['\f'] = CT_SPACE, + ['\r'] = CT_SPACE, + // 14 - 31 + [' '] = CT_PRINT | CT_SPACE, + + ['!'] = CT_PRINT | CT_PUNCT, + ['"'] = CT_PRINT | CT_PUNCT, + ['#'] = CT_PRINT | CT_PUNCT, + ['$'] = CT_PRINT | CT_PUNCT, + ['%'] = CT_PRINT | CT_PUNCT, + ['&'] = CT_PRINT | CT_PUNCT, + ['\''] = CT_PRINT | CT_PUNCT, + ['('] = CT_PRINT | CT_PUNCT, + [')'] = CT_PRINT | CT_PUNCT, + ['*'] = CT_PRINT | CT_PUNCT, + ['+'] = CT_PRINT | CT_PUNCT, + [','] = CT_PRINT | CT_PUNCT, + ['-'] = CT_PRINT | CT_PUNCT, + ['.'] = CT_PRINT | CT_PUNCT, + ['/'] = CT_PRINT | CT_PUNCT, + + ['0'] = CT_PRINT | CT_DIGIT | CT_XDIGT, + ['1'] = CT_PRINT | CT_DIGIT | CT_XDIGT, + ['2'] = CT_PRINT | CT_DIGIT | CT_XDIGT, + ['3'] = CT_PRINT | CT_DIGIT | CT_XDIGT, + ['4'] = CT_PRINT | CT_DIGIT | CT_XDIGT, + ['5'] = CT_PRINT | CT_DIGIT | CT_XDIGT, + ['6'] = CT_PRINT | CT_DIGIT | CT_XDIGT, + ['7'] = CT_PRINT | CT_DIGIT | CT_XDIGT, + ['8'] = CT_PRINT | CT_DIGIT | CT_XDIGT, + ['9'] = CT_PRINT | CT_DIGIT | CT_XDIGT, + + [':'] = CT_PRINT | CT_PUNCT, + [';'] = CT_PRINT | CT_PUNCT, + ['<'] = CT_PRINT | CT_PUNCT, + ['='] = CT_PRINT | CT_PUNCT, + ['>'] = CT_PRINT | CT_PUNCT, + ['?'] = CT_PRINT | CT_PUNCT, + ['@'] = CT_PRINT | CT_PUNCT, + + ['A'] = CT_PRINT | CT_UPPER | CT_XDIGT, + ['B'] = CT_PRINT | CT_UPPER | CT_XDIGT, + ['C'] = CT_PRINT | CT_UPPER | CT_XDIGT, + ['D'] = CT_PRINT | CT_UPPER | CT_XDIGT, + ['E'] = CT_PRINT | CT_UPPER | CT_XDIGT, + ['F'] = CT_PRINT | CT_UPPER | CT_XDIGT, + ['G'] = CT_PRINT | CT_UPPER, + ['H'] = CT_PRINT | CT_UPPER, + ['I'] = CT_PRINT | CT_UPPER, + ['J'] = CT_PRINT | CT_UPPER, + ['K'] = CT_PRINT | CT_UPPER, + ['L'] = CT_PRINT | CT_UPPER, + ['M'] = CT_PRINT | CT_UPPER, + ['N'] = CT_PRINT | CT_UPPER, + ['O'] = CT_PRINT | CT_UPPER, + ['P'] = CT_PRINT | CT_UPPER, + ['Q'] = CT_PRINT | CT_UPPER, + ['R'] = CT_PRINT | CT_UPPER, + ['S'] = CT_PRINT | CT_UPPER, + ['T'] = CT_PRINT | CT_UPPER, + ['U'] = CT_PRINT | CT_UPPER, + ['V'] = CT_PRINT | CT_UPPER, + ['W'] = CT_PRINT | CT_UPPER, + ['X'] = CT_PRINT | CT_UPPER, + ['Y'] = CT_PRINT | CT_UPPER, + ['Z'] = CT_PRINT | CT_UPPER, + + ['['] = CT_PRINT | CT_PUNCT, + ['\\'] = CT_PRINT | CT_PUNCT, + [']'] = CT_PRINT | CT_PUNCT, + ['^'] = CT_PRINT | CT_PUNCT, + ['_'] = CT_PRINT | CT_PUNCT, + ['`'] = CT_PRINT | CT_PUNCT, + + ['a'] = CT_PRINT | CT_LOWER | CT_XDIGT, + ['b'] = CT_PRINT | CT_LOWER | CT_XDIGT, + ['c'] = CT_PRINT | CT_LOWER | CT_XDIGT, + ['d'] = CT_PRINT | CT_LOWER | CT_XDIGT, + ['e'] = CT_PRINT | CT_LOWER | CT_XDIGT, + ['f'] = CT_PRINT | CT_LOWER | CT_XDIGT, + ['g'] = CT_PRINT | CT_LOWER, + ['h'] = CT_PRINT | CT_LOWER, + ['i'] = CT_PRINT | CT_LOWER, + ['j'] = CT_PRINT | CT_LOWER, + ['k'] = CT_PRINT | CT_LOWER, + ['l'] = CT_PRINT | CT_LOWER, + ['m'] = CT_PRINT | CT_LOWER, + ['n'] = CT_PRINT | CT_LOWER, + ['o'] = CT_PRINT | CT_LOWER, + ['p'] = CT_PRINT | CT_LOWER, + ['q'] = CT_PRINT | CT_LOWER, + ['r'] = CT_PRINT | CT_LOWER, + ['s'] = CT_PRINT | CT_LOWER, + ['t'] = CT_PRINT | CT_LOWER, + ['u'] = CT_PRINT | CT_LOWER, + ['v'] = CT_PRINT | CT_LOWER, + ['w'] = CT_PRINT | CT_LOWER, + ['x'] = CT_PRINT | CT_LOWER, + ['y'] = CT_PRINT | CT_LOWER, + ['z'] = CT_PRINT | CT_LOWER, + + ['{'] = CT_PRINT | CT_PUNCT, + ['|'] = CT_PRINT | CT_PUNCT, + ['}'] = CT_PRINT | CT_PUNCT, + ['~'] = CT_PRINT | CT_PUNCT, + // 127 - 255 +}; + +static inline bool is_alnum(uint8_t c) +{ + return char_mask[c] & (CT_DIGIT | CT_UPPER | CT_LOWER); +} + +static inline bool is_alpha(uint8_t c) +{ + return char_mask[c] & (CT_UPPER | CT_LOWER); +} + +static inline bool is_digit(uint8_t c) +{ + return char_mask[c] & CT_DIGIT; +} + +static inline bool is_xdigit(uint8_t c) +{ + return char_mask[c] & CT_XDIGT; +} + +static inline bool is_lower(uint8_t c) +{ + return char_mask[c] & CT_LOWER; +} + +static inline bool is_upper(uint8_t c) +{ + return char_mask[c] & CT_UPPER; +} + +static inline bool is_print(uint8_t c) +{ + return char_mask[c] & CT_PRINT; +} + +static inline bool is_punct(uint8_t c) +{ + return char_mask[c] & CT_PUNCT; +} + +static inline bool is_space(uint8_t c) +{ + return char_mask[c] & CT_SPACE; +} diff --git a/src/contrib/dnstap/convert.c b/src/contrib/dnstap/convert.c new file mode 100644 index 0000000..f59bacd --- /dev/null +++ b/src/contrib/dnstap/convert.c @@ -0,0 +1,142 @@ +/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <sys/socket.h> +#include <sys/types.h> +#include <netinet/in.h> +#include <netinet/ip.h> + +#include "contrib/dnstap/convert.h" +#include "contrib/dnstap/dnstap.pb-c.h" + +/*! + * \brief Translation between real and Dnstap value. + */ +typedef struct mapping { + int real; + int dnstap; +} mapping_t; + +/*! + * \brief Mapping for network family. + */ +static const mapping_t SOCKET_FAMILY_MAPPING[] = { + { AF_INET, DNSTAP__SOCKET_FAMILY__INET }, + { AF_INET6, DNSTAP__SOCKET_FAMILY__INET6 }, + { 0 } +}; + +/*! + * \brief Mapping from network protocol. + */ +static const mapping_t SOCKET_PROTOCOL_MAPPING[] = { + { IPPROTO_UDP, DNSTAP__SOCKET_PROTOCOL__UDP }, + { IPPROTO_TCP, DNSTAP__SOCKET_PROTOCOL__TCP }, + { 0 } +}; + +/*! + * \brief Get Dnstap value for a given real value. + */ +static int encode(const mapping_t *mapping, int real) +{ + for (const mapping_t *m = mapping; m->real != 0; m += 1) { + if (m->real == real) { + return m->dnstap; + } + } + + return 0; +} + +/*! + * \brief Get real value for a given Dnstap value. + */ +static int decode(const mapping_t *mapping, int dnstap) +{ + for (const mapping_t *m = mapping; m->real != 0; m += 1) { + if (m->dnstap == dnstap) { + return m->real; + } + } + + return 0; +} + +/* -- public API ----------------------------------------------------------- */ + +Dnstap__SocketFamily dt_family_encode(int family) +{ + return encode(SOCKET_FAMILY_MAPPING, family); +} + +int dt_family_decode(Dnstap__SocketFamily dnstap_family) +{ + return decode(SOCKET_FAMILY_MAPPING, dnstap_family); +} + +Dnstap__SocketProtocol dt_protocol_encode(int protocol) +{ + return encode(SOCKET_PROTOCOL_MAPPING, protocol); +} + +int dt_protocol_decode(Dnstap__SocketProtocol dnstap_protocol) +{ + return decode(SOCKET_PROTOCOL_MAPPING, dnstap_protocol); +} + +bool dt_message_type_is_query(Dnstap__Message__Type type) +{ + switch (type) { + case DNSTAP__MESSAGE__TYPE__AUTH_QUERY: + case DNSTAP__MESSAGE__TYPE__CLIENT_QUERY: + case DNSTAP__MESSAGE__TYPE__FORWARDER_QUERY: + case DNSTAP__MESSAGE__TYPE__RESOLVER_QUERY: + case DNSTAP__MESSAGE__TYPE__STUB_QUERY: + case DNSTAP__MESSAGE__TYPE__TOOL_QUERY: + return true; + default: + return false; + } +} + +bool dt_message_type_is_response(Dnstap__Message__Type type) +{ + switch (type) { + case DNSTAP__MESSAGE__TYPE__AUTH_RESPONSE: + case DNSTAP__MESSAGE__TYPE__CLIENT_RESPONSE: + case DNSTAP__MESSAGE__TYPE__FORWARDER_RESPONSE: + case DNSTAP__MESSAGE__TYPE__RESOLVER_RESPONSE: + case DNSTAP__MESSAGE__TYPE__STUB_RESPONSE: + case DNSTAP__MESSAGE__TYPE__TOOL_RESPONSE: + return true; + default: + return false; + } +} + +bool dt_message_role_is_initiator(Dnstap__Message__Type type) +{ + switch (type) { + case DNSTAP__MESSAGE__TYPE__AUTH_QUERY: + case DNSTAP__MESSAGE__TYPE__AUTH_RESPONSE: + case DNSTAP__MESSAGE__TYPE__CLIENT_QUERY: + case DNSTAP__MESSAGE__TYPE__CLIENT_RESPONSE: + return false; + default: + return true; + } +} diff --git a/src/contrib/dnstap/convert.h b/src/contrib/dnstap/convert.h new file mode 100644 index 0000000..bf00ffb --- /dev/null +++ b/src/contrib/dnstap/convert.h @@ -0,0 +1,59 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \brief Dnstap identifiers conversions. + */ + +#pragma once + +#include <stdbool.h> + +#include "contrib/dnstap/dnstap.pb-c.h" + +/*! + * \brief Get Dnstap socket family from the real one. + */ +Dnstap__SocketFamily dt_family_encode(int family); + +/*! + * \brief Get real socket family from the Dnstap one. + */ +int dt_family_decode(Dnstap__SocketFamily dnstap_family); + +/*! + * \brief Get Dnstap protocol from a real one. + */ +Dnstap__SocketProtocol dt_protocol_encode(int protocol); + +/*! + * \brief Get real protocol from the Dnstap one. + */ +int dt_protocol_decode(Dnstap__SocketProtocol dnstap_protocol); + +/*! + * Check if a message type is any type of a query. + */ +bool dt_message_type_is_query(Dnstap__Message__Type type); + +/*! + * Check if a message type is any type of a response. + */ +bool dt_message_type_is_response(Dnstap__Message__Type type); + +/*! + * Check if a message role is any type of an initiator. + */ +bool dt_message_role_is_initiator(Dnstap__Message__Type type); diff --git a/src/contrib/dnstap/dnstap.c b/src/contrib/dnstap/dnstap.c new file mode 100644 index 0000000..9e24eac --- /dev/null +++ b/src/contrib/dnstap/dnstap.c @@ -0,0 +1,40 @@ +/* Copyright (C) 2014 Farsight Security, Inc. <software@farsightsecurity.com> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdint.h> +#include <stdlib.h> + +#include "contrib/dnstap/dnstap.pb-c.h" + +#define DNSTAP_INITIAL_BUF_SIZE 256 + +uint8_t* dt_pack(const Dnstap__Dnstap *d, uint8_t **buf, size_t *sz) +{ + ProtobufCBufferSimple sbuf = { { NULL } }; + + sbuf.base.append = protobuf_c_buffer_simple_append; + sbuf.len = 0; + sbuf.alloced = DNSTAP_INITIAL_BUF_SIZE; + sbuf.data = malloc(sbuf.alloced); + if (sbuf.data == NULL) { + return NULL; + } + sbuf.must_free_data = 1; + + *sz = dnstap__dnstap__pack_to_buffer(d, (ProtobufCBuffer *) &sbuf); + *buf = sbuf.data; + return *buf; +} diff --git a/src/contrib/dnstap/dnstap.h b/src/contrib/dnstap/dnstap.h new file mode 100644 index 0000000..41e5f65 --- /dev/null +++ b/src/contrib/dnstap/dnstap.h @@ -0,0 +1,46 @@ +/* Copyright (C) 2014 Farsight Security, Inc. <software@farsightsecurity.com> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \author Robert Edmonds <edmonds@fsi.io> + * + * \brief Public interface for dnstap. + */ + +#pragma once + +#include <stddef.h> +#include <stdint.h> + +#include "contrib/dnstap/dnstap.pb-c.h" + +/*! \brief Frame Streams "Content Type" value for dnstap. */ +#define DNSTAP_CONTENT_TYPE "protobuf:dnstap.Dnstap" + +/*! + * \brief Serializes a filled out dnstap protobuf struct. Dynamically allocates + * storage for the serialized frame. + * + * \note This function returns a copy of its parameter return value 'buf' to + * make error checking slightly easier. + * + * \param d dnstap protobuf struct. + * \param[out] buf Serialized frame. + * \param[out] sz Size in bytes of the serialized frame. + * + * \return Serialized frame. + * \retval NULL if error. + */ +uint8_t* dt_pack(const Dnstap__Dnstap *d, uint8_t **buf, size_t *sz); diff --git a/src/contrib/dnstap/dnstap.proto b/src/contrib/dnstap/dnstap.proto new file mode 100644 index 0000000..ea5c77a --- /dev/null +++ b/src/contrib/dnstap/dnstap.proto @@ -0,0 +1,270 @@ +// dnstap: flexible, structured event replication format for DNS software +// +// This file contains the protobuf schemas for the "dnstap" structured event +// replication format for DNS software. + +// Written in 2013-2014 by Farsight Security, Inc. +// +// To the extent possible under law, the author(s) have dedicated all +// copyright and related and neighboring rights to this file to the public +// domain worldwide. This file is distributed without any warranty. +// +// You should have received a copy of the CC0 Public Domain Dedication along +// with this file. If not, see: +// +// <http://creativecommons.org/publicdomain/zero/1.0/>. + +syntax = "proto2"; + +package dnstap; + +// "Dnstap": this is the top-level dnstap type, which is a "union" type that +// contains other kinds of dnstap payloads, although currently only one type +// of dnstap payload is defined. +// See: https://developers.google.com/protocol-buffers/docs/techniques#union +message Dnstap { + // DNS server identity. + // If enabled, this is the identity string of the DNS server which generated + // this message. Typically this would be the same string as returned by an + // "NSID" (RFC 5001) query. + optional bytes identity = 1; + + // DNS server version. + // If enabled, this is the version string of the DNS server which generated + // this message. Typically this would be the same string as returned by a + // "version.bind" query. + optional bytes version = 2; + + // Extra data for this payload. + // This field can be used for adding an arbitrary byte-string annotation to + // the payload. No encoding or interpretation is applied or enforced. + optional bytes extra = 3; + + // Identifies which field below is filled in. + enum Type { + MESSAGE = 1; + } + required Type type = 15; + + // One of the following will be filled in. + optional Message message = 14; +} + +// SocketFamily: the network protocol family of a socket. This specifies how +// to interpret "network address" fields. +enum SocketFamily { + INET = 1; // IPv4 (RFC 791) + INET6 = 2; // IPv6 (RFC 2460) +} + +// SocketProtocol: the transport protocol of a socket. This specifies how to +// interpret "transport port" fields. +enum SocketProtocol { + UDP = 1; // User Datagram Protocol (RFC 768) + TCP = 2; // Transmission Control Protocol (RFC 793) +} + +// Message: a wire-format (RFC 1035 section 4) DNS message and associated +// metadata. Applications generating "Message" payloads should follow +// certain requirements based on the MessageType, see below. +message Message { + + // There are eight types of "Message" defined that correspond to the + // four arrows in the following diagram, slightly modified from RFC 1035 + // section 2: + + // +---------+ +----------+ +--------+ + // | | query | | query | | + // | Stub |-SQ--------CQ->| Recursive|-RQ----AQ->| Auth. | + // | Resolver| | Server | | Name | + // | |<-SR--------CR-| |<-RR----AR-| Server | + // +---------+ response | | response | | + // +----------+ +--------+ + + // Each arrow has two Type values each, one for each "end" of each arrow, + // because these are considered to be distinct events. Each end of each + // arrow on the diagram above has been marked with a two-letter Type + // mnemonic. Clockwise from upper left, these mnemonic values are: + // + // SQ: STUB_QUERY + // CQ: CLIENT_QUERY + // RQ: RESOLVER_QUERY + // AQ: AUTH_QUERY + // AR: AUTH_RESPONSE + // RR: RESOLVER_RESPONSE + // CR: CLIENT_RESPONSE + // SR: STUB_RESPONSE + + // Two additional types of "Message" have been defined for the + // "forwarding" case where an upstream DNS server is responsible for + // further recursion. These are not shown on the diagram above, but have + // the following mnemonic values: + + // FQ: FORWARDER_QUERY + // FR: FORWARDER_RESPONSE + + // The "Message" Type values are defined below. + + enum Type { + // AUTH_QUERY is a DNS query message received from a resolver by an + // authoritative name server, from the perspective of the authoritative + // name server. + AUTH_QUERY = 1; + + // AUTH_RESPONSE is a DNS response message sent from an authoritative + // name server to a resolver, from the perspective of the authoritative + // name server. + AUTH_RESPONSE = 2; + + // RESOLVER_QUERY is a DNS query message sent from a resolver to an + // authoritative name server, from the perspective of the resolver. + // Resolvers typically clear the RD (recursion desired) bit when + // sending queries. + RESOLVER_QUERY = 3; + + // RESOLVER_RESPONSE is a DNS response message received from an + // authoritative name server by a resolver, from the perspective of + // the resolver. + RESOLVER_RESPONSE = 4; + + // CLIENT_QUERY is a DNS query message sent from a client to a DNS + // server which is expected to perform further recursion, from the + // perspective of the DNS server. The client may be a stub resolver or + // forwarder or some other type of software which typically sets the RD + // (recursion desired) bit when querying the DNS server. The DNS server + // may be a simple forwarding proxy or it may be a full recursive + // resolver. + CLIENT_QUERY = 5; + + // CLIENT_RESPONSE is a DNS response message sent from a DNS server to + // a client, from the perspective of the DNS server. The DNS server + // typically sets the RA (recursion available) bit when responding. + CLIENT_RESPONSE = 6; + + // FORWARDER_QUERY is a DNS query message sent from a downstream DNS + // server to an upstream DNS server which is expected to perform + // further recursion, from the perspective of the downstream DNS + // server. + FORWARDER_QUERY = 7; + + // FORWARDER_RESPONSE is a DNS response message sent from an upstream + // DNS server performing recursion to a downstream DNS server, from the + // perspective of the downstream DNS server. + FORWARDER_RESPONSE = 8; + + // STUB_QUERY is a DNS query message sent from a stub resolver to a DNS + // server, from the perspective of the stub resolver. + STUB_QUERY = 9; + + // STUB_RESPONSE is a DNS response message sent from a DNS server to a + // stub resolver, from the perspective of the stub resolver. + STUB_RESPONSE = 10; + + // TOOL_QUERY is a DNS query message sent from a DNS software tool to a + // DNS server, from the perspective of the tool. + TOOL_QUERY = 11; + + // TOOL_RESPONSE is a DNS response message received by a DNS software + // tool from a DNS server, from the perspective of the tool. + TOOL_RESPONSE = 12; + } + + // One of the Type values described above. + required Type type = 1; + + // One of the SocketFamily values described above. + optional SocketFamily socket_family = 2; + + // One of the SocketProtocol values described above. + optional SocketProtocol socket_protocol = 3; + + // The network address of the message initiator. + // For SocketFamily INET, this field is 4 octets (IPv4 address). + // For SocketFamily INET6, this field is 16 octets (IPv6 address). + optional bytes query_address = 4; + + // The network address of the message responder. + // For SocketFamily INET, this field is 4 octets (IPv4 address). + // For SocketFamily INET6, this field is 16 octets (IPv6 address). + optional bytes response_address = 5; + + // The transport port of the message initiator. + // This is a 16-bit UDP or TCP port number, depending on SocketProtocol. + optional uint32 query_port = 6; + + // The transport port of the message responder. + // This is a 16-bit UDP or TCP port number, depending on SocketProtocol. + optional uint32 response_port = 7; + + // The time at which the DNS query message was sent or received, depending + // on whether this is an AUTH_QUERY, RESOLVER_QUERY, or CLIENT_QUERY. + // This is the number of seconds since the UNIX epoch. + optional uint64 query_time_sec = 8; + + // The time at which the DNS query message was sent or received. + // This is the seconds fraction, expressed as a count of nanoseconds. + optional fixed32 query_time_nsec = 9; + + // The initiator's original wire-format DNS query message, verbatim. + optional bytes query_message = 10; + + // The "zone" or "bailiwick" pertaining to the DNS query message. + // This is a wire-format DNS domain name. + optional bytes query_zone = 11; + + // The time at which the DNS response message was sent or received, + // depending on whether this is an AUTH_RESPONSE, RESOLVER_RESPONSE, or + // CLIENT_RESPONSE. + // This is the number of seconds since the UNIX epoch. + optional uint64 response_time_sec = 12; + + // The time at which the DNS response message was sent or received. + // This is the seconds fraction, expressed as a count of nanoseconds. + optional fixed32 response_time_nsec = 13; + + // The responder's original wire-format DNS response message, verbatim. + optional bytes response_message = 14; +} + +// All fields except for 'type' in the Message schema are optional. +// It is recommended that at least the following fields be filled in for +// particular types of Messages. + +// AUTH_QUERY: +// socket_family, socket_protocol +// query_address, query_port +// query_message +// query_time_sec, query_time_nsec + +// AUTH_RESPONSE: +// socket_family, socket_protocol +// query_address, query_port +// query_time_sec, query_time_nsec +// response_message +// response_time_sec, response_time_nsec + +// RESOLVER_QUERY: +// socket_family, socket_protocol +// query_message +// query_time_sec, query_time_nsec +// query_zone +// response_address, response_port + +// RESOLVER_RESPONSE: +// socket_family, socket_protocol +// query_time_sec, query_time_nsec +// query_zone +// response_address, response_port +// response_message +// response_time_sec, response_time_nsec + +// CLIENT_QUERY: +// socket_family, socket_protocol +// query_message +// query_time_sec, query_time_nsec + +// CLIENT_RESPONSE: +// socket_family, socket_protocol +// query_time_sec, query_time_nsec +// response_message +// response_time_sec, response_time_nsec diff --git a/src/contrib/dnstap/message.c b/src/contrib/dnstap/message.c new file mode 100644 index 0000000..0b41e9d --- /dev/null +++ b/src/contrib/dnstap/message.c @@ -0,0 +1,130 @@ +/* Copyright (C) 2017 Farsight Security, Inc. <software@farsightsecurity.com> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <netinet/in.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +#include "libknot/errcode.h" + +#include "contrib/dnstap/convert.h" +#include "contrib/dnstap/message.h" + +static void set_address(const struct sockaddr *sockaddr, + ProtobufCBinaryData *addr, + protobuf_c_boolean *has_addr, + uint32_t *port, + protobuf_c_boolean *has_port) +{ + if (sockaddr == NULL) { + *has_addr = 0; + *has_port = 0; + return; + } + + *has_addr = 1; + *has_port = 1; + + if (sockaddr->sa_family == AF_INET) { + const struct sockaddr_in *sai; + sai = (const struct sockaddr_in *)sockaddr; + addr->len = sizeof(sai->sin_addr); + addr->data = (uint8_t *)&sai->sin_addr.s_addr; + *port = ntohs(sai->sin_port); + } else if (sockaddr->sa_family == AF_INET6) { + const struct sockaddr_in6 *sai6; + sai6 = (const struct sockaddr_in6 *)sockaddr; + addr->len = sizeof(sai6->sin6_addr); + addr->data = (uint8_t *)&sai6->sin6_addr.s6_addr; + *port = ntohs(sai6->sin6_port); + } +} + +static int get_family(const struct sockaddr *query_sa, + const struct sockaddr *response_sa) +{ + const struct sockaddr *source = query_sa ? query_sa : response_sa; + if (source == NULL) { + return 0; + } + + return dt_family_encode(source->sa_family); +} + +int dt_message_fill(Dnstap__Message *m, + const Dnstap__Message__Type type, + const struct sockaddr *query_sa, + const struct sockaddr *response_sa, + const int protocol, + const void *wire, + const size_t len_wire, + const struct timespec *mtime) +{ + if (m == NULL) { + return KNOT_EINVAL; + } + + memset(m, 0, sizeof(*m)); + + m->base.descriptor = &dnstap__message__descriptor; + + // Message.type + m->type = type; + + // Message.socket_family + m->socket_family = get_family(query_sa, response_sa); + m->has_socket_family = m->socket_family != 0; + + // Message.socket_protocol + m->socket_protocol = dt_protocol_encode(protocol); + m->has_socket_protocol = m->socket_protocol != 0; + + // Message addresses + set_address(query_sa, &m->query_address, &m->has_query_address, + &m->query_port, &m->has_query_port); + set_address(response_sa, &m->response_address, &m->has_response_address, + &m->response_port, &m->has_response_port); + + if (dt_message_type_is_query(type)) { + // Message.query_message + m->query_message.len = len_wire; + m->query_message.data = (uint8_t *)wire; + m->has_query_message = 1; + // Message.query_time_sec, Message.query_time_nsec + if (mtime != NULL) { + m->query_time_sec = mtime->tv_sec; + m->query_time_nsec = mtime->tv_nsec; + m->has_query_time_sec = 1; + m->has_query_time_nsec = 1; + } + } else if (dt_message_type_is_response(type)) { + // Message.response_message + m->response_message.len = len_wire; + m->response_message.data = (uint8_t *)wire; + m->has_response_message = 1; + // Message.response_time_sec, Message.response_time_nsec + if (mtime != NULL) { + m->response_time_sec = mtime->tv_sec; + m->response_time_nsec = mtime->tv_nsec; + m->has_response_time_sec = 1; + m->has_response_time_nsec = 1; + } + } + + return KNOT_EOK; +} diff --git a/src/contrib/dnstap/message.h b/src/contrib/dnstap/message.h new file mode 100644 index 0000000..11a967d --- /dev/null +++ b/src/contrib/dnstap/message.h @@ -0,0 +1,62 @@ +/* Copyright (C) 2017 Farsight Security, Inc. <software@farsightsecurity.com> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \author Robert Edmonds <edmonds@fsi.io> + * + * \brief Dnstap message interface. + */ + +#pragma once + +#include <sys/socket.h> +#include <sys/time.h> +#include <stddef.h> + +#include "contrib/dnstap/dnstap.pb-c.h" + +/*! + * \brief Fill a Dnstap__Message structure with the given parameters. + * + * \param[out] m + * Dnstap__Message structure to fill. Will be zeroed first. + * \param type + * One of the DNSTAP__MESSAGE__TYPE__* values. + * \param query_sa + * sockaddr_in or sockaddr_in6 to use when filling the 'socket_family', + * 'query_address', 'query_port' fields. + * \param response_sa + * sockaddr_in or sockaddr_in6 to use when filling the 'socket_family', + * 'response_address', 'response_port' fields. + * \param protocol + * \c IPPROTO_UDP or \c IPPROTO_TCP. + * \param wire + * Wire-format query message or response message (depending on 'type'). + * \param len_wire + * Length in bytes of 'wire'. + * \param mtime + * Message time. May be NULL. + * + * \retval KNOT_EOK + * \retval KNOT_EINVAL + */ +int dt_message_fill(Dnstap__Message *m, + const Dnstap__Message__Type type, + const struct sockaddr *query_sa, + const struct sockaddr *response_sa, + const int protocol, + const void *wire, + const size_t len_wire, + const struct timespec *mtime); diff --git a/src/contrib/dnstap/reader.c b/src/contrib/dnstap/reader.c new file mode 100644 index 0000000..593c6ec --- /dev/null +++ b/src/contrib/dnstap/reader.c @@ -0,0 +1,105 @@ +/* Copyright (C) 2014 Farsight Security, Inc. <software@farsightsecurity.com> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "libknot/errcode.h" + +#include "contrib/macros.h" +#include "contrib/dnstap/dnstap.h" +#include "contrib/dnstap/reader.h" + +dt_reader_t* dt_reader_create(const char *file_path) +{ + struct fstrm_file_options *fopt = NULL; + struct fstrm_reader_options *ropt = NULL; + dt_reader_t *reader = NULL; + fstrm_res res; + + reader = calloc(1, sizeof(dt_reader_t)); + if (reader == NULL) { + goto fail; + } + + // Open reader. + fopt = fstrm_file_options_init(); + fstrm_file_options_set_file_path(fopt, file_path); + ropt = fstrm_reader_options_init(); + fstrm_reader_options_add_content_type(ropt, + (const uint8_t *) DNSTAP_CONTENT_TYPE, + strlen(DNSTAP_CONTENT_TYPE)); + reader->fr = fstrm_file_reader_init(fopt, ropt); + fstrm_file_options_destroy(&fopt); + fstrm_reader_options_destroy(&ropt); + if (reader->fr == NULL) { + goto fail; + } + res = fstrm_reader_open(reader->fr); + if (res != fstrm_res_success) { + goto fail; + } + + return reader; +fail: + dt_reader_free(reader); + return NULL; +} + +void dt_reader_free(dt_reader_t *reader) +{ + if (reader == NULL) { + return; + } + + fstrm_reader_destroy(&reader->fr); + free(reader); +} + +int dt_reader_read(dt_reader_t *reader, Dnstap__Dnstap **d) +{ + fstrm_res res; + const uint8_t *data = NULL; + size_t len = 0; + + res = fstrm_reader_read(reader->fr, &data, &len); + if (res == fstrm_res_success) { + *d = dnstap__dnstap__unpack(NULL, len, data); + if (*d == NULL) { + return KNOT_ENOMEM; + } + } else if (res == fstrm_res_failure) { + return KNOT_ERROR; + } else if (res == fstrm_res_stop) { + return KNOT_EOF; + } + + return KNOT_EOK; +} + +void dt_reader_free_frame(dt_reader_t *reader, Dnstap__Dnstap **frame_ptr) +{ + if (!*frame_ptr) { + return; + } + + UNUSED(reader); + + dnstap__dnstap__free_unpacked(*frame_ptr, NULL); + *frame_ptr = NULL; +} diff --git a/src/contrib/dnstap/reader.h b/src/contrib/dnstap/reader.h new file mode 100644 index 0000000..c8cda26 --- /dev/null +++ b/src/contrib/dnstap/reader.h @@ -0,0 +1,72 @@ +/* Copyright (C) 2017 Farsight Security, Inc. <software@farsightsecurity.com> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \brief Dnstap file reader. + */ + +#pragma once + +#include <fstrm.h> +#include <protobuf-c/protobuf-c.h> + +#include "contrib/dnstap/dnstap.pb-c.h" + +/*! \brief Structure for dnstap file reader. */ +typedef struct { + /*!< Input reader. */ + struct fstrm_reader *fr; +} dt_reader_t; + +/*! + * \brief Creates dnstap file reader structure. + * + * \param file_path Name of file to read input from. + * + * \retval reader if success. + * \retval NULL if error. + */ +dt_reader_t* dt_reader_create(const char *file_path); + +/*! + * \brief Close dnstap file reader. + * + * \param reader dnstap file reader structure. + */ +void dt_reader_free(dt_reader_t *reader); + +/*! + * \brief Read a dnstap protobuf from a dnstap file reader. + * + * Caller must deallocate the returned protobuf with the + * dnstap__dnstap__free_unpacked() function. + * + * \param[in] reader dnstap file reader structure. + * \param[out] d Unpacked dnstap protobuf. + * + * \retval KNOT_EOK + * \retval KNOT_ERROR + * \retval KNOT_EOF + * \retval KNOT_ENOMEM + */ +int dt_reader_read(dt_reader_t *reader, Dnstap__Dnstap **d); + +/*! + * \brief free the frame allocated by dt_read_data. + * + * \param reader Dnstap reader context. + * \param d The frame to be freed. + */ +void dt_reader_free_frame(dt_reader_t *reader, Dnstap__Dnstap **d); diff --git a/src/contrib/dnstap/writer.c b/src/contrib/dnstap/writer.c new file mode 100644 index 0000000..08f4519 --- /dev/null +++ b/src/contrib/dnstap/writer.c @@ -0,0 +1,120 @@ +/* Copyright (C) 2014 Farsight Security, Inc. <software@farsightsecurity.com> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "libknot/errcode.h" + +#include "contrib/dnstap/dnstap.h" +#include "contrib/dnstap/writer.h" + +dt_writer_t* dt_writer_create(const char *file_path, const char *version) +{ + struct fstrm_file_options *fopt = NULL; + struct fstrm_writer_options *wopt = NULL; + dt_writer_t *writer = NULL; + fstrm_res res; + + writer = calloc(1, sizeof(dt_writer_t)); + if (writer == NULL) { + goto fail; + } + + // Set "version". + if (version != NULL) { + writer->len_version = strlen(version); + writer->version = strdup(version); + if (!writer->version) { + goto fail; + } + } + + // Open writer. + fopt = fstrm_file_options_init(); + fstrm_file_options_set_file_path(fopt, file_path); + wopt = fstrm_writer_options_init(); + fstrm_writer_options_add_content_type(wopt, + (const uint8_t *) DNSTAP_CONTENT_TYPE, + strlen(DNSTAP_CONTENT_TYPE)); + writer->fw = fstrm_file_writer_init(fopt, wopt); + fstrm_file_options_destroy(&fopt); + fstrm_writer_options_destroy(&wopt); + if (writer->fw == NULL) { + goto fail; + } + + res = fstrm_writer_open(writer->fw); + if (res != fstrm_res_success) { + goto fail; + } + + return writer; +fail: + dt_writer_free(writer); + return NULL; +} + +void dt_writer_free(dt_writer_t *writer) +{ + if (writer == NULL) { + return; + } + + fstrm_writer_destroy(&writer->fw); + free(writer->version); + free(writer); +} + +int dt_writer_write(dt_writer_t *writer, const ProtobufCMessage *msg) +{ + Dnstap__Dnstap dnstap = DNSTAP__DNSTAP__INIT; + size_t len; + uint8_t *data; + + if (writer->fw == NULL) { + return KNOT_EOK; + } + + // Only handle dnstap/Message. + assert(msg->descriptor == &dnstap__message__descriptor); + + // Fill out 'dnstap'. + if (writer->version) { + dnstap.version.data = writer->version; + dnstap.version.len = writer->len_version; + dnstap.has_version = 1; + } + dnstap.type = DNSTAP__DNSTAP__TYPE__MESSAGE; + dnstap.message = (Dnstap__Message *)msg; + + // Serialize the dnstap frame. + if (!dt_pack(&dnstap, &data, &len)) { + return KNOT_ENOMEM; + } + + // Write the dnstap frame to the output stream. + if (fstrm_writer_write(writer->fw, data, len) != fstrm_res_success) { + return KNOT_ERROR; + } + + // Cleanup. + free(data); + + return KNOT_EOK; +} diff --git a/src/contrib/dnstap/writer.h b/src/contrib/dnstap/writer.h new file mode 100644 index 0000000..4fe7326 --- /dev/null +++ b/src/contrib/dnstap/writer.h @@ -0,0 +1,70 @@ +/* Copyright (C) 2014 Farsight Security, Inc. <software@farsightsecurity.com> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \author Robert Edmonds <edmonds@fsi.io> + * + * \brief Dnstap file writer. + */ + +#pragma once + +#include <fstrm.h> +#include <protobuf-c/protobuf-c.h> + +/*! \brief Structure for dnstap file writer. */ +typedef struct { + /*!< Output writer. */ + struct fstrm_writer *fw; + + /*!< dnstap "version" field. */ + void *version; + + /*!< length of dnstap "version" field. */ + size_t len_version; +} dt_writer_t; + +/*! + * \brief Creates dnstap file writer structure. + * + * \param file_path Name of file to write output to. + * \param version Version string of software. May be NULL. + * + * \retval writer if success. + * \retval NULL if error. + */ +dt_writer_t* dt_writer_create(const char *file_path, const char *version); + +/*! + * \brief Finish writing dnstap file writer and free resources. + * + * \param writer dnstap file writer structure. + */ +void dt_writer_free(dt_writer_t *writer); + +/*! + * \brief Write a protobuf to the dnstap file writer. + * + * Supported protobuf types for the 'msg' parameter: + * \c Dnstap__Message + * + * \param writer dnstap file writer structure. + * \param msg dnstap protobuf. Must be a supported type. + * + * \retval KNOT_EOK + * \retval KNOT_EINVAL + * \retval KNOT_ENOMEM + */ +int dt_writer_write(dt_writer_t *writer, const ProtobufCMessage *msg); diff --git a/src/contrib/dynarray.h b/src/contrib/dynarray.h new file mode 100644 index 0000000..79eab31 --- /dev/null +++ b/src/contrib/dynarray.h @@ -0,0 +1,123 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \brief Simple write-once allocation-optimal dynamic array. + * + * Include it into your .c file + * + * prefix - identifier prefix, e.g. ptr -> struct ptr_dynarray, ptr_dynarray_add(), ... + * ntype - data type to be stored. Let it be a number, pointer or small struct + * initial_capacity - how many data items will be allocated on stac and copied with assignment + * + * prefix_dynarray_add() - add a data item + * prefix_dynarray_fix() - call EVERYTIME the array is copied from some already invalid stack + * prefix_dynarray_free() - call EVERYTIME you dismiss all copies of the array + * + */ + +#include <stdlib.h> +#include <assert.h> + +#pragma once + +#define DYNARRAY_VISIBILITY_STATIC static +#define DYNARRAY_VISIBILITY_PUBLIC +#define DYNARRAY_VISIBILITY_LIBRARY __public__ + +#define dynarray_declare(prefix, ntype, visibility, initial_capacity) \ + typedef struct prefix ## _dynarray { \ + ssize_t capacity; \ + ssize_t size; \ + ntype *(*arr)(struct prefix ## _dynarray *dynarray); \ + ntype init[initial_capacity]; \ + ntype *_arr; \ + } prefix ## _dynarray_t; \ + \ + visibility ntype *prefix ## _dynarray_arr(prefix ## _dynarray_t *dynarray); \ + visibility void prefix ## _dynarray_add(prefix ## _dynarray_t *dynarray, \ + ntype const *to_add); \ + visibility void prefix ## _dynarray_free(prefix ## _dynarray_t *dynarray); + +#define dynarray_foreach(prefix, ntype, ptr, array) \ + for (ntype *ptr = prefix ## _dynarray_arr(&(array)); \ + ptr < prefix ## _dynarray_arr(&(array)) + (array).size; ptr++) + +#define dynarray_define(prefix, ntype, visibility) \ + \ + static void prefix ## _dynarray_free__(struct prefix ## _dynarray *dynarray) \ + { \ + if (dynarray->capacity > sizeof(dynarray->init) / sizeof(*dynarray->init)) { \ + free(dynarray->_arr); \ + } \ + } \ + \ + __attribute__((unused)) \ + visibility ntype *prefix ## _dynarray_arr(struct prefix ## _dynarray *dynarray) \ + { \ + assert(dynarray->size <= dynarray->capacity); \ + return (dynarray->capacity <= sizeof(dynarray->init) / sizeof(*dynarray->init) ? \ + dynarray->init : dynarray->_arr); \ + } \ + \ + static ntype *prefix ## _dynarray_arr_init__(struct prefix ## _dynarray *dynarray) \ + { \ + assert(dynarray->capacity == sizeof(dynarray->init) / sizeof(*dynarray->init)); \ + return dynarray->init; \ + } \ + \ + static ntype *prefix ## _dynarray_arr_arr__(struct prefix ## _dynarray *dynarray) \ + { \ + assert(dynarray->capacity > sizeof(dynarray->init) / sizeof(*dynarray->init)); \ + return dynarray->_arr; \ + } \ + \ + __attribute__((unused)) \ + visibility void prefix ## _dynarray_add(struct prefix ## _dynarray *dynarray, \ + ntype const *to_add) \ + { \ + if (dynarray->capacity < 0) { \ + return; \ + } \ + if (dynarray->capacity == 0) { \ + dynarray->capacity = sizeof(dynarray->init) / sizeof(*dynarray->init); \ + dynarray->arr = prefix ## _dynarray_arr_init__; \ + } \ + if (dynarray->size >= dynarray->capacity) { \ + ssize_t new_capacity = dynarray->capacity * 2 + 1; \ + ntype *new_arr = calloc(new_capacity, sizeof(ntype)); \ + if (new_arr == NULL) { \ + prefix ## _dynarray_free__(dynarray); \ + dynarray->capacity = dynarray->size = -1; \ + return; \ + } \ + if (dynarray->capacity > 0) { \ + memcpy(new_arr, prefix ## _dynarray_arr(dynarray), \ + dynarray->capacity * sizeof(ntype)); \ + } \ + prefix ## _dynarray_free__(dynarray); \ + dynarray->_arr = new_arr; \ + dynarray->capacity = new_capacity; \ + dynarray->arr = prefix ## _dynarray_arr_arr__; \ + } \ + prefix ## _dynarray_arr(dynarray)[dynarray->size++] = *to_add; \ + } \ + \ + __attribute__((unused)) \ + visibility void prefix ## _dynarray_free(struct prefix ## _dynarray *dynarray) \ + { \ + prefix ## _dynarray_free__(dynarray); \ + memset(dynarray, 0, sizeof(*dynarray)); \ + } diff --git a/src/contrib/files.c b/src/contrib/files.c new file mode 100644 index 0000000..3181386 --- /dev/null +++ b/src/contrib/files.c @@ -0,0 +1,134 @@ +/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <assert.h> +#include <dirent.h> +#include <fcntl.h> +#include <ftw.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <unistd.h> + +#include "contrib/files.h" +#include "contrib/string.h" +#include "libknot/errcode.h" + +static int remove_file(const char *path, const struct stat *stat, int type, struct FTW *ftw) +{ + (void)stat; + (void)ftw; + if (type == FTW_DP) { + return rmdir(path); + } else { + return unlink(path); + } +} + +bool remove_path(const char *path) +{ + return (0 == nftw(path, remove_file, 1, FTW_DEPTH | FTW_PHYS)); +} + +int make_dir(const char *path, mode_t mode, bool ignore_existing) +{ + if (mkdir(path, mode) == 0) { + return KNOT_EOK; + } + + if (!ignore_existing || errno != EEXIST) { + return knot_map_errno(); + } + + assert(errno == EEXIST); + + struct stat st = { 0 }; + if (stat(path, &st) != 0) { + return knot_map_errno(); + } + + if (!S_ISDIR(st.st_mode)) { + return knot_map_errno_code(ENOTDIR); + } + + return KNOT_EOK; +} + +int make_path(const char *path, mode_t mode) +{ + if (path == NULL) { + return KNOT_EINVAL; + } + + char *dir = strdup(path); + if (dir == NULL) { + return KNOT_ENOMEM; + } + + for (char *p = strchr(dir + 1, '/'); p != NULL; p = strchr(p + 1, '/')) { + *p = '\0'; + if (mkdir(dir, mode) == -1 && errno != EEXIST) { + free(dir); + return knot_map_errno(); + } + *p = '/'; + } + + free(dir); + + return KNOT_EOK; +} + +int open_tmp_file(const char *path, char **tmp_name, FILE **file, mode_t mode) +{ + int ret; + + *tmp_name = sprintf_alloc("%s.XXXXXX", path); + if (*tmp_name == NULL) { + ret = KNOT_ENOMEM; + goto open_tmp_failed; + } + + int fd = mkstemp(*tmp_name); + if (fd < 0) { + ret = knot_map_errno(); + goto open_tmp_failed; + } + + if (fchmod(fd, mode) != 0) { + ret = knot_map_errno(); + close(fd); + unlink(*tmp_name); + goto open_tmp_failed; + } + + *file = fdopen(fd, "w"); + if (*file == NULL) { + ret = knot_map_errno(); + close(fd); + unlink(*tmp_name); + goto open_tmp_failed; + } + + return KNOT_EOK; +open_tmp_failed: + free(*tmp_name); + *tmp_name = NULL; + *file = NULL; + + assert(ret != KNOT_EOK); + return ret; +} diff --git a/src/contrib/files.h b/src/contrib/files.h new file mode 100644 index 0000000..c4c681f --- /dev/null +++ b/src/contrib/files.h @@ -0,0 +1,43 @@ +/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include <stdbool.h> +#include <stdio.h> +#include <sys/types.h> + +/*! + * \brief Delete file or directory (recursive). + * + * \return true on success, false when one or more files failed to be removed. + */ +bool remove_path(const char *path); + +/*! + * Equivalent to mkdir(2), can succeed if the directory already exists. + */ +int make_dir(const char *path, mode_t mode, bool ignore_existing); + +/*! + * Makes a directory part of the path with all parent directories if not exist. + */ +int make_path(const char *path, mode_t mode); + +/*! + * Creates and opens for writing a temporary file based on given path. + */ +int open_tmp_file(const char *path, char **tmp_name, FILE **file, mode_t mode); diff --git a/src/contrib/getline.c b/src/contrib/getline.c new file mode 100644 index 0000000..074c511 --- /dev/null +++ b/src/contrib/getline.c @@ -0,0 +1,60 @@ +/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +// FreeBSD POSIX2008 getline +#ifndef _WITH_GETLINE +#define _WITH_GETLINE +#endif + +#include "contrib/getline.h" + +#include <stdio.h> // getline or fgetln +#include <stdlib.h> // free +#include <string.h> // memcpy + +ssize_t knot_getline(char **lineptr, size_t *n, FILE *stream) +{ +#ifdef HAVE_GETLINE + return getline(lineptr, n, stream); +#else +#ifdef HAVE_FGETLN + size_t length = 0; + char *buffer = fgetln(stream, &length); + if (buffer == NULL) { + return -1; + } + + /* NOTE: Function fgetln doesn't return terminated string! + * Output buffer from the fgetln can't be freed. + */ + + // If the output buffer is not specified or is small, extend it. + if (*lineptr == NULL || *n <= length) { + char *tmp = realloc(*lineptr, length + 1); + if (tmp == NULL) { + return -1; + } + *lineptr = tmp; + *n = length + 1; + } + + memcpy(*lineptr, buffer, length); + (*lineptr)[length] = '\0'; + + return length; +#endif +#endif +} diff --git a/src/contrib/getline.h b/src/contrib/getline.h new file mode 100644 index 0000000..11c7fe3 --- /dev/null +++ b/src/contrib/getline.h @@ -0,0 +1,38 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \brief Multiplatform getline wrapper. + */ + +#pragma once + +#include <stdio.h> +#include <sys/types.h> + +/*! + * \brief Reads a line from a stream. + * + * This function has the same semantics as POSIX.1-2008 getline(). + * If necessary, the output buffer will be allocated/reallocated. + * + * \param lineptr Output buffer. + * \param n Output buffer size. + * \param stream Input stream. + * + * \retval Number of characters read, including new line delimiter, + * not including terminating. -1 on error or EOF. + */ +ssize_t knot_getline(char **lineptr, size_t *n, FILE *stream); diff --git a/src/contrib/licenses/0BSD b/src/contrib/licenses/0BSD new file mode 100644 index 0000000..56c5528 --- /dev/null +++ b/src/contrib/licenses/0BSD @@ -0,0 +1,12 @@ +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + diff --git a/src/contrib/licenses/BSD-3-Clause b/src/contrib/licenses/BSD-3-Clause new file mode 100644 index 0000000..8041f21 --- /dev/null +++ b/src/contrib/licenses/BSD-3-Clause @@ -0,0 +1,21 @@ +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/contrib/licenses/LGPL-2.0 b/src/contrib/licenses/LGPL-2.0 new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/src/contrib/licenses/LGPL-2.0 @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/src/contrib/licenses/OLDAP-2.8 b/src/contrib/licenses/OLDAP-2.8 new file mode 100644 index 0000000..05ad757 --- /dev/null +++ b/src/contrib/licenses/OLDAP-2.8 @@ -0,0 +1,47 @@ +The OpenLDAP Public License + Version 2.8, 17 August 2003 + +Redistribution and use of this software and associated documentation +("Software"), with or without modification, are permitted provided +that the following conditions are met: + +1. Redistributions in source form must retain copyright statements + and notices, + +2. Redistributions in binary form must reproduce applicable copyright + statements and notices, this list of conditions, and the following + disclaimer in the documentation and/or other materials provided + with the distribution, and + +3. Redistributions must contain a verbatim copy of this document. + +The OpenLDAP Foundation may revise this license from time to time. +Each revision is distinguished by a version number. You may use +this Software under terms of this license revision or under the +terms of any subsequent revision of the license. + +THIS SOFTWARE IS PROVIDED BY THE OPENLDAP FOUNDATION AND ITS +CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL THE OPENLDAP FOUNDATION, ITS CONTRIBUTORS, OR THE AUTHOR(S) +OR OWNER(S) OF THE SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +The names of the authors and copyright holders must not be used in +advertising or otherwise to promote the sale, use or other dealing +in this Software without specific, written prior permission. Title +to copyright in this Software shall at all times remain with copyright +holders. + +OpenLDAP is a registered trademark of the OpenLDAP Foundation. + +Copyright 1999-2003 The OpenLDAP Foundation, Redwood City, +California, USA. All Rights Reserved. Permission to copy and +distribute verbatim copies of this document is granted. diff --git a/src/contrib/lmdb/LICENSE b/src/contrib/lmdb/LICENSE new file mode 100644 index 0000000..66210f0 --- /dev/null +++ b/src/contrib/lmdb/LICENSE @@ -0,0 +1 @@ +../licenses/OLDAP-2.8
\ No newline at end of file diff --git a/src/contrib/lmdb/lmdb.h b/src/contrib/lmdb/lmdb.h new file mode 100644 index 0000000..32a278e --- /dev/null +++ b/src/contrib/lmdb/lmdb.h @@ -0,0 +1,1604 @@ +/** @file lmdb.h + * @brief Lightning memory-mapped database library + * + * @mainpage Lightning Memory-Mapped Database Manager (LMDB) + * + * @section intro_sec Introduction + * LMDB is a Btree-based database management library modeled loosely on the + * BerkeleyDB API, but much simplified. The entire database is exposed + * in a memory map, and all data fetches return data directly + * from the mapped memory, so no malloc's or memcpy's occur during + * data fetches. As such, the library is extremely simple because it + * requires no page caching layer of its own, and it is extremely high + * performance and memory-efficient. It is also fully transactional with + * full ACID semantics, and when the memory map is read-only, the + * database integrity cannot be corrupted by stray pointer writes from + * application code. + * + * The library is fully thread-aware and supports concurrent read/write + * access from multiple processes and threads. Data pages use a copy-on- + * write strategy so no active data pages are ever overwritten, which + * also provides resistance to corruption and eliminates the need of any + * special recovery procedures after a system crash. Writes are fully + * serialized; only one write transaction may be active at a time, which + * guarantees that writers can never deadlock. The database structure is + * multi-versioned so readers run with no locks; writers cannot block + * readers, and readers don't block writers. + * + * Unlike other well-known database mechanisms which use either write-ahead + * transaction logs or append-only data writes, LMDB requires no maintenance + * during operation. Both write-ahead loggers and append-only databases + * require periodic checkpointing and/or compaction of their log or database + * files otherwise they grow without bound. LMDB tracks free pages within + * the database and re-uses them for new write operations, so the database + * size does not grow without bound in normal use. + * + * The memory map can be used as a read-only or read-write map. It is + * read-only by default as this provides total immunity to corruption. + * Using read-write mode offers much higher write performance, but adds + * the possibility for stray application writes thru pointers to silently + * corrupt the database. Of course if your application code is known to + * be bug-free (...) then this is not an issue. + * + * If this is your first time using a transactional embedded key/value + * store, you may find the \ref starting page to be helpful. + * + * @section caveats_sec Caveats + * Troubleshooting the lock file, plus semaphores on BSD systems: + * + * - A broken lockfile can cause sync issues. + * Stale reader transactions left behind by an aborted program + * cause further writes to grow the database quickly, and + * stale locks can block further operation. + * + * Fix: Check for stale readers periodically, using the + * #mdb_reader_check function or the \ref mdb_stat_1 "mdb_stat" tool. + * Stale writers will be cleared automatically on some systems: + * - Windows - automatic + * - Linux, systems using POSIX mutexes with Robust option - automatic + * - not on BSD, systems using POSIX semaphores. + * Otherwise just make all programs using the database close it; + * the lockfile is always reset on first open of the environment. + * + * - On BSD systems or others configured with MDB_USE_POSIX_SEM, + * startup can fail due to semaphores owned by another userid. + * + * Fix: Open and close the database as the user which owns the + * semaphores (likely last user) or as root, while no other + * process is using the database. + * + * Restrictions/caveats (in addition to those listed for some functions): + * + * - Only the database owner should normally use the database on + * BSD systems or when otherwise configured with MDB_USE_POSIX_SEM. + * Multiple users can cause startup to fail later, as noted above. + * + * - There is normally no pure read-only mode, since readers need write + * access to locks and lock file. Exceptions: On read-only filesystems + * or with the #MDB_NOLOCK flag described under #mdb_env_open(). + * + * - An LMDB configuration will often reserve considerable \b unused + * memory address space and maybe file size for future growth. + * This does not use actual memory or disk space, but users may need + * to understand the difference so they won't be scared off. + * + * - By default, in versions before 0.9.10, unused portions of the data + * file might receive garbage data from memory freed by other code. + * (This does not happen when using the #MDB_WRITEMAP flag.) As of + * 0.9.10 the default behavior is to initialize such memory before + * writing to the data file. Since there may be a slight performance + * cost due to this initialization, applications may disable it using + * the #MDB_NOMEMINIT flag. Applications handling sensitive data + * which must not be written should not use this flag. This flag is + * irrelevant when using #MDB_WRITEMAP. + * + * - A thread can only use one transaction at a time, plus any child + * transactions. Each transaction belongs to one thread. See below. + * The #MDB_NOTLS flag changes this for read-only transactions. + * + * - Use an MDB_env* in the process which opened it, not after fork(). + * + * - Do not have open an LMDB database twice in the same process at + * the same time. Not even from a plain open() call - close()ing it + * breaks fcntl() advisory locking. (It is OK to reopen it after + * fork() - exec*(), since the lockfile has FD_CLOEXEC set.) + * + * - Avoid long-lived transactions. Read transactions prevent + * reuse of pages freed by newer write transactions, thus the + * database can grow quickly. Write transactions prevent + * other write transactions, since writes are serialized. + * + * - Avoid suspending a process with active transactions. These + * would then be "long-lived" as above. Also read transactions + * suspended when writers commit could sometimes see wrong data. + * + * ...when several processes can use a database concurrently: + * + * - Avoid aborting a process with an active transaction. + * The transaction becomes "long-lived" as above until a check + * for stale readers is performed or the lockfile is reset, + * since the process may not remove it from the lockfile. + * + * This does not apply to write transactions if the system clears + * stale writers, see above. + * + * - If you do that anyway, do a periodic check for stale readers. Or + * close the environment once in a while, so the lockfile can get reset. + * + * - Do not use LMDB databases on remote filesystems, even between + * processes on the same host. This breaks flock() on some OSes, + * possibly memory map sync, and certainly sync between programs + * on different hosts. + * + * - Opening a database can fail if another process is opening or + * closing it at exactly the same time. + * + * @author Howard Chu, Symas Corporation. + * + * @copyright Copyright 2011-2018 Howard Chu, Symas Corp. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + * + * @par Derived From: + * This code is derived from btree.c written by Martin Hedenfalk. + * + * Copyright (c) 2009, 2010 Martin Hedenfalk <martin@bzero.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef _LMDB_H_ +#define _LMDB_H_ + +#include <sys/types.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** Unix permissions for creating files, or dummy definition for Windows */ +#ifdef _MSC_VER +typedef int mdb_mode_t; +#else +typedef mode_t mdb_mode_t; +#endif + +/** An abstraction for a file handle. + * On POSIX systems file handles are small integers. On Windows + * they're opaque pointers. + */ +#ifdef _WIN32 +typedef void *mdb_filehandle_t; +#else +typedef int mdb_filehandle_t; +#endif + +/** @defgroup mdb LMDB API + * @{ + * @brief OpenLDAP Lightning Memory-Mapped Database Manager + */ +/** @defgroup Version Version Macros + * @{ + */ +/** Library major version */ +#define MDB_VERSION_MAJOR 0 +/** Library minor version */ +#define MDB_VERSION_MINOR 9 +/** Library patch version */ +#define MDB_VERSION_PATCH 22 + +/** Combine args a,b,c into a single integer for easy version comparisons */ +#define MDB_VERINT(a,b,c) (((a) << 24) | ((b) << 16) | (c)) + +/** The full library version as a single integer */ +#define MDB_VERSION_FULL \ + MDB_VERINT(MDB_VERSION_MAJOR,MDB_VERSION_MINOR,MDB_VERSION_PATCH) + +/** The release date of this library version */ +#define MDB_VERSION_DATE "March 21, 2018" + +/** A stringifier for the version info */ +#define MDB_VERSTR(a,b,c,d) "LMDB " #a "." #b "." #c ": (" d ")" + +/** A helper for the stringifier macro */ +#define MDB_VERFOO(a,b,c,d) MDB_VERSTR(a,b,c,d) + +/** The full library version as a C string */ +#define MDB_VERSION_STRING \ + MDB_VERFOO(MDB_VERSION_MAJOR,MDB_VERSION_MINOR,MDB_VERSION_PATCH,MDB_VERSION_DATE) +/** @} */ + +/** @brief Opaque structure for a database environment. + * + * A DB environment supports multiple databases, all residing in the same + * shared-memory map. + */ +typedef struct MDB_env MDB_env; + +/** @brief Opaque structure for a transaction handle. + * + * All database operations require a transaction handle. Transactions may be + * read-only or read-write. + */ +typedef struct MDB_txn MDB_txn; + +/** @brief A handle for an individual database in the DB environment. */ +typedef unsigned int MDB_dbi; + +/** @brief Opaque structure for navigating through a database */ +typedef struct MDB_cursor MDB_cursor; + +/** @brief Generic structure used for passing keys and data in and out + * of the database. + * + * Values returned from the database are valid only until a subsequent + * update operation, or the end of the transaction. Do not modify or + * free them, they commonly point into the database itself. + * + * Key sizes must be between 1 and #mdb_env_get_maxkeysize() inclusive. + * The same applies to data sizes in databases with the #MDB_DUPSORT flag. + * Other data items can in theory be from 0 to 0xffffffff bytes long. + */ +typedef struct MDB_val { + size_t mv_size; /**< size of the data item */ + void *mv_data; /**< address of the data item */ +} MDB_val; + +/** @brief A callback function used to compare two keys in a database */ +typedef int (MDB_cmp_func)(const MDB_val *a, const MDB_val *b); + +/** @brief A callback function used to relocate a position-dependent data item + * in a fixed-address database. + * + * The \b newptr gives the item's desired address in + * the memory map, and \b oldptr gives its previous address. The item's actual + * data resides at the address in \b item. This callback is expected to walk + * through the fields of the record in \b item and modify any + * values based at the \b oldptr address to be relative to the \b newptr address. + * @param[in,out] item The item that is to be relocated. + * @param[in] oldptr The previous address. + * @param[in] newptr The new address to relocate to. + * @param[in] relctx An application-provided context, set by #mdb_set_relctx(). + * @todo This feature is currently unimplemented. + */ +typedef void (MDB_rel_func)(MDB_val *item, void *oldptr, void *newptr, void *relctx); + +/** @defgroup mdb_env Environment Flags + * @{ + */ + /** mmap at a fixed address (experimental) */ +#define MDB_FIXEDMAP 0x01 + /** no environment directory */ +#define MDB_NOSUBDIR 0x4000 + /** don't fsync after commit */ +#define MDB_NOSYNC 0x10000 + /** read only */ +#define MDB_RDONLY 0x20000 + /** don't fsync metapage after commit */ +#define MDB_NOMETASYNC 0x40000 + /** use writable mmap */ +#define MDB_WRITEMAP 0x80000 + /** use asynchronous msync when #MDB_WRITEMAP is used */ +#define MDB_MAPASYNC 0x100000 + /** tie reader locktable slots to #MDB_txn objects instead of to threads */ +#define MDB_NOTLS 0x200000 + /** don't do any locking, caller must manage their own locks */ +#define MDB_NOLOCK 0x400000 + /** don't do readahead (no effect on Windows) */ +#define MDB_NORDAHEAD 0x800000 + /** don't initialize malloc'd memory before writing to datafile */ +#define MDB_NOMEMINIT 0x1000000 +/** @} */ + +/** @defgroup mdb_dbi_open Database Flags + * @{ + */ + /** use reverse string keys */ +#define MDB_REVERSEKEY 0x02 + /** use sorted duplicates */ +#define MDB_DUPSORT 0x04 + /** numeric keys in native byte order: either unsigned int or size_t. + * The keys must all be of the same size. */ +#define MDB_INTEGERKEY 0x08 + /** with #MDB_DUPSORT, sorted dup items have fixed size */ +#define MDB_DUPFIXED 0x10 + /** with #MDB_DUPSORT, dups are #MDB_INTEGERKEY-style integers */ +#define MDB_INTEGERDUP 0x20 + /** with #MDB_DUPSORT, use reverse string dups */ +#define MDB_REVERSEDUP 0x40 + /** create DB if not already existing */ +#define MDB_CREATE 0x40000 +/** @} */ + +/** @defgroup mdb_put Write Flags + * @{ + */ +/** For put: Don't write if the key already exists. */ +#define MDB_NOOVERWRITE 0x10 +/** Only for #MDB_DUPSORT<br> + * For put: don't write if the key and data pair already exist.<br> + * For mdb_cursor_del: remove all duplicate data items. + */ +#define MDB_NODUPDATA 0x20 +/** For mdb_cursor_put: overwrite the current key/data pair */ +#define MDB_CURRENT 0x40 +/** For put: Just reserve space for data, don't copy it. Return a + * pointer to the reserved space. + */ +#define MDB_RESERVE 0x10000 +/** Data is being appended, don't split full pages. */ +#define MDB_APPEND 0x20000 +/** Duplicate data is being appended, don't split full pages. */ +#define MDB_APPENDDUP 0x40000 +/** Store multiple data items in one call. Only for #MDB_DUPFIXED. */ +#define MDB_MULTIPLE 0x80000 +/* @} */ + +/** @defgroup mdb_copy Copy Flags + * @{ + */ +/** Compacting copy: Omit free space from copy, and renumber all + * pages sequentially. + */ +#define MDB_CP_COMPACT 0x01 +/* @} */ + +/** @brief Cursor Get operations. + * + * This is the set of all operations for retrieving data + * using a cursor. + */ +typedef enum MDB_cursor_op { + MDB_FIRST, /**< Position at first key/data item */ + MDB_FIRST_DUP, /**< Position at first data item of current key. + Only for #MDB_DUPSORT */ + MDB_GET_BOTH, /**< Position at key/data pair. Only for #MDB_DUPSORT */ + MDB_GET_BOTH_RANGE, /**< position at key, nearest data. Only for #MDB_DUPSORT */ + MDB_GET_CURRENT, /**< Return key/data at current cursor position */ + MDB_GET_MULTIPLE, /**< Return key and up to a page of duplicate data items + from current cursor position. Move cursor to prepare + for #MDB_NEXT_MULTIPLE. Only for #MDB_DUPFIXED */ + MDB_LAST, /**< Position at last key/data item */ + MDB_LAST_DUP, /**< Position at last data item of current key. + Only for #MDB_DUPSORT */ + MDB_NEXT, /**< Position at next data item */ + MDB_NEXT_DUP, /**< Position at next data item of current key. + Only for #MDB_DUPSORT */ + MDB_NEXT_MULTIPLE, /**< Return key and up to a page of duplicate data items + from next cursor position. Move cursor to prepare + for #MDB_NEXT_MULTIPLE. Only for #MDB_DUPFIXED */ + MDB_NEXT_NODUP, /**< Position at first data item of next key */ + MDB_PREV, /**< Position at previous data item */ + MDB_PREV_DUP, /**< Position at previous data item of current key. + Only for #MDB_DUPSORT */ + MDB_PREV_NODUP, /**< Position at last data item of previous key */ + MDB_SET, /**< Position at specified key */ + MDB_SET_KEY, /**< Position at specified key, return key + data */ + MDB_SET_RANGE, /**< Position at first key greater than or equal to specified key. */ + MDB_PREV_MULTIPLE /**< Position at previous page and return key and up to + a page of duplicate data items. Only for #MDB_DUPFIXED */ +} MDB_cursor_op; + +/** @defgroup errors Return Codes + * + * BerkeleyDB uses -30800 to -30999, we'll go under them + * @{ + */ + /** Successful result */ +#define MDB_SUCCESS 0 + /** key/data pair already exists */ +#define MDB_KEYEXIST (-30799) + /** key/data pair not found (EOF) */ +#define MDB_NOTFOUND (-30798) + /** Requested page not found - this usually indicates corruption */ +#define MDB_PAGE_NOTFOUND (-30797) + /** Located page was wrong type */ +#define MDB_CORRUPTED (-30796) + /** Update of meta page failed or environment had fatal error */ +#define MDB_PANIC (-30795) + /** Environment version mismatch */ +#define MDB_VERSION_MISMATCH (-30794) + /** File is not a valid LMDB file */ +#define MDB_INVALID (-30793) + /** Environment mapsize reached */ +#define MDB_MAP_FULL (-30792) + /** Environment maxdbs reached */ +#define MDB_DBS_FULL (-30791) + /** Environment maxreaders reached */ +#define MDB_READERS_FULL (-30790) + /** Too many TLS keys in use - Windows only */ +#define MDB_TLS_FULL (-30789) + /** Txn has too many dirty pages */ +#define MDB_TXN_FULL (-30788) + /** Cursor stack too deep - internal error */ +#define MDB_CURSOR_FULL (-30787) + /** Page has not enough space - internal error */ +#define MDB_PAGE_FULL (-30786) + /** Database contents grew beyond environment mapsize */ +#define MDB_MAP_RESIZED (-30785) + /** Operation and DB incompatible, or DB type changed. This can mean: + * <ul> + * <li>The operation expects an #MDB_DUPSORT / #MDB_DUPFIXED database. + * <li>Opening a named DB when the unnamed DB has #MDB_DUPSORT / #MDB_INTEGERKEY. + * <li>Accessing a data record as a database, or vice versa. + * <li>The database was dropped and recreated with different flags. + * </ul> + */ +#define MDB_INCOMPATIBLE (-30784) + /** Invalid reuse of reader locktable slot */ +#define MDB_BAD_RSLOT (-30783) + /** Transaction must abort, has a child, or is invalid */ +#define MDB_BAD_TXN (-30782) + /** Unsupported size of key/DB name/data, or wrong DUPFIXED size */ +#define MDB_BAD_VALSIZE (-30781) + /** The specified DBI was changed unexpectedly */ +#define MDB_BAD_DBI (-30780) + /** The last defined error code */ +#define MDB_LAST_ERRCODE MDB_BAD_DBI +/** @} */ + +/** @brief Statistics for a database in the environment */ +typedef struct MDB_stat { + unsigned int ms_psize; /**< Size of a database page. + This is currently the same for all databases. */ + unsigned int ms_depth; /**< Depth (height) of the B-tree */ + size_t ms_branch_pages; /**< Number of internal (non-leaf) pages */ + size_t ms_leaf_pages; /**< Number of leaf pages */ + size_t ms_overflow_pages; /**< Number of overflow pages */ + size_t ms_entries; /**< Number of data items */ +} MDB_stat; + +/** @brief Information about the environment */ +typedef struct MDB_envinfo { + void *me_mapaddr; /**< Address of map, if fixed */ + size_t me_mapsize; /**< Size of the data memory map */ + size_t me_last_pgno; /**< ID of the last used page */ + size_t me_last_txnid; /**< ID of the last committed transaction */ + unsigned int me_maxreaders; /**< max reader slots in the environment */ + unsigned int me_numreaders; /**< max reader slots used in the environment */ +} MDB_envinfo; + + /** @brief Return the LMDB library version information. + * + * @param[out] major if non-NULL, the library major version number is copied here + * @param[out] minor if non-NULL, the library minor version number is copied here + * @param[out] patch if non-NULL, the library patch version number is copied here + * @retval "version string" The library version as a string + */ +char *mdb_version(int *major, int *minor, int *patch); + + /** @brief Return a string describing a given error code. + * + * This function is a superset of the ANSI C X3.159-1989 (ANSI C) strerror(3) + * function. If the error code is greater than or equal to 0, then the string + * returned by the system function strerror(3) is returned. If the error code + * is less than 0, an error string corresponding to the LMDB library error is + * returned. See @ref errors for a list of LMDB-specific error codes. + * @param[in] err The error code + * @retval "error message" The description of the error + */ +char *mdb_strerror(int err); + + /** @brief Create an LMDB environment handle. + * + * This function allocates memory for a #MDB_env structure. To release + * the allocated memory and discard the handle, call #mdb_env_close(). + * Before the handle may be used, it must be opened using #mdb_env_open(). + * Various other options may also need to be set before opening the handle, + * e.g. #mdb_env_set_mapsize(), #mdb_env_set_maxreaders(), #mdb_env_set_maxdbs(), + * depending on usage requirements. + * @param[out] env The address where the new handle will be stored + * @return A non-zero error value on failure and 0 on success. + */ +int mdb_env_create(MDB_env **env); + + /** @brief Open an environment handle. + * + * If this function fails, #mdb_env_close() must be called to discard the #MDB_env handle. + * @param[in] env An environment handle returned by #mdb_env_create() + * @param[in] path The directory in which the database files reside. This + * directory must already exist and be writable. + * @param[in] flags Special options for this environment. This parameter + * must be set to 0 or by bitwise OR'ing together one or more of the + * values described here. + * Flags set by mdb_env_set_flags() are also used. + * <ul> + * <li>#MDB_FIXEDMAP + * use a fixed address for the mmap region. This flag must be specified + * when creating the environment, and is stored persistently in the environment. + * If successful, the memory map will always reside at the same virtual address + * and pointers used to reference data items in the database will be constant + * across multiple invocations. This option may not always work, depending on + * how the operating system has allocated memory to shared libraries and other uses. + * The feature is highly experimental. + * <li>#MDB_NOSUBDIR + * By default, LMDB creates its environment in a directory whose + * pathname is given in \b path, and creates its data and lock files + * under that directory. With this option, \b path is used as-is for + * the database main data file. The database lock file is the \b path + * with "-lock" appended. + * <li>#MDB_RDONLY + * Open the environment in read-only mode. No write operations will be + * allowed. LMDB will still modify the lock file - except on read-only + * filesystems, where LMDB does not use locks. + * <li>#MDB_WRITEMAP + * Use a writeable memory map unless MDB_RDONLY is set. This uses + * fewer mallocs but loses protection from application bugs + * like wild pointer writes and other bad updates into the database. + * This may be slightly faster for DBs that fit entirely in RAM, but + * is slower for DBs larger than RAM. + * Incompatible with nested transactions. + * Do not mix processes with and without MDB_WRITEMAP on the same + * environment. This can defeat durability (#mdb_env_sync etc). + * <li>#MDB_NOMETASYNC + * Flush system buffers to disk only once per transaction, omit the + * metadata flush. Defer that until the system flushes files to disk, + * or next non-MDB_RDONLY commit or #mdb_env_sync(). This optimization + * maintains database integrity, but a system crash may undo the last + * committed transaction. I.e. it preserves the ACI (atomicity, + * consistency, isolation) but not D (durability) database property. + * This flag may be changed at any time using #mdb_env_set_flags(). + * <li>#MDB_NOSYNC + * Don't flush system buffers to disk when committing a transaction. + * This optimization means a system crash can corrupt the database or + * lose the last transactions if buffers are not yet flushed to disk. + * The risk is governed by how often the system flushes dirty buffers + * to disk and how often #mdb_env_sync() is called. However, if the + * filesystem preserves write order and the #MDB_WRITEMAP flag is not + * used, transactions exhibit ACI (atomicity, consistency, isolation) + * properties and only lose D (durability). I.e. database integrity + * is maintained, but a system crash may undo the final transactions. + * Note that (#MDB_NOSYNC | #MDB_WRITEMAP) leaves the system with no + * hint for when to write transactions to disk, unless #mdb_env_sync() + * is called. (#MDB_MAPASYNC | #MDB_WRITEMAP) may be preferable. + * This flag may be changed at any time using #mdb_env_set_flags(). + * <li>#MDB_MAPASYNC + * When using #MDB_WRITEMAP, use asynchronous flushes to disk. + * As with #MDB_NOSYNC, a system crash can then corrupt the + * database or lose the last transactions. Calling #mdb_env_sync() + * ensures on-disk database integrity until next commit. + * This flag may be changed at any time using #mdb_env_set_flags(). + * <li>#MDB_NOTLS + * Don't use Thread-Local Storage. Tie reader locktable slots to + * #MDB_txn objects instead of to threads. I.e. #mdb_txn_reset() keeps + * the slot reseved for the #MDB_txn object. A thread may use parallel + * read-only transactions. A read-only transaction may span threads if + * the user synchronizes its use. Applications that multiplex many + * user threads over individual OS threads need this option. Such an + * application must also serialize the write transactions in an OS + * thread, since LMDB's write locking is unaware of the user threads. + * <li>#MDB_NOLOCK + * Don't do any locking. If concurrent access is anticipated, the + * caller must manage all concurrency itself. For proper operation + * the caller must enforce single-writer semantics, and must ensure + * that no readers are using old transactions while a writer is + * active. The simplest approach is to use an exclusive lock so that + * no readers may be active at all when a writer begins. + * <li>#MDB_NORDAHEAD + * Turn off readahead. Most operating systems perform readahead on + * read requests by default. This option turns it off if the OS + * supports it. Turning it off may help random read performance + * when the DB is larger than RAM and system RAM is full. + * The option is not implemented on Windows. + * <li>#MDB_NOMEMINIT + * Don't initialize malloc'd memory before writing to unused spaces + * in the data file. By default, memory for pages written to the data + * file is obtained using malloc. While these pages may be reused in + * subsequent transactions, freshly malloc'd pages will be initialized + * to zeroes before use. This avoids persisting leftover data from other + * code (that used the heap and subsequently freed the memory) into the + * data file. Note that many other system libraries may allocate + * and free memory from the heap for arbitrary uses. E.g., stdio may + * use the heap for file I/O buffers. This initialization step has a + * modest performance cost so some applications may want to disable + * it using this flag. This option can be a problem for applications + * which handle sensitive data like passwords, and it makes memory + * checkers like Valgrind noisy. This flag is not needed with #MDB_WRITEMAP, + * which writes directly to the mmap instead of using malloc for pages. The + * initialization is also skipped if #MDB_RESERVE is used; the + * caller is expected to overwrite all of the memory that was + * reserved in that case. + * This flag may be changed at any time using #mdb_env_set_flags(). + * </ul> + * @param[in] mode The UNIX permissions to set on created files and semaphores. + * This parameter is ignored on Windows. + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>#MDB_VERSION_MISMATCH - the version of the LMDB library doesn't match the + * version that created the database environment. + * <li>#MDB_INVALID - the environment file headers are corrupted. + * <li>ENOENT - the directory specified by the path parameter doesn't exist. + * <li>EACCES - the user didn't have permission to access the environment files. + * <li>EAGAIN - the environment was locked by another process. + * </ul> + */ +int mdb_env_open(MDB_env *env, const char *path, unsigned int flags, mdb_mode_t mode); + + /** @brief Copy an LMDB environment to the specified path. + * + * This function may be used to make a backup of an existing environment. + * No lockfile is created, since it gets recreated at need. + * @note This call can trigger significant file size growth if run in + * parallel with write transactions, because it employs a read-only + * transaction. See long-lived transactions under @ref caveats_sec. + * @param[in] env An environment handle returned by #mdb_env_create(). It + * must have already been opened successfully. + * @param[in] path The directory in which the copy will reside. This + * directory must already exist and be writable but must otherwise be + * empty. + * @return A non-zero error value on failure and 0 on success. + */ +int mdb_env_copy(MDB_env *env, const char *path); + + /** @brief Copy an LMDB environment to the specified file descriptor. + * + * This function may be used to make a backup of an existing environment. + * No lockfile is created, since it gets recreated at need. + * @note This call can trigger significant file size growth if run in + * parallel with write transactions, because it employs a read-only + * transaction. See long-lived transactions under @ref caveats_sec. + * @param[in] env An environment handle returned by #mdb_env_create(). It + * must have already been opened successfully. + * @param[in] fd The filedescriptor to write the copy to. It must + * have already been opened for Write access. + * @return A non-zero error value on failure and 0 on success. + */ +int mdb_env_copyfd(MDB_env *env, mdb_filehandle_t fd); + + /** @brief Copy an LMDB environment to the specified path, with options. + * + * This function may be used to make a backup of an existing environment. + * No lockfile is created, since it gets recreated at need. + * @note This call can trigger significant file size growth if run in + * parallel with write transactions, because it employs a read-only + * transaction. See long-lived transactions under @ref caveats_sec. + * @param[in] env An environment handle returned by #mdb_env_create(). It + * must have already been opened successfully. + * @param[in] path The directory in which the copy will reside. This + * directory must already exist and be writable but must otherwise be + * empty. + * @param[in] flags Special options for this operation. This parameter + * must be set to 0 or by bitwise OR'ing together one or more of the + * values described here. + * <ul> + * <li>#MDB_CP_COMPACT - Perform compaction while copying: omit free + * pages and sequentially renumber all pages in output. This option + * consumes more CPU and runs more slowly than the default. + * Currently it fails if the environment has suffered a page leak. + * </ul> + * @return A non-zero error value on failure and 0 on success. + */ +int mdb_env_copy2(MDB_env *env, const char *path, unsigned int flags); + + /** @brief Copy an LMDB environment to the specified file descriptor, + * with options. + * + * This function may be used to make a backup of an existing environment. + * No lockfile is created, since it gets recreated at need. See + * #mdb_env_copy2() for further details. + * @note This call can trigger significant file size growth if run in + * parallel with write transactions, because it employs a read-only + * transaction. See long-lived transactions under @ref caveats_sec. + * @param[in] env An environment handle returned by #mdb_env_create(). It + * must have already been opened successfully. + * @param[in] fd The filedescriptor to write the copy to. It must + * have already been opened for Write access. + * @param[in] flags Special options for this operation. + * See #mdb_env_copy2() for options. + * @return A non-zero error value on failure and 0 on success. + */ +int mdb_env_copyfd2(MDB_env *env, mdb_filehandle_t fd, unsigned int flags); + + /** @brief Return statistics about the LMDB environment. + * + * @param[in] env An environment handle returned by #mdb_env_create() + * @param[out] stat The address of an #MDB_stat structure + * where the statistics will be copied + */ +int mdb_env_stat(MDB_env *env, MDB_stat *stat); + + /** @brief Return information about the LMDB environment. + * + * @param[in] env An environment handle returned by #mdb_env_create() + * @param[out] stat The address of an #MDB_envinfo structure + * where the information will be copied + */ +int mdb_env_info(MDB_env *env, MDB_envinfo *stat); + + /** @brief Flush the data buffers to disk. + * + * Data is always written to disk when #mdb_txn_commit() is called, + * but the operating system may keep it buffered. LMDB always flushes + * the OS buffers upon commit as well, unless the environment was + * opened with #MDB_NOSYNC or in part #MDB_NOMETASYNC. This call is + * not valid if the environment was opened with #MDB_RDONLY. + * @param[in] env An environment handle returned by #mdb_env_create() + * @param[in] force If non-zero, force a synchronous flush. Otherwise + * if the environment has the #MDB_NOSYNC flag set the flushes + * will be omitted, and with #MDB_MAPASYNC they will be asynchronous. + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EACCES - the environment is read-only. + * <li>EINVAL - an invalid parameter was specified. + * <li>EIO - an error occurred during synchronization. + * </ul> + */ +int mdb_env_sync(MDB_env *env, int force); + + /** @brief Close the environment and release the memory map. + * + * Only a single thread may call this function. All transactions, databases, + * and cursors must already be closed before calling this function. Attempts to + * use any such handles after calling this function will cause a SIGSEGV. + * The environment handle will be freed and must not be used again after this call. + * @param[in] env An environment handle returned by #mdb_env_create() + */ +void mdb_env_close(MDB_env *env); + + /** @brief Set environment flags. + * + * This may be used to set some flags in addition to those from + * #mdb_env_open(), or to unset these flags. If several threads + * change the flags at the same time, the result is undefined. + * @param[in] env An environment handle returned by #mdb_env_create() + * @param[in] flags The flags to change, bitwise OR'ed together + * @param[in] onoff A non-zero value sets the flags, zero clears them. + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EINVAL - an invalid parameter was specified. + * </ul> + */ +int mdb_env_set_flags(MDB_env *env, unsigned int flags, int onoff); + + /** @brief Get environment flags. + * + * @param[in] env An environment handle returned by #mdb_env_create() + * @param[out] flags The address of an integer to store the flags + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EINVAL - an invalid parameter was specified. + * </ul> + */ +int mdb_env_get_flags(MDB_env *env, unsigned int *flags); + + /** @brief Return the path that was used in #mdb_env_open(). + * + * @param[in] env An environment handle returned by #mdb_env_create() + * @param[out] path Address of a string pointer to contain the path. This + * is the actual string in the environment, not a copy. It should not be + * altered in any way. + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EINVAL - an invalid parameter was specified. + * </ul> + */ +int mdb_env_get_path(MDB_env *env, const char **path); + + /** @brief Return the filedescriptor for the given environment. + * + * This function may be called after fork(), so the descriptor can be + * closed before exec*(). Other LMDB file descriptors have FD_CLOEXEC. + * (Until LMDB 0.9.18, only the lockfile had that.) + * + * @param[in] env An environment handle returned by #mdb_env_create() + * @param[out] fd Address of a mdb_filehandle_t to contain the descriptor. + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EINVAL - an invalid parameter was specified. + * </ul> + */ +int mdb_env_get_fd(MDB_env *env, mdb_filehandle_t *fd); + + /** @brief Set the size of the memory map to use for this environment. + * + * The size should be a multiple of the OS page size. The default is + * 10485760 bytes. The size of the memory map is also the maximum size + * of the database. The value should be chosen as large as possible, + * to accommodate future growth of the database. + * This function should be called after #mdb_env_create() and before #mdb_env_open(). + * It may be called at later times if no transactions are active in + * this process. Note that the library does not check for this condition, + * the caller must ensure it explicitly. + * + * The new size takes effect immediately for the current process but + * will not be persisted to any others until a write transaction has been + * committed by the current process. Also, only mapsize increases are + * persisted into the environment. + * + * If the mapsize is increased by another process, and data has grown + * beyond the range of the current mapsize, #mdb_txn_begin() will + * return #MDB_MAP_RESIZED. This function may be called with a size + * of zero to adopt the new size. + * + * Any attempt to set a size smaller than the space already consumed + * by the environment will be silently changed to the current size of the used space. + * @param[in] env An environment handle returned by #mdb_env_create() + * @param[in] size The size in bytes + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EINVAL - an invalid parameter was specified, or the environment has + * an active write transaction. + * </ul> + */ +int mdb_env_set_mapsize(MDB_env *env, size_t size); + + /** @brief Set the maximum number of threads/reader slots for the environment. + * + * This defines the number of slots in the lock table that is used to track readers in the + * the environment. The default is 126. + * Starting a read-only transaction normally ties a lock table slot to the + * current thread until the environment closes or the thread exits. If + * MDB_NOTLS is in use, #mdb_txn_begin() instead ties the slot to the + * MDB_txn object until it or the #MDB_env object is destroyed. + * This function may only be called after #mdb_env_create() and before #mdb_env_open(). + * @param[in] env An environment handle returned by #mdb_env_create() + * @param[in] readers The maximum number of reader lock table slots + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EINVAL - an invalid parameter was specified, or the environment is already open. + * </ul> + */ +int mdb_env_set_maxreaders(MDB_env *env, unsigned int readers); + + /** @brief Get the maximum number of threads/reader slots for the environment. + * + * @param[in] env An environment handle returned by #mdb_env_create() + * @param[out] readers Address of an integer to store the number of readers + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EINVAL - an invalid parameter was specified. + * </ul> + */ +int mdb_env_get_maxreaders(MDB_env *env, unsigned int *readers); + + /** @brief Set the maximum number of named databases for the environment. + * + * This function is only needed if multiple databases will be used in the + * environment. Simpler applications that use the environment as a single + * unnamed database can ignore this option. + * This function may only be called after #mdb_env_create() and before #mdb_env_open(). + * + * Currently a moderate number of slots are cheap but a huge number gets + * expensive: 7-120 words per transaction, and every #mdb_dbi_open() + * does a linear search of the opened slots. + * @param[in] env An environment handle returned by #mdb_env_create() + * @param[in] dbs The maximum number of databases + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EINVAL - an invalid parameter was specified, or the environment is already open. + * </ul> + */ +int mdb_env_set_maxdbs(MDB_env *env, MDB_dbi dbs); + + /** @brief Get the maximum size of keys and #MDB_DUPSORT data we can write. + * + * Depends on the compile-time constant #MDB_MAXKEYSIZE. Default 511. + * See @ref MDB_val. + * @param[in] env An environment handle returned by #mdb_env_create() + * @return The maximum size of a key we can write + */ +int mdb_env_get_maxkeysize(MDB_env *env); + + /** @brief Set application information associated with the #MDB_env. + * + * @param[in] env An environment handle returned by #mdb_env_create() + * @param[in] ctx An arbitrary pointer for whatever the application needs. + * @return A non-zero error value on failure and 0 on success. + */ +int mdb_env_set_userctx(MDB_env *env, void *ctx); + + /** @brief Get the application information associated with the #MDB_env. + * + * @param[in] env An environment handle returned by #mdb_env_create() + * @return The pointer set by #mdb_env_set_userctx(). + */ +void *mdb_env_get_userctx(MDB_env *env); + + /** @brief A callback function for most LMDB assert() failures, + * called before printing the message and aborting. + * + * @param[in] env An environment handle returned by #mdb_env_create(). + * @param[in] msg The assertion message, not including newline. + */ +typedef void MDB_assert_func(MDB_env *env, const char *msg); + + /** Set or reset the assert() callback of the environment. + * Disabled if liblmdb is buillt with NDEBUG. + * @note This hack should become obsolete as lmdb's error handling matures. + * @param[in] env An environment handle returned by #mdb_env_create(). + * @param[in] func An #MDB_assert_func function, or 0. + * @return A non-zero error value on failure and 0 on success. + */ +int mdb_env_set_assert(MDB_env *env, MDB_assert_func *func); + + /** @brief Create a transaction for use with the environment. + * + * The transaction handle may be discarded using #mdb_txn_abort() or #mdb_txn_commit(). + * @note A transaction and its cursors must only be used by a single + * thread, and a thread may only have a single transaction at a time. + * If #MDB_NOTLS is in use, this does not apply to read-only transactions. + * @note Cursors may not span transactions. + * @param[in] env An environment handle returned by #mdb_env_create() + * @param[in] parent If this parameter is non-NULL, the new transaction + * will be a nested transaction, with the transaction indicated by \b parent + * as its parent. Transactions may be nested to any level. A parent + * transaction and its cursors may not issue any other operations than + * mdb_txn_commit and mdb_txn_abort while it has active child transactions. + * @param[in] flags Special options for this transaction. This parameter + * must be set to 0 or by bitwise OR'ing together one or more of the + * values described here. + * <ul> + * <li>#MDB_RDONLY + * This transaction will not perform any write operations. + * </ul> + * @param[out] txn Address where the new #MDB_txn handle will be stored + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>#MDB_PANIC - a fatal error occurred earlier and the environment + * must be shut down. + * <li>#MDB_MAP_RESIZED - another process wrote data beyond this MDB_env's + * mapsize and this environment's map must be resized as well. + * See #mdb_env_set_mapsize(). + * <li>#MDB_READERS_FULL - a read-only transaction was requested and + * the reader lock table is full. See #mdb_env_set_maxreaders(). + * <li>ENOMEM - out of memory. + * </ul> + */ +int mdb_txn_begin(MDB_env *env, MDB_txn *parent, unsigned int flags, MDB_txn **txn); + + /** @brief Returns the transaction's #MDB_env + * + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + */ +MDB_env *mdb_txn_env(MDB_txn *txn); + + /** @brief Return the transaction's ID. + * + * This returns the identifier associated with this transaction. For a + * read-only transaction, this corresponds to the snapshot being read; + * concurrent readers will frequently have the same transaction ID. + * + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @return A transaction ID, valid if input is an active transaction. + */ +size_t mdb_txn_id(MDB_txn *txn); + + /** @brief Commit all the operations of a transaction into the database. + * + * The transaction handle is freed. It and its cursors must not be used + * again after this call, except with #mdb_cursor_renew(). + * @note Earlier documentation incorrectly said all cursors would be freed. + * Only write-transactions free cursors. + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EINVAL - an invalid parameter was specified. + * <li>ENOSPC - no more disk space. + * <li>EIO - a low-level I/O error occurred while writing. + * <li>ENOMEM - out of memory. + * </ul> + */ +int mdb_txn_commit(MDB_txn *txn); + + /** @brief Abandon all the operations of the transaction instead of saving them. + * + * The transaction handle is freed. It and its cursors must not be used + * again after this call, except with #mdb_cursor_renew(). + * @note Earlier documentation incorrectly said all cursors would be freed. + * Only write-transactions free cursors. + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + */ +void mdb_txn_abort(MDB_txn *txn); + + /** @brief Reset a read-only transaction. + * + * Abort the transaction like #mdb_txn_abort(), but keep the transaction + * handle. #mdb_txn_renew() may reuse the handle. This saves allocation + * overhead if the process will start a new read-only transaction soon, + * and also locking overhead if #MDB_NOTLS is in use. The reader table + * lock is released, but the table slot stays tied to its thread or + * #MDB_txn. Use mdb_txn_abort() to discard a reset handle, and to free + * its lock table slot if MDB_NOTLS is in use. + * Cursors opened within the transaction must not be used + * again after this call, except with #mdb_cursor_renew(). + * Reader locks generally don't interfere with writers, but they keep old + * versions of database pages allocated. Thus they prevent the old pages + * from being reused when writers commit new data, and so under heavy load + * the database size may grow much more rapidly than otherwise. + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + */ +void mdb_txn_reset(MDB_txn *txn); + + /** @brief Renew a read-only transaction. + * + * This acquires a new reader lock for a transaction handle that had been + * released by #mdb_txn_reset(). It must be called before a reset transaction + * may be used again. + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>#MDB_PANIC - a fatal error occurred earlier and the environment + * must be shut down. + * <li>EINVAL - an invalid parameter was specified. + * </ul> + */ +int mdb_txn_renew(MDB_txn *txn); + +/** Compat with version <= 0.9.4, avoid clash with libmdb from MDB Tools project */ +#define mdb_open(txn,name,flags,dbi) mdb_dbi_open(txn,name,flags,dbi) +/** Compat with version <= 0.9.4, avoid clash with libmdb from MDB Tools project */ +#define mdb_close(env,dbi) mdb_dbi_close(env,dbi) + + /** @brief Open a database in the environment. + * + * A database handle denotes the name and parameters of a database, + * independently of whether such a database exists. + * The database handle may be discarded by calling #mdb_dbi_close(). + * The old database handle is returned if the database was already open. + * The handle may only be closed once. + * + * The database handle will be private to the current transaction until + * the transaction is successfully committed. If the transaction is + * aborted the handle will be closed automatically. + * After a successful commit the handle will reside in the shared + * environment, and may be used by other transactions. + * + * This function must not be called from multiple concurrent + * transactions in the same process. A transaction that uses + * this function must finish (either commit or abort) before + * any other transaction in the process may use this function. + * + * To use named databases (with name != NULL), #mdb_env_set_maxdbs() + * must be called before opening the environment. Database names are + * keys in the unnamed database, and may be read but not written. + * + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @param[in] name The name of the database to open. If only a single + * database is needed in the environment, this value may be NULL. + * @param[in] flags Special options for this database. This parameter + * must be set to 0 or by bitwise OR'ing together one or more of the + * values described here. + * <ul> + * <li>#MDB_REVERSEKEY + * Keys are strings to be compared in reverse order, from the end + * of the strings to the beginning. By default, Keys are treated as strings and + * compared from beginning to end. + * <li>#MDB_DUPSORT + * Duplicate keys may be used in the database. (Or, from another perspective, + * keys may have multiple data items, stored in sorted order.) By default + * keys must be unique and may have only a single data item. + * <li>#MDB_INTEGERKEY + * Keys are binary integers in native byte order, either unsigned int + * or size_t, and will be sorted as such. + * The keys must all be of the same size. + * <li>#MDB_DUPFIXED + * This flag may only be used in combination with #MDB_DUPSORT. This option + * tells the library that the data items for this database are all the same + * size, which allows further optimizations in storage and retrieval. When + * all data items are the same size, the #MDB_GET_MULTIPLE, #MDB_NEXT_MULTIPLE + * and #MDB_PREV_MULTIPLE cursor operations may be used to retrieve multiple + * items at once. + * <li>#MDB_INTEGERDUP + * This option specifies that duplicate data items are binary integers, + * similar to #MDB_INTEGERKEY keys. + * <li>#MDB_REVERSEDUP + * This option specifies that duplicate data items should be compared as + * strings in reverse order. + * <li>#MDB_CREATE + * Create the named database if it doesn't exist. This option is not + * allowed in a read-only transaction or a read-only environment. + * </ul> + * @param[out] dbi Address where the new #MDB_dbi handle will be stored + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>#MDB_NOTFOUND - the specified database doesn't exist in the environment + * and #MDB_CREATE was not specified. + * <li>#MDB_DBS_FULL - too many databases have been opened. See #mdb_env_set_maxdbs(). + * </ul> + */ +int mdb_dbi_open(MDB_txn *txn, const char *name, unsigned int flags, MDB_dbi *dbi); + + /** @brief Retrieve statistics for a database. + * + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @param[in] dbi A database handle returned by #mdb_dbi_open() + * @param[out] stat The address of an #MDB_stat structure + * where the statistics will be copied + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EINVAL - an invalid parameter was specified. + * </ul> + */ +int mdb_stat(MDB_txn *txn, MDB_dbi dbi, MDB_stat *stat); + + /** @brief Retrieve the DB flags for a database handle. + * + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @param[in] dbi A database handle returned by #mdb_dbi_open() + * @param[out] flags Address where the flags will be returned. + * @return A non-zero error value on failure and 0 on success. + */ +int mdb_dbi_flags(MDB_txn *txn, MDB_dbi dbi, unsigned int *flags); + + /** @brief Close a database handle. Normally unnecessary. Use with care: + * + * This call is not mutex protected. Handles should only be closed by + * a single thread, and only if no other threads are going to reference + * the database handle or one of its cursors any further. Do not close + * a handle if an existing transaction has modified its database. + * Doing so can cause misbehavior from database corruption to errors + * like MDB_BAD_VALSIZE (since the DB name is gone). + * + * Closing a database handle is not necessary, but lets #mdb_dbi_open() + * reuse the handle value. Usually it's better to set a bigger + * #mdb_env_set_maxdbs(), unless that value would be large. + * + * @param[in] env An environment handle returned by #mdb_env_create() + * @param[in] dbi A database handle returned by #mdb_dbi_open() + */ +void mdb_dbi_close(MDB_env *env, MDB_dbi dbi); + + /** @brief Empty or delete+close a database. + * + * See #mdb_dbi_close() for restrictions about closing the DB handle. + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @param[in] dbi A database handle returned by #mdb_dbi_open() + * @param[in] del 0 to empty the DB, 1 to delete it from the + * environment and close the DB handle. + * @return A non-zero error value on failure and 0 on success. + */ +int mdb_drop(MDB_txn *txn, MDB_dbi dbi, int del); + + /** @brief Set a custom key comparison function for a database. + * + * The comparison function is called whenever it is necessary to compare a + * key specified by the application with a key currently stored in the database. + * If no comparison function is specified, and no special key flags were specified + * with #mdb_dbi_open(), the keys are compared lexically, with shorter keys collating + * before longer keys. + * @warning This function must be called before any data access functions are used, + * otherwise data corruption may occur. The same comparison function must be used by every + * program accessing the database, every time the database is used. + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @param[in] dbi A database handle returned by #mdb_dbi_open() + * @param[in] cmp A #MDB_cmp_func function + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EINVAL - an invalid parameter was specified. + * </ul> + */ +int mdb_set_compare(MDB_txn *txn, MDB_dbi dbi, MDB_cmp_func *cmp); + + /** @brief Set a custom data comparison function for a #MDB_DUPSORT database. + * + * This comparison function is called whenever it is necessary to compare a data + * item specified by the application with a data item currently stored in the database. + * This function only takes effect if the database was opened with the #MDB_DUPSORT + * flag. + * If no comparison function is specified, and no special key flags were specified + * with #mdb_dbi_open(), the data items are compared lexically, with shorter items collating + * before longer items. + * @warning This function must be called before any data access functions are used, + * otherwise data corruption may occur. The same comparison function must be used by every + * program accessing the database, every time the database is used. + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @param[in] dbi A database handle returned by #mdb_dbi_open() + * @param[in] cmp A #MDB_cmp_func function + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EINVAL - an invalid parameter was specified. + * </ul> + */ +int mdb_set_dupsort(MDB_txn *txn, MDB_dbi dbi, MDB_cmp_func *cmp); + + /** @brief Set a relocation function for a #MDB_FIXEDMAP database. + * + * @todo The relocation function is called whenever it is necessary to move the data + * of an item to a different position in the database (e.g. through tree + * balancing operations, shifts as a result of adds or deletes, etc.). It is + * intended to allow address/position-dependent data items to be stored in + * a database in an environment opened with the #MDB_FIXEDMAP option. + * Currently the relocation feature is unimplemented and setting + * this function has no effect. + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @param[in] dbi A database handle returned by #mdb_dbi_open() + * @param[in] rel A #MDB_rel_func function + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EINVAL - an invalid parameter was specified. + * </ul> + */ +int mdb_set_relfunc(MDB_txn *txn, MDB_dbi dbi, MDB_rel_func *rel); + + /** @brief Set a context pointer for a #MDB_FIXEDMAP database's relocation function. + * + * See #mdb_set_relfunc and #MDB_rel_func for more details. + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @param[in] dbi A database handle returned by #mdb_dbi_open() + * @param[in] ctx An arbitrary pointer for whatever the application needs. + * It will be passed to the callback function set by #mdb_set_relfunc + * as its \b relctx parameter whenever the callback is invoked. + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EINVAL - an invalid parameter was specified. + * </ul> + */ +int mdb_set_relctx(MDB_txn *txn, MDB_dbi dbi, void *ctx); + + /** @brief Get items from a database. + * + * This function retrieves key/data pairs from the database. The address + * and length of the data associated with the specified \b key are returned + * in the structure to which \b data refers. + * If the database supports duplicate keys (#MDB_DUPSORT) then the + * first data item for the key will be returned. Retrieval of other + * items requires the use of #mdb_cursor_get(). + * + * @note The memory pointed to by the returned values is owned by the + * database. The caller need not dispose of the memory, and may not + * modify it in any way. For values returned in a read-only transaction + * any modification attempts will cause a SIGSEGV. + * @note Values returned from the database are valid only until a + * subsequent update operation, or the end of the transaction. + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @param[in] dbi A database handle returned by #mdb_dbi_open() + * @param[in] key The key to search for in the database + * @param[out] data The data corresponding to the key + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>#MDB_NOTFOUND - the key was not in the database. + * <li>EINVAL - an invalid parameter was specified. + * </ul> + */ +int mdb_get(MDB_txn *txn, MDB_dbi dbi, MDB_val *key, MDB_val *data); + + /** @brief Store items into a database. + * + * This function stores key/data pairs in the database. The default behavior + * is to enter the new key/data pair, replacing any previously existing key + * if duplicates are disallowed, or adding a duplicate data item if + * duplicates are allowed (#MDB_DUPSORT). + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @param[in] dbi A database handle returned by #mdb_dbi_open() + * @param[in] key The key to store in the database + * @param[in,out] data The data to store + * @param[in] flags Special options for this operation. This parameter + * must be set to 0 or by bitwise OR'ing together one or more of the + * values described here. + * <ul> + * <li>#MDB_NODUPDATA - enter the new key/data pair only if it does not + * already appear in the database. This flag may only be specified + * if the database was opened with #MDB_DUPSORT. The function will + * return #MDB_KEYEXIST if the key/data pair already appears in the + * database. + * <li>#MDB_NOOVERWRITE - enter the new key/data pair only if the key + * does not already appear in the database. The function will return + * #MDB_KEYEXIST if the key already appears in the database, even if + * the database supports duplicates (#MDB_DUPSORT). The \b data + * parameter will be set to point to the existing item. + * <li>#MDB_RESERVE - reserve space for data of the given size, but + * don't copy the given data. Instead, return a pointer to the + * reserved space, which the caller can fill in later - before + * the next update operation or the transaction ends. This saves + * an extra memcpy if the data is being generated later. + * LMDB does nothing else with this memory, the caller is expected + * to modify all of the space requested. This flag must not be + * specified if the database was opened with #MDB_DUPSORT. + * <li>#MDB_APPEND - append the given key/data pair to the end of the + * database. This option allows fast bulk loading when keys are + * already known to be in the correct order. Loading unsorted keys + * with this flag will cause a #MDB_KEYEXIST error. + * <li>#MDB_APPENDDUP - as above, but for sorted dup data. + * </ul> + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>#MDB_MAP_FULL - the database is full, see #mdb_env_set_mapsize(). + * <li>#MDB_TXN_FULL - the transaction has too many dirty pages. + * <li>EACCES - an attempt was made to write in a read-only transaction. + * <li>EINVAL - an invalid parameter was specified. + * </ul> + */ +int mdb_put(MDB_txn *txn, MDB_dbi dbi, MDB_val *key, MDB_val *data, + unsigned int flags); + + /** @brief Delete items from a database. + * + * This function removes key/data pairs from the database. + * If the database does not support sorted duplicate data items + * (#MDB_DUPSORT) the data parameter is ignored. + * If the database supports sorted duplicates and the data parameter + * is NULL, all of the duplicate data items for the key will be + * deleted. Otherwise, if the data parameter is non-NULL + * only the matching data item will be deleted. + * This function will return #MDB_NOTFOUND if the specified key/data + * pair is not in the database. + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @param[in] dbi A database handle returned by #mdb_dbi_open() + * @param[in] key The key to delete from the database + * @param[in] data The data to delete + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EACCES - an attempt was made to write in a read-only transaction. + * <li>EINVAL - an invalid parameter was specified. + * </ul> + */ +int mdb_del(MDB_txn *txn, MDB_dbi dbi, MDB_val *key, MDB_val *data); + + /** @brief Create a cursor handle. + * + * A cursor is associated with a specific transaction and database. + * A cursor cannot be used when its database handle is closed. Nor + * when its transaction has ended, except with #mdb_cursor_renew(). + * It can be discarded with #mdb_cursor_close(). + * A cursor in a write-transaction can be closed before its transaction + * ends, and will otherwise be closed when its transaction ends. + * A cursor in a read-only transaction must be closed explicitly, before + * or after its transaction ends. It can be reused with + * #mdb_cursor_renew() before finally closing it. + * @note Earlier documentation said that cursors in every transaction + * were closed when the transaction committed or aborted. + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @param[in] dbi A database handle returned by #mdb_dbi_open() + * @param[out] cursor Address where the new #MDB_cursor handle will be stored + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EINVAL - an invalid parameter was specified. + * </ul> + */ +int mdb_cursor_open(MDB_txn *txn, MDB_dbi dbi, MDB_cursor **cursor); + + /** @brief Close a cursor handle. + * + * The cursor handle will be freed and must not be used again after this call. + * Its transaction must still be live if it is a write-transaction. + * @param[in] cursor A cursor handle returned by #mdb_cursor_open() + */ +void mdb_cursor_close(MDB_cursor *cursor); + + /** @brief Renew a cursor handle. + * + * A cursor is associated with a specific transaction and database. + * Cursors that are only used in read-only + * transactions may be re-used, to avoid unnecessary malloc/free overhead. + * The cursor may be associated with a new read-only transaction, and + * referencing the same database handle as it was created with. + * This may be done whether the previous transaction is live or dead. + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @param[in] cursor A cursor handle returned by #mdb_cursor_open() + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EINVAL - an invalid parameter was specified. + * </ul> + */ +int mdb_cursor_renew(MDB_txn *txn, MDB_cursor *cursor); + + /** @brief Return the cursor's transaction handle. + * + * @param[in] cursor A cursor handle returned by #mdb_cursor_open() + */ +MDB_txn *mdb_cursor_txn(MDB_cursor *cursor); + + /** @brief Return the cursor's database handle. + * + * @param[in] cursor A cursor handle returned by #mdb_cursor_open() + */ +MDB_dbi mdb_cursor_dbi(MDB_cursor *cursor); + + /** @brief Retrieve by cursor. + * + * This function retrieves key/data pairs from the database. The address and length + * of the key are returned in the object to which \b key refers (except for the + * case of the #MDB_SET option, in which the \b key object is unchanged), and + * the address and length of the data are returned in the object to which \b data + * refers. + * See #mdb_get() for restrictions on using the output values. + * @param[in] cursor A cursor handle returned by #mdb_cursor_open() + * @param[in,out] key The key for a retrieved item + * @param[in,out] data The data of a retrieved item + * @param[in] op A cursor operation #MDB_cursor_op + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>#MDB_NOTFOUND - no matching key found. + * <li>EINVAL - an invalid parameter was specified. + * </ul> + */ +int mdb_cursor_get(MDB_cursor *cursor, MDB_val *key, MDB_val *data, + MDB_cursor_op op); + + /** @brief Store by cursor. + * + * This function stores key/data pairs into the database. + * The cursor is positioned at the new item, or on failure usually near it. + * @note Earlier documentation incorrectly said errors would leave the + * state of the cursor unchanged. + * @param[in] cursor A cursor handle returned by #mdb_cursor_open() + * @param[in] key The key operated on. + * @param[in] data The data operated on. + * @param[in] flags Options for this operation. This parameter + * must be set to 0 or one of the values described here. + * <ul> + * <li>#MDB_CURRENT - replace the item at the current cursor position. + * The \b key parameter must still be provided, and must match it. + * If using sorted duplicates (#MDB_DUPSORT) the data item must still + * sort into the same place. This is intended to be used when the + * new data is the same size as the old. Otherwise it will simply + * perform a delete of the old record followed by an insert. + * <li>#MDB_NODUPDATA - enter the new key/data pair only if it does not + * already appear in the database. This flag may only be specified + * if the database was opened with #MDB_DUPSORT. The function will + * return #MDB_KEYEXIST if the key/data pair already appears in the + * database. + * <li>#MDB_NOOVERWRITE - enter the new key/data pair only if the key + * does not already appear in the database. The function will return + * #MDB_KEYEXIST if the key already appears in the database, even if + * the database supports duplicates (#MDB_DUPSORT). + * <li>#MDB_RESERVE - reserve space for data of the given size, but + * don't copy the given data. Instead, return a pointer to the + * reserved space, which the caller can fill in later - before + * the next update operation or the transaction ends. This saves + * an extra memcpy if the data is being generated later. This flag + * must not be specified if the database was opened with #MDB_DUPSORT. + * <li>#MDB_APPEND - append the given key/data pair to the end of the + * database. No key comparisons are performed. This option allows + * fast bulk loading when keys are already known to be in the + * correct order. Loading unsorted keys with this flag will cause + * a #MDB_KEYEXIST error. + * <li>#MDB_APPENDDUP - as above, but for sorted dup data. + * <li>#MDB_MULTIPLE - store multiple contiguous data elements in a + * single request. This flag may only be specified if the database + * was opened with #MDB_DUPFIXED. The \b data argument must be an + * array of two MDB_vals. The mv_size of the first MDB_val must be + * the size of a single data element. The mv_data of the first MDB_val + * must point to the beginning of the array of contiguous data elements. + * The mv_size of the second MDB_val must be the count of the number + * of data elements to store. On return this field will be set to + * the count of the number of elements actually written. The mv_data + * of the second MDB_val is unused. + * </ul> + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>#MDB_MAP_FULL - the database is full, see #mdb_env_set_mapsize(). + * <li>#MDB_TXN_FULL - the transaction has too many dirty pages. + * <li>EACCES - an attempt was made to write in a read-only transaction. + * <li>EINVAL - an invalid parameter was specified. + * </ul> + */ +int mdb_cursor_put(MDB_cursor *cursor, MDB_val *key, MDB_val *data, + unsigned int flags); + + /** @brief Delete current key/data pair + * + * This function deletes the key/data pair to which the cursor refers. + * @param[in] cursor A cursor handle returned by #mdb_cursor_open() + * @param[in] flags Options for this operation. This parameter + * must be set to 0 or one of the values described here. + * <ul> + * <li>#MDB_NODUPDATA - delete all of the data items for the current key. + * This flag may only be specified if the database was opened with #MDB_DUPSORT. + * </ul> + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EACCES - an attempt was made to write in a read-only transaction. + * <li>EINVAL - an invalid parameter was specified. + * </ul> + */ +int mdb_cursor_del(MDB_cursor *cursor, unsigned int flags); + + /** @brief Return count of duplicates for current key. + * + * This call is only valid on databases that support sorted duplicate + * data items #MDB_DUPSORT. + * @param[in] cursor A cursor handle returned by #mdb_cursor_open() + * @param[out] countp Address where the count will be stored + * @return A non-zero error value on failure and 0 on success. Some possible + * errors are: + * <ul> + * <li>EINVAL - cursor is not initialized, or an invalid parameter was specified. + * </ul> + */ +int mdb_cursor_count(MDB_cursor *cursor, size_t *countp); + + /** @brief Compare two data items according to a particular database. + * + * This returns a comparison as if the two data items were keys in the + * specified database. + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @param[in] dbi A database handle returned by #mdb_dbi_open() + * @param[in] a The first item to compare + * @param[in] b The second item to compare + * @return < 0 if a < b, 0 if a == b, > 0 if a > b + */ +int mdb_cmp(MDB_txn *txn, MDB_dbi dbi, const MDB_val *a, const MDB_val *b); + + /** @brief Compare two data items according to a particular database. + * + * This returns a comparison as if the two items were data items of + * the specified database. The database must have the #MDB_DUPSORT flag. + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @param[in] dbi A database handle returned by #mdb_dbi_open() + * @param[in] a The first item to compare + * @param[in] b The second item to compare + * @return < 0 if a < b, 0 if a == b, > 0 if a > b + */ +int mdb_dcmp(MDB_txn *txn, MDB_dbi dbi, const MDB_val *a, const MDB_val *b); + + /** @brief A callback function used to print a message from the library. + * + * @param[in] msg The string to be printed. + * @param[in] ctx An arbitrary context pointer for the callback. + * @return < 0 on failure, >= 0 on success. + */ +typedef int (MDB_msg_func)(const char *msg, void *ctx); + + /** @brief Dump the entries in the reader lock table. + * + * @param[in] env An environment handle returned by #mdb_env_create() + * @param[in] func A #MDB_msg_func function + * @param[in] ctx Anything the message function needs + * @return < 0 on failure, >= 0 on success. + */ +int mdb_reader_list(MDB_env *env, MDB_msg_func *func, void *ctx); + + /** @brief Check for stale entries in the reader lock table. + * + * @param[in] env An environment handle returned by #mdb_env_create() + * @param[out] dead Number of stale slots that were cleared + * @return 0 on success, non-zero on failure. + */ +int mdb_reader_check(MDB_env *env, int *dead); +/** @} */ + +#ifdef __cplusplus +} +#endif +/** @page tools LMDB Command Line Tools + The following describes the command line tools that are available for LMDB. + \li \ref mdb_copy_1 + \li \ref mdb_dump_1 + \li \ref mdb_load_1 + \li \ref mdb_stat_1 +*/ + +#endif /* _LMDB_H_ */ diff --git a/src/contrib/lmdb/mdb.c b/src/contrib/lmdb/mdb.c new file mode 100644 index 0000000..d9e7c5e --- /dev/null +++ b/src/contrib/lmdb/mdb.c @@ -0,0 +1,10266 @@ +/** @file mdb.c + * @brief Lightning memory-mapped database library + * + * A Btree-based database management library modeled loosely on the + * BerkeleyDB API, but much simplified. + */ +/* + * Copyright 2011-2018 Howard Chu, Symas Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + * + * This code is derived from btree.c written by Martin Hedenfalk. + * + * Copyright (c) 2009, 2010 Martin Hedenfalk <martin@bzero.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE 1 +#endif +#if defined(__WIN64__) +#define _FILE_OFFSET_BITS 64 +#endif +#ifdef _WIN32 +#include <malloc.h> +#include <windows.h> +#include <wchar.h> /* get wcscpy() */ + +/** getpid() returns int; MinGW defines pid_t but MinGW64 typedefs it + * as int64 which is wrong. MSVC doesn't define it at all, so just + * don't use it. + */ +#define MDB_PID_T int +#define MDB_THR_T DWORD +#include <sys/types.h> +#include <sys/stat.h> +#ifdef __GNUC__ +# include <sys/param.h> +#else +# define LITTLE_ENDIAN 1234 +# define BIG_ENDIAN 4321 +# define BYTE_ORDER LITTLE_ENDIAN +# ifndef SSIZE_MAX +# define SSIZE_MAX INT_MAX +# endif +#endif +#else +#include <sys/types.h> +#include <sys/stat.h> +#define MDB_PID_T pid_t +#define MDB_THR_T pthread_t +#include <sys/param.h> +#include <sys/uio.h> +#include <sys/mman.h> +#ifdef HAVE_SYS_FILE_H +#include <sys/file.h> +#endif +#include <fcntl.h> +#endif + +#if defined(__mips) && defined(__linux) +/* MIPS has cache coherency issues, requires explicit cache control */ +#include <asm/cachectl.h> +extern int cacheflush(char *addr, int nbytes, int cache); +#define CACHEFLUSH(addr, bytes, cache) cacheflush(addr, bytes, cache) +#else +#define CACHEFLUSH(addr, bytes, cache) +#endif + +#if defined(__linux) && !defined(MDB_FDATASYNC_WORKS) +/** fdatasync is broken on ext3/ext4fs on older kernels, see + * description in #mdb_env_open2 comments. You can safely + * define MDB_FDATASYNC_WORKS if this code will only be run + * on kernels 3.6 and newer. + */ +#define BROKEN_FDATASYNC +#endif + +#include <errno.h> +#include <limits.h> +#include <stddef.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#ifdef _MSC_VER +#include <io.h> +typedef SSIZE_T ssize_t; +#else +#include <unistd.h> +#endif + +#if defined(__sun) || defined(ANDROID) +/* Most platforms have posix_memalign, older may only have memalign */ +#define HAVE_MEMALIGN 1 +#include <malloc.h> +/* On Solaris, we need the POSIX sigwait function */ +#if defined (__sun) +# define _POSIX_PTHREAD_SEMANTICS 1 +#endif +#endif + +#if !(defined(BYTE_ORDER) || defined(__BYTE_ORDER)) +#include <netinet/in.h> +#include <resolv.h> /* defines BYTE_ORDER on HPUX and Solaris */ +#endif + +#if defined(__APPLE__) || defined (BSD) || defined(__FreeBSD_kernel__) +# define MDB_USE_POSIX_SEM 1 +# define MDB_FDATASYNC fsync +#elif defined(ANDROID) +# define MDB_FDATASYNC fsync +#endif + +#ifndef _WIN32 +#include <pthread.h> +#include <signal.h> +#ifdef MDB_USE_POSIX_SEM +# define MDB_USE_HASH 1 +#include <semaphore.h> +#else +#define MDB_USE_POSIX_MUTEX 1 +#endif +#endif + +#if defined(_WIN32) + defined(MDB_USE_POSIX_SEM) \ + + defined(MDB_USE_POSIX_MUTEX) != 1 +# error "Ambiguous shared-lock implementation" +#endif + +#ifdef USE_VALGRIND +#include <valgrind/memcheck.h> +#define VGMEMP_CREATE(h,r,z) VALGRIND_CREATE_MEMPOOL(h,r,z) +#define VGMEMP_ALLOC(h,a,s) VALGRIND_MEMPOOL_ALLOC(h,a,s) +#define VGMEMP_FREE(h,a) VALGRIND_MEMPOOL_FREE(h,a) +#define VGMEMP_DESTROY(h) VALGRIND_DESTROY_MEMPOOL(h) +#define VGMEMP_DEFINED(a,s) VALGRIND_MAKE_MEM_DEFINED(a,s) +#else +#define VGMEMP_CREATE(h,r,z) +#define VGMEMP_ALLOC(h,a,s) +#define VGMEMP_FREE(h,a) +#define VGMEMP_DESTROY(h) +#define VGMEMP_DEFINED(a,s) +#endif + +#ifndef BYTE_ORDER +# if (defined(_LITTLE_ENDIAN) || defined(_BIG_ENDIAN)) && !(defined(_LITTLE_ENDIAN) && defined(_BIG_ENDIAN)) +/* Solaris just defines one or the other */ +# define LITTLE_ENDIAN 1234 +# define BIG_ENDIAN 4321 +# ifdef _LITTLE_ENDIAN +# define BYTE_ORDER LITTLE_ENDIAN +# else +# define BYTE_ORDER BIG_ENDIAN +# endif +# else +# define BYTE_ORDER __BYTE_ORDER +# endif +#endif + +#ifndef LITTLE_ENDIAN +#define LITTLE_ENDIAN __LITTLE_ENDIAN +#endif +#ifndef BIG_ENDIAN +#define BIG_ENDIAN __BIG_ENDIAN +#endif + +#if defined(__i386) || defined(__x86_64) || defined(_M_IX86) +#define MISALIGNED_OK 1 +#endif + +#include "lmdb.h" +#include "midl.h" + +#if (BYTE_ORDER == LITTLE_ENDIAN) == (BYTE_ORDER == BIG_ENDIAN) +# error "Unknown or unsupported endianness (BYTE_ORDER)" +#elif (-6 & 5) || CHAR_BIT != 8 || UINT_MAX < 0xffffffff || ULONG_MAX % 0xFFFF +# error "Two's complement, reasonably sized integer types, please" +#endif + +#ifdef __GNUC__ +/** Put infrequently used env functions in separate section */ +# ifdef __APPLE__ +# define ESECT __attribute__ ((section("__TEXT,text_env"))) +# else +# define ESECT __attribute__ ((section("text_env"))) +# endif +#else +#define ESECT +#endif + +#ifdef _WIN32 +#define CALL_CONV WINAPI +#else +#define CALL_CONV +#endif + +/** @defgroup internal LMDB Internals + * @{ + */ +/** @defgroup compat Compatibility Macros + * A bunch of macros to minimize the amount of platform-specific ifdefs + * needed throughout the rest of the code. When the features this library + * needs are similar enough to POSIX to be hidden in a one-or-two line + * replacement, this macro approach is used. + * @{ + */ + + /** Features under development */ +#ifndef MDB_DEVEL +#define MDB_DEVEL 0 +#endif + + /** Wrapper around __func__, which is a C99 feature */ +#if __STDC_VERSION__ >= 199901L +# define mdb_func_ __func__ +#elif __GNUC__ >= 2 || _MSC_VER >= 1300 +# define mdb_func_ __FUNCTION__ +#else +/* If a debug message says <mdb_unknown>(), update the #if statements above */ +# define mdb_func_ "<mdb_unknown>" +#endif + +/* Internal error codes, not exposed outside liblmdb */ +#define MDB_NO_ROOT (MDB_LAST_ERRCODE + 10) +#ifdef _WIN32 +#define MDB_OWNERDEAD ((int) WAIT_ABANDONED) +#elif defined(MDB_USE_POSIX_MUTEX) && defined(EOWNERDEAD) +#define MDB_OWNERDEAD EOWNERDEAD /**< #LOCK_MUTEX0() result if dead owner */ +#endif + +#ifdef __GLIBC__ +#define GLIBC_VER ((__GLIBC__ << 16 )| __GLIBC_MINOR__) +#endif +/** Some platforms define the EOWNERDEAD error code + * even though they don't support Robust Mutexes. + * Compile with -DMDB_USE_ROBUST=0, or use some other + * mechanism like -DMDB_USE_POSIX_SEM instead of + * -DMDB_USE_POSIX_MUTEX. + * (Posix semaphores are not robust.) + */ +#ifndef MDB_USE_ROBUST +/* Android currently lacks Robust Mutex support. So does glibc < 2.4. */ +# if defined(MDB_USE_POSIX_MUTEX) && (defined(ANDROID) || \ + (defined(__GLIBC__) && GLIBC_VER < 0x020004)) +# define MDB_USE_ROBUST 0 +# else +# define MDB_USE_ROBUST 1 +# endif +#endif /* !MDB_USE_ROBUST */ + +#if defined(MDB_USE_POSIX_MUTEX) && (MDB_USE_ROBUST) +/* glibc < 2.12 only provided _np API */ +# if (defined(__GLIBC__) && GLIBC_VER < 0x02000c) || \ + (defined(PTHREAD_MUTEX_ROBUST_NP) && !defined(PTHREAD_MUTEX_ROBUST)) +# define PTHREAD_MUTEX_ROBUST PTHREAD_MUTEX_ROBUST_NP +# define pthread_mutexattr_setrobust(attr, flag) pthread_mutexattr_setrobust_np(attr, flag) +# define pthread_mutex_consistent(mutex) pthread_mutex_consistent_np(mutex) +# endif +#endif /* MDB_USE_POSIX_MUTEX && MDB_USE_ROBUST */ + +#if defined(MDB_OWNERDEAD) && (MDB_USE_ROBUST) +#define MDB_ROBUST_SUPPORTED 1 +#endif + +#ifdef _WIN32 +#define MDB_USE_HASH 1 +#define MDB_PIDLOCK 0 +#define THREAD_RET DWORD +#define pthread_t HANDLE +#define pthread_mutex_t HANDLE +#define pthread_cond_t HANDLE +typedef HANDLE mdb_mutex_t, mdb_mutexref_t; +#define pthread_key_t DWORD +#define pthread_self() GetCurrentThreadId() +#define pthread_key_create(x,y) \ + ((*(x) = TlsAlloc()) == TLS_OUT_OF_INDEXES ? ErrCode() : 0) +#define pthread_key_delete(x) TlsFree(x) +#define pthread_getspecific(x) TlsGetValue(x) +#define pthread_setspecific(x,y) (TlsSetValue(x,y) ? 0 : ErrCode()) +#define pthread_mutex_unlock(x) ReleaseMutex(*x) +#define pthread_mutex_lock(x) WaitForSingleObject(*x, INFINITE) +#define pthread_cond_signal(x) SetEvent(*x) +#define pthread_cond_wait(cond,mutex) do{SignalObjectAndWait(*mutex, *cond, INFINITE, FALSE); WaitForSingleObject(*mutex, INFINITE);}while(0) +#define THREAD_CREATE(thr,start,arg) \ + (((thr) = CreateThread(NULL, 0, start, arg, 0, NULL)) ? 0 : ErrCode()) +#define THREAD_FINISH(thr) \ + (WaitForSingleObject(thr, INFINITE) ? ErrCode() : 0) +#define LOCK_MUTEX0(mutex) WaitForSingleObject(mutex, INFINITE) +#define UNLOCK_MUTEX(mutex) ReleaseMutex(mutex) +#define mdb_mutex_consistent(mutex) 0 +#define getpid() GetCurrentProcessId() +#define MDB_FDATASYNC(fd) (!FlushFileBuffers(fd)) +#define MDB_MSYNC(addr,len,flags) (!FlushViewOfFile(addr,len)) +#define ErrCode() GetLastError() +#define GET_PAGESIZE(x) {SYSTEM_INFO si; GetSystemInfo(&si); (x) = si.dwPageSize;} +#define close(fd) (CloseHandle(fd) ? 0 : -1) +#define munmap(ptr,len) UnmapViewOfFile(ptr) +#ifdef PROCESS_QUERY_LIMITED_INFORMATION +#define MDB_PROCESS_QUERY_LIMITED_INFORMATION PROCESS_QUERY_LIMITED_INFORMATION +#else +#define MDB_PROCESS_QUERY_LIMITED_INFORMATION 0x1000 +#endif +#define Z "I" +#else +#define THREAD_RET void * +#define THREAD_CREATE(thr,start,arg) pthread_create(&thr,NULL,start,arg) +#define THREAD_FINISH(thr) pthread_join(thr,NULL) +#define Z "z" /**< printf format modifier for size_t */ + + /** For MDB_LOCK_FORMAT: True if readers take a pid lock in the lockfile */ +#define MDB_PIDLOCK 1 + +#ifdef MDB_USE_POSIX_SEM + +typedef sem_t *mdb_mutex_t, *mdb_mutexref_t; +#define LOCK_MUTEX0(mutex) mdb_sem_wait(mutex) +#define UNLOCK_MUTEX(mutex) sem_post(mutex) + +static int +mdb_sem_wait(sem_t *sem) +{ + int rc; + while ((rc = sem_wait(sem)) && (rc = errno) == EINTR) ; + return rc; +} + +#else /* MDB_USE_POSIX_MUTEX: */ + /** Shared mutex/semaphore as the original is stored. + * + * Not for copies. Instead it can be assigned to an #mdb_mutexref_t. + * When mdb_mutexref_t is a pointer and mdb_mutex_t is not, then it + * is array[size 1] so it can be assigned to the pointer. + */ +typedef pthread_mutex_t mdb_mutex_t[1]; + /** Reference to an #mdb_mutex_t */ +typedef pthread_mutex_t *mdb_mutexref_t; + /** Lock the reader or writer mutex. + * Returns 0 or a code to give #mdb_mutex_failed(), as in #LOCK_MUTEX(). + */ +#define LOCK_MUTEX0(mutex) pthread_mutex_lock(mutex) + /** Unlock the reader or writer mutex. + */ +#define UNLOCK_MUTEX(mutex) pthread_mutex_unlock(mutex) + /** Mark mutex-protected data as repaired, after death of previous owner. + */ +#define mdb_mutex_consistent(mutex) pthread_mutex_consistent(mutex) +#endif /* MDB_USE_POSIX_SEM */ + + /** Get the error code for the last failed system function. + */ +#define ErrCode() errno + + /** An abstraction for a file handle. + * On POSIX systems file handles are small integers. On Windows + * they're opaque pointers. + */ +#define HANDLE int + + /** A value for an invalid file handle. + * Mainly used to initialize file variables and signify that they are + * unused. + */ +#define INVALID_HANDLE_VALUE (-1) + + /** Get the size of a memory page for the system. + * This is the basic size that the platform's memory manager uses, and is + * fundamental to the use of memory-mapped files. + */ +#define GET_PAGESIZE(x) ((x) = sysconf(_SC_PAGE_SIZE)) +#endif + +#if defined(_WIN32) || defined(MDB_USE_POSIX_SEM) +#define MNAME_LEN 32 +#else +#define MNAME_LEN (sizeof(pthread_mutex_t)) +#endif + +/** @} */ + +#ifdef MDB_ROBUST_SUPPORTED + /** Lock mutex, handle any error, set rc = result. + * Return 0 on success, nonzero (not rc) on error. + */ +#define LOCK_MUTEX(rc, env, mutex) \ + (((rc) = LOCK_MUTEX0(mutex)) && \ + ((rc) = mdb_mutex_failed(env, mutex, rc))) +static int mdb_mutex_failed(MDB_env *env, mdb_mutexref_t mutex, int rc); +#else +#define LOCK_MUTEX(rc, env, mutex) ((rc) = LOCK_MUTEX0(mutex)) +#define mdb_mutex_failed(env, mutex, rc) (rc) +#endif + +#ifndef _WIN32 +/** A flag for opening a file and requesting synchronous data writes. + * This is only used when writing a meta page. It's not strictly needed; + * we could just do a normal write and then immediately perform a flush. + * But if this flag is available it saves us an extra system call. + * + * @note If O_DSYNC is undefined but exists in /usr/include, + * preferably set some compiler flag to get the definition. + */ +#ifndef MDB_DSYNC +# ifdef O_DSYNC +# define MDB_DSYNC O_DSYNC +# else +# define MDB_DSYNC O_SYNC +# endif +#endif +#endif + +/** Function for flushing the data of a file. Define this to fsync + * if fdatasync() is not supported. + */ +#ifndef MDB_FDATASYNC +# define MDB_FDATASYNC fdatasync +#endif + +#ifndef MDB_MSYNC +# define MDB_MSYNC(addr,len,flags) msync(addr,len,flags) +#endif + +#ifndef MS_SYNC +#define MS_SYNC 1 +#endif + +#ifndef MS_ASYNC +#define MS_ASYNC 0 +#endif + + /** A page number in the database. + * Note that 64 bit page numbers are overkill, since pages themselves + * already represent 12-13 bits of addressable memory, and the OS will + * always limit applications to a maximum of 63 bits of address space. + * + * @note In the #MDB_node structure, we only store 48 bits of this value, + * which thus limits us to only 60 bits of addressable data. + */ +typedef MDB_ID pgno_t; + + /** A transaction ID. + * See struct MDB_txn.mt_txnid for details. + */ +typedef MDB_ID txnid_t; + +/** @defgroup debug Debug Macros + * @{ + */ +#ifndef MDB_DEBUG + /** Enable debug output. Needs variable argument macros (a C99 feature). + * Set this to 1 for copious tracing. Set to 2 to add dumps of all IDLs + * read from and written to the database (used for free space management). + */ +#define MDB_DEBUG 0 +#endif + +#if MDB_DEBUG +static int mdb_debug; +static txnid_t mdb_debug_start; + + /** Print a debug message with printf formatting. + * Requires double parenthesis around 2 or more args. + */ +# define DPRINTF(args) ((void) ((mdb_debug) && DPRINTF0 args)) +# define DPRINTF0(fmt, ...) \ + fprintf(stderr, "%s:%d " fmt "\n", mdb_func_, __LINE__, __VA_ARGS__) +#else +# define DPRINTF(args) ((void) 0) +#endif + /** Print a debug string. + * The string is printed literally, with no format processing. + */ +#define DPUTS(arg) DPRINTF(("%s", arg)) + /** Debuging output value of a cursor DBI: Negative in a sub-cursor. */ +#define DDBI(mc) \ + (((mc)->mc_flags & C_SUB) ? -(int)(mc)->mc_dbi : (int)(mc)->mc_dbi) +/** @} */ + + /** @brief The maximum size of a database page. + * + * It is 32k or 64k, since value-PAGEBASE must fit in + * #MDB_page.%mp_upper. + * + * LMDB will use database pages < OS pages if needed. + * That causes more I/O in write transactions: The OS must + * know (read) the whole page before writing a partial page. + * + * Note that we don't currently support Huge pages. On Linux, + * regular data files cannot use Huge pages, and in general + * Huge pages aren't actually pageable. We rely on the OS + * demand-pager to read our data and page it out when memory + * pressure from other processes is high. So until OSs have + * actual paging support for Huge pages, they're not viable. + */ +#define MAX_PAGESIZE (PAGEBASE ? 0x10000 : 0x8000) + + /** The minimum number of keys required in a database page. + * Setting this to a larger value will place a smaller bound on the + * maximum size of a data item. Data items larger than this size will + * be pushed into overflow pages instead of being stored directly in + * the B-tree node. This value used to default to 4. With a page size + * of 4096 bytes that meant that any item larger than 1024 bytes would + * go into an overflow page. That also meant that on average 2-3KB of + * each overflow page was wasted space. The value cannot be lower than + * 2 because then there would no longer be a tree structure. With this + * value, items larger than 2KB will go into overflow pages, and on + * average only 1KB will be wasted. + */ +#define MDB_MINKEYS 2 + + /** A stamp that identifies a file as an LMDB file. + * There's nothing special about this value other than that it is easily + * recognizable, and it will reflect any byte order mismatches. + */ +#define MDB_MAGIC 0xBEEFC0DE + + /** The version number for a database's datafile format. */ +#define MDB_DATA_VERSION ((MDB_DEVEL) ? 999 : 1) + /** The version number for a database's lockfile format. */ +#define MDB_LOCK_VERSION 1 + + /** @brief The max size of a key we can write, or 0 for computed max. + * + * This macro should normally be left alone or set to 0. + * Note that a database with big keys or dupsort data cannot be + * reliably modified by a liblmdb which uses a smaller max. + * The default is 511 for backwards compat, or 0 when #MDB_DEVEL. + * + * Other values are allowed, for backwards compat. However: + * A value bigger than the computed max can break if you do not + * know what you are doing, and liblmdb <= 0.9.10 can break when + * modifying a DB with keys/dupsort data bigger than its max. + * + * Data items in an #MDB_DUPSORT database are also limited to + * this size, since they're actually keys of a sub-DB. Keys and + * #MDB_DUPSORT data items must fit on a node in a regular page. + */ +#ifndef MDB_MAXKEYSIZE +#define MDB_MAXKEYSIZE ((MDB_DEVEL) ? 0 : 511) +#endif + + /** The maximum size of a key we can write to the environment. */ +#if MDB_MAXKEYSIZE +#define ENV_MAXKEY(env) (MDB_MAXKEYSIZE) +#else +#define ENV_MAXKEY(env) ((env)->me_maxkey) +#endif + + /** @brief The maximum size of a data item. + * + * We only store a 32 bit value for node sizes. + */ +#define MAXDATASIZE 0xffffffffUL + +#if MDB_DEBUG + /** Key size which fits in a #DKBUF. + * @ingroup debug + */ +#define DKBUF_MAXKEYSIZE ((MDB_MAXKEYSIZE) > 0 ? (MDB_MAXKEYSIZE) : 511) + /** A key buffer. + * @ingroup debug + * This is used for printing a hex dump of a key's contents. + */ +#define DKBUF char kbuf[DKBUF_MAXKEYSIZE*2+1] + /** Display a key in hex. + * @ingroup debug + * Invoke a function to display a key in hex. + */ +#define DKEY(x) mdb_dkey(x, kbuf) +#else +#define DKBUF +#define DKEY(x) 0 +#endif + + /** An invalid page number. + * Mainly used to denote an empty tree. + */ +#define P_INVALID (~(pgno_t)0) + + /** Test if the flags \b f are set in a flag word \b w. */ +#define F_ISSET(w, f) (((w) & (f)) == (f)) + + /** Round \b n up to an even number. */ +#define EVEN(n) (((n) + 1U) & -2) /* sign-extending -2 to match n+1U */ + + /** Used for offsets within a single page. + * Since memory pages are typically 4 or 8KB in size, 12-13 bits, + * this is plenty. + */ +typedef uint16_t indx_t; + + /** Default size of memory map. + * This is certainly too small for any actual applications. Apps should always set + * the size explicitly using #mdb_env_set_mapsize(). + */ +#define DEFAULT_MAPSIZE 1048576 + +/** @defgroup readers Reader Lock Table + * Readers don't acquire any locks for their data access. Instead, they + * simply record their transaction ID in the reader table. The reader + * mutex is needed just to find an empty slot in the reader table. The + * slot's address is saved in thread-specific data so that subsequent read + * transactions started by the same thread need no further locking to proceed. + * + * If #MDB_NOTLS is set, the slot address is not saved in thread-specific data. + * + * No reader table is used if the database is on a read-only filesystem, or + * if #MDB_NOLOCK is set. + * + * Since the database uses multi-version concurrency control, readers don't + * actually need any locking. This table is used to keep track of which + * readers are using data from which old transactions, so that we'll know + * when a particular old transaction is no longer in use. Old transactions + * that have discarded any data pages can then have those pages reclaimed + * for use by a later write transaction. + * + * The lock table is constructed such that reader slots are aligned with the + * processor's cache line size. Any slot is only ever used by one thread. + * This alignment guarantees that there will be no contention or cache + * thrashing as threads update their own slot info, and also eliminates + * any need for locking when accessing a slot. + * + * A writer thread will scan every slot in the table to determine the oldest + * outstanding reader transaction. Any freed pages older than this will be + * reclaimed by the writer. The writer doesn't use any locks when scanning + * this table. This means that there's no guarantee that the writer will + * see the most up-to-date reader info, but that's not required for correct + * operation - all we need is to know the upper bound on the oldest reader, + * we don't care at all about the newest reader. So the only consequence of + * reading stale information here is that old pages might hang around a + * while longer before being reclaimed. That's actually good anyway, because + * the longer we delay reclaiming old pages, the more likely it is that a + * string of contiguous pages can be found after coalescing old pages from + * many old transactions together. + * @{ + */ + /** Number of slots in the reader table. + * This value was chosen somewhat arbitrarily. 126 readers plus a + * couple mutexes fit exactly into 8KB on my development machine. + * Applications should set the table size using #mdb_env_set_maxreaders(). + */ +#define DEFAULT_READERS 126 + + /** The size of a CPU cache line in bytes. We want our lock structures + * aligned to this size to avoid false cache line sharing in the + * lock table. + * This value works for most CPUs. For Itanium this should be 128. + */ +#ifndef CACHELINE +#define CACHELINE 64 +#endif + + /** The information we store in a single slot of the reader table. + * In addition to a transaction ID, we also record the process and + * thread ID that owns a slot, so that we can detect stale information, + * e.g. threads or processes that went away without cleaning up. + * @note We currently don't check for stale records. We simply re-init + * the table when we know that we're the only process opening the + * lock file. + */ +typedef struct MDB_rxbody { + /** Current Transaction ID when this transaction began, or (txnid_t)-1. + * Multiple readers that start at the same time will probably have the + * same ID here. Again, it's not important to exclude them from + * anything; all we need to know is which version of the DB they + * started from so we can avoid overwriting any data used in that + * particular version. + */ + volatile txnid_t mrb_txnid; + /** The process ID of the process owning this reader txn. */ + volatile MDB_PID_T mrb_pid; + /** The thread ID of the thread owning this txn. */ + volatile MDB_THR_T mrb_tid; +} MDB_rxbody; + + /** The actual reader record, with cacheline padding. */ +typedef struct MDB_reader { + union { + MDB_rxbody mrx; + /** shorthand for mrb_txnid */ +#define mr_txnid mru.mrx.mrb_txnid +#define mr_pid mru.mrx.mrb_pid +#define mr_tid mru.mrx.mrb_tid + /** cache line alignment */ + char pad[(sizeof(MDB_rxbody)+CACHELINE-1) & ~(CACHELINE-1)]; + } mru; +} MDB_reader; + + /** The header for the reader table. + * The table resides in a memory-mapped file. (This is a different file + * than is used for the main database.) + * + * For POSIX the actual mutexes reside in the shared memory of this + * mapped file. On Windows, mutexes are named objects allocated by the + * kernel; we store the mutex names in this mapped file so that other + * processes can grab them. This same approach is also used on + * MacOSX/Darwin (using named semaphores) since MacOSX doesn't support + * process-shared POSIX mutexes. For these cases where a named object + * is used, the object name is derived from a 64 bit FNV hash of the + * environment pathname. As such, naming collisions are extremely + * unlikely. If a collision occurs, the results are unpredictable. + */ +typedef struct MDB_txbody { + /** Stamp identifying this as an LMDB file. It must be set + * to #MDB_MAGIC. */ + uint32_t mtb_magic; + /** Format of this lock file. Must be set to #MDB_LOCK_FORMAT. */ + uint32_t mtb_format; +#if defined(_WIN32) || defined(MDB_USE_POSIX_SEM) + char mtb_rmname[MNAME_LEN]; +#else + /** Mutex protecting access to this table. + * This is the reader table lock used with LOCK_MUTEX(). + */ + mdb_mutex_t mtb_rmutex; +#endif + /** The ID of the last transaction committed to the database. + * This is recorded here only for convenience; the value can always + * be determined by reading the main database meta pages. + */ + volatile txnid_t mtb_txnid; + /** The number of slots that have been used in the reader table. + * This always records the maximum count, it is not decremented + * when readers release their slots. + */ + volatile unsigned mtb_numreaders; +} MDB_txbody; + + /** The actual reader table definition. */ +typedef struct MDB_txninfo { + union { + MDB_txbody mtb; +#define mti_magic mt1.mtb.mtb_magic +#define mti_format mt1.mtb.mtb_format +#define mti_rmutex mt1.mtb.mtb_rmutex +#define mti_rmname mt1.mtb.mtb_rmname +#define mti_txnid mt1.mtb.mtb_txnid +#define mti_numreaders mt1.mtb.mtb_numreaders + char pad[(sizeof(MDB_txbody)+CACHELINE-1) & ~(CACHELINE-1)]; + } mt1; + union { +#if defined(_WIN32) || defined(MDB_USE_POSIX_SEM) + char mt2_wmname[MNAME_LEN]; +#define mti_wmname mt2.mt2_wmname +#else + mdb_mutex_t mt2_wmutex; +#define mti_wmutex mt2.mt2_wmutex +#endif + char pad[(MNAME_LEN+CACHELINE-1) & ~(CACHELINE-1)]; + } mt2; + MDB_reader mti_readers[1]; +} MDB_txninfo; + + /** Lockfile format signature: version, features and field layout */ +#define MDB_LOCK_FORMAT \ + ((uint32_t) \ + ((MDB_LOCK_VERSION) \ + /* Flags which describe functionality */ \ + + (((MDB_PIDLOCK) != 0) << 16))) +/** @} */ + +/** Common header for all page types. The page type depends on #mp_flags. + * + * #P_BRANCH and #P_LEAF pages have unsorted '#MDB_node's at the end, with + * sorted #mp_ptrs[] entries referring to them. Exception: #P_LEAF2 pages + * omit mp_ptrs and pack sorted #MDB_DUPFIXED values after the page header. + * + * #P_OVERFLOW records occupy one or more contiguous pages where only the + * first has a page header. They hold the real data of #F_BIGDATA nodes. + * + * #P_SUBP sub-pages are small leaf "pages" with duplicate data. + * A node with flag #F_DUPDATA but not #F_SUBDATA contains a sub-page. + * (Duplicate data can also go in sub-databases, which use normal pages.) + * + * #P_META pages contain #MDB_meta, the start point of an LMDB snapshot. + * + * Each non-metapage up to #MDB_meta.%mm_last_pg is reachable exactly once + * in the snapshot: Either used by a database or listed in a freeDB record. + */ +typedef struct MDB_page { +#define mp_pgno mp_p.p_pgno +#define mp_next mp_p.p_next + union { + pgno_t p_pgno; /**< page number */ + struct MDB_page *p_next; /**< for in-memory list of freed pages */ + } mp_p; + uint16_t mp_pad; /**< key size if this is a LEAF2 page */ +/** @defgroup mdb_page Page Flags + * @ingroup internal + * Flags for the page headers. + * @{ + */ +#define P_BRANCH 0x01 /**< branch page */ +#define P_LEAF 0x02 /**< leaf page */ +#define P_OVERFLOW 0x04 /**< overflow page */ +#define P_META 0x08 /**< meta page */ +#define P_DIRTY 0x10 /**< dirty page, also set for #P_SUBP pages */ +#define P_LEAF2 0x20 /**< for #MDB_DUPFIXED records */ +#define P_SUBP 0x40 /**< for #MDB_DUPSORT sub-pages */ +#define P_LOOSE 0x4000 /**< page was dirtied then freed, can be reused */ +#define P_KEEP 0x8000 /**< leave this page alone during spill */ +/** @} */ + uint16_t mp_flags; /**< @ref mdb_page */ +#define mp_lower mp_pb.pb.pb_lower +#define mp_upper mp_pb.pb.pb_upper +#define mp_pages mp_pb.pb_pages + union { + struct { + indx_t pb_lower; /**< lower bound of free space */ + indx_t pb_upper; /**< upper bound of free space */ + } pb; + uint32_t pb_pages; /**< number of overflow pages */ + } mp_pb; + indx_t mp_ptrs[1]; /**< dynamic size */ +} MDB_page; + + /** Size of the page header, excluding dynamic data at the end */ +#define PAGEHDRSZ ((unsigned) offsetof(MDB_page, mp_ptrs)) + + /** Address of first usable data byte in a page, after the header */ +#define METADATA(p) ((void *)((char *)(p) + PAGEHDRSZ)) + + /** ITS#7713, change PAGEBASE to handle 65536 byte pages */ +#define PAGEBASE ((MDB_DEVEL) ? PAGEHDRSZ : 0) + + /** Number of nodes on a page */ +#define NUMKEYS(p) (((p)->mp_lower - (PAGEHDRSZ-PAGEBASE)) >> 1) + + /** The amount of space remaining in the page */ +#define SIZELEFT(p) (indx_t)((p)->mp_upper - (p)->mp_lower) + + /** The percentage of space used in the page, in tenths of a percent. */ +#define PAGEFILL(env, p) (1000L * ((env)->me_psize - PAGEHDRSZ - SIZELEFT(p)) / \ + ((env)->me_psize - PAGEHDRSZ)) + /** The minimum page fill factor, in tenths of a percent. + * Pages emptier than this are candidates for merging. + */ +#define FILL_THRESHOLD 250 + + /** Test if a page is a leaf page */ +#define IS_LEAF(p) F_ISSET((p)->mp_flags, P_LEAF) + /** Test if a page is a LEAF2 page */ +#define IS_LEAF2(p) F_ISSET((p)->mp_flags, P_LEAF2) + /** Test if a page is a branch page */ +#define IS_BRANCH(p) F_ISSET((p)->mp_flags, P_BRANCH) + /** Test if a page is an overflow page */ +#define IS_OVERFLOW(p) F_ISSET((p)->mp_flags, P_OVERFLOW) + /** Test if a page is a sub page */ +#define IS_SUBP(p) F_ISSET((p)->mp_flags, P_SUBP) + + /** The number of overflow pages needed to store the given size. */ +#define OVPAGES(size, psize) ((PAGEHDRSZ-1 + (size)) / (psize) + 1) + + /** Link in #MDB_txn.%mt_loose_pgs list. + * Kept outside the page header, which is needed when reusing the page. + */ +#define NEXT_LOOSE_PAGE(p) (*(MDB_page **)((p) + 2)) + + /** Header for a single key/data pair within a page. + * Used in pages of type #P_BRANCH and #P_LEAF without #P_LEAF2. + * We guarantee 2-byte alignment for 'MDB_node's. + * + * #mn_lo and #mn_hi are used for data size on leaf nodes, and for child + * pgno on branch nodes. On 64 bit platforms, #mn_flags is also used + * for pgno. (Branch nodes have no flags). Lo and hi are in host byte + * order in case some accesses can be optimized to 32-bit word access. + * + * Leaf node flags describe node contents. #F_BIGDATA says the node's + * data part is the page number of an overflow page with actual data. + * #F_DUPDATA and #F_SUBDATA can be combined giving duplicate data in + * a sub-page/sub-database, and named databases (just #F_SUBDATA). + */ +typedef struct MDB_node { + /** part of data size or pgno + * @{ */ +#if BYTE_ORDER == LITTLE_ENDIAN + unsigned short mn_lo, mn_hi; +#else + unsigned short mn_hi, mn_lo; +#endif + /** @} */ +/** @defgroup mdb_node Node Flags + * @ingroup internal + * Flags for node headers. + * @{ + */ +#define F_BIGDATA 0x01 /**< data put on overflow page */ +#define F_SUBDATA 0x02 /**< data is a sub-database */ +#define F_DUPDATA 0x04 /**< data has duplicates */ + +/** valid flags for #mdb_node_add() */ +#define NODE_ADD_FLAGS (F_DUPDATA|F_SUBDATA|MDB_RESERVE|MDB_APPEND) + +/** @} */ + unsigned short mn_flags; /**< @ref mdb_node */ + unsigned short mn_ksize; /**< key size */ + char mn_data[1]; /**< key and data are appended here */ +} MDB_node; + + /** Size of the node header, excluding dynamic data at the end */ +#define NODESIZE offsetof(MDB_node, mn_data) + + /** Bit position of top word in page number, for shifting mn_flags */ +#define PGNO_TOPWORD ((pgno_t)-1 > 0xffffffffu ? 32 : 0) + + /** Size of a node in a branch page with a given key. + * This is just the node header plus the key, there is no data. + */ +#define INDXSIZE(k) (NODESIZE + ((k) == NULL ? 0 : (k)->mv_size)) + + /** Size of a node in a leaf page with a given key and data. + * This is node header plus key plus data size. + */ +#define LEAFSIZE(k, d) (NODESIZE + (k)->mv_size + (d)->mv_size) + + /** Address of node \b i in page \b p */ +#define NODEPTR(p, i) ((MDB_node *)((char *)(p) + (p)->mp_ptrs[i] + PAGEBASE)) + + /** Address of the key for the node */ +#define NODEKEY(node) (void *)((node)->mn_data) + + /** Address of the data for a node */ +#define NODEDATA(node) (void *)((char *)(node)->mn_data + (node)->mn_ksize) + + /** Get the page number pointed to by a branch node */ +#define NODEPGNO(node) \ + ((node)->mn_lo | ((pgno_t) (node)->mn_hi << 16) | \ + (PGNO_TOPWORD ? ((pgno_t) (node)->mn_flags << PGNO_TOPWORD) : 0)) + /** Set the page number in a branch node */ +#define SETPGNO(node,pgno) do { \ + (node)->mn_lo = (pgno) & 0xffff; (node)->mn_hi = (pgno) >> 16; \ + if (PGNO_TOPWORD) (node)->mn_flags = (pgno) >> PGNO_TOPWORD; } while(0) + + /** Get the size of the data in a leaf node */ +#define NODEDSZ(node) ((node)->mn_lo | ((unsigned)(node)->mn_hi << 16)) + /** Set the size of the data for a leaf node */ +#define SETDSZ(node,size) do { \ + (node)->mn_lo = (size) & 0xffff; (node)->mn_hi = (size) >> 16;} while(0) + /** The size of a key in a node */ +#define NODEKSZ(node) ((node)->mn_ksize) + + /** Copy a page number from src to dst */ +#ifdef MISALIGNED_OK +#define COPY_PGNO(dst,src) dst = src +#else +#if SIZE_MAX > 4294967295UL +#define COPY_PGNO(dst,src) do { \ + unsigned short *s, *d; \ + s = (unsigned short *)&(src); \ + d = (unsigned short *)&(dst); \ + *d++ = *s++; \ + *d++ = *s++; \ + *d++ = *s++; \ + *d = *s; \ +} while (0) +#else +#define COPY_PGNO(dst,src) do { \ + unsigned short *s, *d; \ + s = (unsigned short *)&(src); \ + d = (unsigned short *)&(dst); \ + *d++ = *s++; \ + *d = *s; \ +} while (0) +#endif +#endif + /** The address of a key in a LEAF2 page. + * LEAF2 pages are used for #MDB_DUPFIXED sorted-duplicate sub-DBs. + * There are no node headers, keys are stored contiguously. + */ +#define LEAF2KEY(p, i, ks) ((char *)(p) + PAGEHDRSZ + ((i)*(ks))) + + /** Set the \b node's key into \b keyptr, if requested. */ +#define MDB_GET_KEY(node, keyptr) { if ((keyptr) != NULL) { \ + (keyptr)->mv_size = NODEKSZ(node); (keyptr)->mv_data = NODEKEY(node); } } + + /** Set the \b node's key into \b key. */ +#define MDB_GET_KEY2(node, key) { key.mv_size = NODEKSZ(node); key.mv_data = NODEKEY(node); } + + /** Information about a single database in the environment. */ +typedef struct MDB_db { + uint32_t md_pad; /**< also ksize for LEAF2 pages */ + uint16_t md_flags; /**< @ref mdb_dbi_open */ + uint16_t md_depth; /**< depth of this tree */ + pgno_t md_branch_pages; /**< number of internal pages */ + pgno_t md_leaf_pages; /**< number of leaf pages */ + pgno_t md_overflow_pages; /**< number of overflow pages */ + size_t md_entries; /**< number of data items */ + pgno_t md_root; /**< the root page of this tree */ +} MDB_db; + +#define MDB_VALID 0x8000 /**< DB handle is valid, for me_dbflags */ +#define PERSISTENT_FLAGS (0xffff & ~(MDB_VALID)) + /** #mdb_dbi_open() flags */ +#define VALID_FLAGS (MDB_REVERSEKEY|MDB_DUPSORT|MDB_INTEGERKEY|MDB_DUPFIXED|\ + MDB_INTEGERDUP|MDB_REVERSEDUP|MDB_CREATE) + + /** Handle for the DB used to track free pages. */ +#define FREE_DBI 0 + /** Handle for the default DB. */ +#define MAIN_DBI 1 + /** Number of DBs in metapage (free and main) - also hardcoded elsewhere */ +#define CORE_DBS 2 + + /** Number of meta pages - also hardcoded elsewhere */ +#define NUM_METAS 2 + + /** Meta page content. + * A meta page is the start point for accessing a database snapshot. + * Pages 0-1 are meta pages. Transaction N writes meta page #(N % 2). + */ +typedef struct MDB_meta { + /** Stamp identifying this as an LMDB file. It must be set + * to #MDB_MAGIC. */ + uint32_t mm_magic; + /** Version number of this file. Must be set to #MDB_DATA_VERSION. */ + uint32_t mm_version; + void *mm_address; /**< address for fixed mapping */ + size_t mm_mapsize; /**< size of mmap region */ + MDB_db mm_dbs[CORE_DBS]; /**< first is free space, 2nd is main db */ + /** The size of pages used in this DB */ +#define mm_psize mm_dbs[FREE_DBI].md_pad + /** Any persistent environment flags. @ref mdb_env */ +#define mm_flags mm_dbs[FREE_DBI].md_flags + /** Last used page in the datafile. + * Actually the file may be shorter if the freeDB lists the final pages. + */ + pgno_t mm_last_pg; + volatile txnid_t mm_txnid; /**< txnid that committed this page */ +} MDB_meta; + + /** Buffer for a stack-allocated meta page. + * The members define size and alignment, and silence type + * aliasing warnings. They are not used directly; that could + * mean incorrectly using several union members in parallel. + */ +typedef union MDB_metabuf { + MDB_page mb_page; + struct { + char mm_pad[PAGEHDRSZ]; + MDB_meta mm_meta; + } mb_metabuf; +} MDB_metabuf; + + /** Auxiliary DB info. + * The information here is mostly static/read-only. There is + * only a single copy of this record in the environment. + */ +typedef struct MDB_dbx { + MDB_val md_name; /**< name of the database */ + MDB_cmp_func *md_cmp; /**< function for comparing keys */ + MDB_cmp_func *md_dcmp; /**< function for comparing data items */ + MDB_rel_func *md_rel; /**< user relocate function */ + void *md_relctx; /**< user-provided context for md_rel */ +} MDB_dbx; + + /** A database transaction. + * Every operation requires a transaction handle. + */ +struct MDB_txn { + MDB_txn *mt_parent; /**< parent of a nested txn */ + /** Nested txn under this txn, set together with flag #MDB_TXN_HAS_CHILD */ + MDB_txn *mt_child; + pgno_t mt_next_pgno; /**< next unallocated page */ + /** The ID of this transaction. IDs are integers incrementing from 1. + * Only committed write transactions increment the ID. If a transaction + * aborts, the ID may be re-used by the next writer. + */ + txnid_t mt_txnid; + MDB_env *mt_env; /**< the DB environment */ + /** The list of pages that became unused during this transaction. + */ + MDB_IDL mt_free_pgs; + /** The list of loose pages that became unused and may be reused + * in this transaction, linked through #NEXT_LOOSE_PAGE(page). + */ + MDB_page *mt_loose_pgs; + /** Number of loose pages (#mt_loose_pgs) */ + int mt_loose_count; + /** The sorted list of dirty pages we temporarily wrote to disk + * because the dirty list was full. page numbers in here are + * shifted left by 1, deleted slots have the LSB set. + */ + MDB_IDL mt_spill_pgs; + union { + /** For write txns: Modified pages. Sorted when not MDB_WRITEMAP. */ + MDB_ID2L dirty_list; + /** For read txns: This thread/txn's reader table slot, or NULL. */ + MDB_reader *reader; + } mt_u; + /** Array of records for each DB known in the environment. */ + MDB_dbx *mt_dbxs; + /** Array of MDB_db records for each known DB */ + MDB_db *mt_dbs; + /** Array of sequence numbers for each DB handle */ + unsigned int *mt_dbiseqs; +/** @defgroup mt_dbflag Transaction DB Flags + * @ingroup internal + * @{ + */ +#define DB_DIRTY 0x01 /**< DB was written in this txn */ +#define DB_STALE 0x02 /**< Named-DB record is older than txnID */ +#define DB_NEW 0x04 /**< Named-DB handle opened in this txn */ +#define DB_VALID 0x08 /**< DB handle is valid, see also #MDB_VALID */ +#define DB_USRVALID 0x10 /**< As #DB_VALID, but not set for #FREE_DBI */ +#define DB_DUPDATA 0x20 /**< DB is #MDB_DUPSORT data */ +/** @} */ + /** In write txns, array of cursors for each DB */ + MDB_cursor **mt_cursors; + /** Array of flags for each DB */ + unsigned char *mt_dbflags; + /** Number of DB records in use, or 0 when the txn is finished. + * This number only ever increments until the txn finishes; we + * don't decrement it when individual DB handles are closed. + */ + MDB_dbi mt_numdbs; + +/** @defgroup mdb_txn Transaction Flags + * @ingroup internal + * @{ + */ + /** #mdb_txn_begin() flags */ +#define MDB_TXN_BEGIN_FLAGS MDB_RDONLY +#define MDB_TXN_RDONLY MDB_RDONLY /**< read-only transaction */ + /* internal txn flags */ +#define MDB_TXN_WRITEMAP MDB_WRITEMAP /**< copy of #MDB_env flag in writers */ +#define MDB_TXN_FINISHED 0x01 /**< txn is finished or never began */ +#define MDB_TXN_ERROR 0x02 /**< txn is unusable after an error */ +#define MDB_TXN_DIRTY 0x04 /**< must write, even if dirty list is empty */ +#define MDB_TXN_SPILLS 0x08 /**< txn or a parent has spilled pages */ +#define MDB_TXN_HAS_CHILD 0x10 /**< txn has an #MDB_txn.%mt_child */ + /** most operations on the txn are currently illegal */ +#define MDB_TXN_BLOCKED (MDB_TXN_FINISHED|MDB_TXN_ERROR|MDB_TXN_HAS_CHILD) +/** @} */ + unsigned int mt_flags; /**< @ref mdb_txn */ + /** #dirty_list room: Array size - \#dirty pages visible to this txn. + * Includes ancestor txns' dirty pages not hidden by other txns' + * dirty/spilled pages. Thus commit(nested txn) has room to merge + * dirty_list into mt_parent after freeing hidden mt_parent pages. + */ + unsigned int mt_dirty_room; +}; + +/** Enough space for 2^32 nodes with minimum of 2 keys per node. I.e., plenty. + * At 4 keys per node, enough for 2^64 nodes, so there's probably no need to + * raise this on a 64 bit machine. + */ +#define CURSOR_STACK 32 + +struct MDB_xcursor; + + /** Cursors are used for all DB operations. + * A cursor holds a path of (page pointer, key index) from the DB + * root to a position in the DB, plus other state. #MDB_DUPSORT + * cursors include an xcursor to the current data item. Write txns + * track their cursors and keep them up to date when data moves. + * Exception: An xcursor's pointer to a #P_SUBP page can be stale. + * (A node with #F_DUPDATA but no #F_SUBDATA contains a subpage). + */ +struct MDB_cursor { + /** Next cursor on this DB in this txn */ + MDB_cursor *mc_next; + /** Backup of the original cursor if this cursor is a shadow */ + MDB_cursor *mc_backup; + /** Context used for databases with #MDB_DUPSORT, otherwise NULL */ + struct MDB_xcursor *mc_xcursor; + /** The transaction that owns this cursor */ + MDB_txn *mc_txn; + /** The database handle this cursor operates on */ + MDB_dbi mc_dbi; + /** The database record for this cursor */ + MDB_db *mc_db; + /** The database auxiliary record for this cursor */ + MDB_dbx *mc_dbx; + /** The @ref mt_dbflag for this database */ + unsigned char *mc_dbflag; + unsigned short mc_snum; /**< number of pushed pages */ + unsigned short mc_top; /**< index of top page, normally mc_snum-1 */ +/** @defgroup mdb_cursor Cursor Flags + * @ingroup internal + * Cursor state flags. + * @{ + */ +#define C_INITIALIZED 0x01 /**< cursor has been initialized and is valid */ +#define C_EOF 0x02 /**< No more data */ +#define C_SUB 0x04 /**< Cursor is a sub-cursor */ +#define C_DEL 0x08 /**< last op was a cursor_del */ +#define C_UNTRACK 0x40 /**< Un-track cursor when closing */ +/** @} */ + unsigned int mc_flags; /**< @ref mdb_cursor */ + MDB_page *mc_pg[CURSOR_STACK]; /**< stack of pushed pages */ + indx_t mc_ki[CURSOR_STACK]; /**< stack of page indices */ +}; + + /** Context for sorted-dup records. + * We could have gone to a fully recursive design, with arbitrarily + * deep nesting of sub-databases. But for now we only handle these + * levels - main DB, optional sub-DB, sorted-duplicate DB. + */ +typedef struct MDB_xcursor { + /** A sub-cursor for traversing the Dup DB */ + MDB_cursor mx_cursor; + /** The database record for this Dup DB */ + MDB_db mx_db; + /** The auxiliary DB record for this Dup DB */ + MDB_dbx mx_dbx; + /** The @ref mt_dbflag for this Dup DB */ + unsigned char mx_dbflag; +} MDB_xcursor; + + /** Check if there is an inited xcursor */ +#define XCURSOR_INITED(mc) \ + ((mc)->mc_xcursor && ((mc)->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED)) + + /** Update the xcursor's sub-page pointer, if any, in \b mc. Needed + * when the node which contains the sub-page may have moved. Called + * with leaf page \b mp = mc->mc_pg[\b top]. + */ +#define XCURSOR_REFRESH(mc, top, mp) do { \ + MDB_page *xr_pg = (mp); \ + MDB_node *xr_node; \ + if (!XCURSOR_INITED(mc) || (mc)->mc_ki[top] >= NUMKEYS(xr_pg)) break; \ + xr_node = NODEPTR(xr_pg, (mc)->mc_ki[top]); \ + if ((xr_node->mn_flags & (F_DUPDATA|F_SUBDATA)) == F_DUPDATA) \ + (mc)->mc_xcursor->mx_cursor.mc_pg[0] = NODEDATA(xr_node); \ +} while (0) + + /** State of FreeDB old pages, stored in the MDB_env */ +typedef struct MDB_pgstate { + pgno_t *mf_pghead; /**< Reclaimed freeDB pages, or NULL before use */ + txnid_t mf_pglast; /**< ID of last used record, or 0 if !mf_pghead */ +} MDB_pgstate; + + /** The database environment. */ +struct MDB_env { + HANDLE me_fd; /**< The main data file */ + HANDLE me_lfd; /**< The lock file */ + HANDLE me_mfd; /**< For writing and syncing the meta pages */ + /** Failed to update the meta page. Probably an I/O error. */ +#define MDB_FATAL_ERROR 0x80000000U + /** Some fields are initialized. */ +#define MDB_ENV_ACTIVE 0x20000000U + /** me_txkey is set */ +#define MDB_ENV_TXKEY 0x10000000U + /** fdatasync is unreliable */ +#define MDB_FSYNCONLY 0x08000000U + uint32_t me_flags; /**< @ref mdb_env */ + unsigned int me_psize; /**< DB page size, inited from me_os_psize */ + unsigned int me_os_psize; /**< OS page size, from #GET_PAGESIZE */ + unsigned int me_maxreaders; /**< size of the reader table */ + /** Max #MDB_txninfo.%mti_numreaders of interest to #mdb_env_close() */ + volatile int me_close_readers; + MDB_dbi me_numdbs; /**< number of DBs opened */ + MDB_dbi me_maxdbs; /**< size of the DB table */ + MDB_PID_T me_pid; /**< process ID of this env */ + char *me_path; /**< path to the DB files */ + char *me_map; /**< the memory map of the data file */ + MDB_txninfo *me_txns; /**< the memory map of the lock file or NULL */ + MDB_meta *me_metas[NUM_METAS]; /**< pointers to the two meta pages */ + void *me_pbuf; /**< scratch area for DUPSORT put() */ + MDB_txn *me_txn; /**< current write transaction */ + MDB_txn *me_txn0; /**< prealloc'd write transaction */ + size_t me_mapsize; /**< size of the data memory map */ + off_t me_size; /**< current file size */ + pgno_t me_maxpg; /**< me_mapsize / me_psize */ + MDB_dbx *me_dbxs; /**< array of static DB info */ + uint16_t *me_dbflags; /**< array of flags from MDB_db.md_flags */ + unsigned int *me_dbiseqs; /**< array of dbi sequence numbers */ + pthread_key_t me_txkey; /**< thread-key for readers */ + txnid_t me_pgoldest; /**< ID of oldest reader last time we looked */ + MDB_pgstate me_pgstate; /**< state of old pages from freeDB */ +# define me_pglast me_pgstate.mf_pglast +# define me_pghead me_pgstate.mf_pghead + MDB_page *me_dpages; /**< list of malloc'd blocks for re-use */ + /** IDL of pages that became unused in a write txn */ + MDB_IDL me_free_pgs; + /** ID2L of pages written during a write txn. Length MDB_IDL_UM_SIZE. */ + MDB_ID2L me_dirty_list; + /** Max number of freelist items that can fit in a single overflow page */ + int me_maxfree_1pg; + /** Max size of a node on a page */ + unsigned int me_nodemax; +#if !(MDB_MAXKEYSIZE) + unsigned int me_maxkey; /**< max size of a key */ +#endif + int me_live_reader; /**< have liveness lock in reader table */ +#ifdef _WIN32 + int me_pidquery; /**< Used in OpenProcess */ +#endif +#ifdef MDB_USE_POSIX_MUTEX /* Posix mutexes reside in shared mem */ +# define me_rmutex me_txns->mti_rmutex /**< Shared reader lock */ +# define me_wmutex me_txns->mti_wmutex /**< Shared writer lock */ +#else + mdb_mutex_t me_rmutex; + mdb_mutex_t me_wmutex; +#endif + void *me_userctx; /**< User-settable context */ + MDB_assert_func *me_assert_func; /**< Callback for assertion failures */ +}; + + /** Nested transaction */ +typedef struct MDB_ntxn { + MDB_txn mnt_txn; /**< the transaction */ + MDB_pgstate mnt_pgstate; /**< parent transaction's saved freestate */ +} MDB_ntxn; + + /** max number of pages to commit in one writev() call */ +#define MDB_COMMIT_PAGES 64 +#if defined(IOV_MAX) && IOV_MAX < MDB_COMMIT_PAGES +#undef MDB_COMMIT_PAGES +#define MDB_COMMIT_PAGES IOV_MAX +#endif + + /** max bytes to write in one call */ +#define MAX_WRITE (0x40000000U >> (sizeof(ssize_t) == 4)) + + /** Check \b txn and \b dbi arguments to a function */ +#define TXN_DBI_EXIST(txn, dbi, validity) \ + ((txn) && (dbi)<(txn)->mt_numdbs && ((txn)->mt_dbflags[dbi] & (validity))) + + /** Check for misused \b dbi handles */ +#define TXN_DBI_CHANGED(txn, dbi) \ + ((txn)->mt_dbiseqs[dbi] != (txn)->mt_env->me_dbiseqs[dbi]) + +static int mdb_page_alloc(MDB_cursor *mc, int num, MDB_page **mp); +static int mdb_page_new(MDB_cursor *mc, uint32_t flags, int num, MDB_page **mp); +static int mdb_page_touch(MDB_cursor *mc); + +#define MDB_END_NAMES {"committed", "empty-commit", "abort", "reset", \ + "reset-tmp", "fail-begin", "fail-beginchild"} +enum { + /* mdb_txn_end operation number, for logging */ + MDB_END_COMMITTED, MDB_END_EMPTY_COMMIT, MDB_END_ABORT, MDB_END_RESET, + MDB_END_RESET_TMP, MDB_END_FAIL_BEGIN, MDB_END_FAIL_BEGINCHILD +}; +#define MDB_END_OPMASK 0x0F /**< mask for #mdb_txn_end() operation number */ +#define MDB_END_UPDATE 0x10 /**< update env state (DBIs) */ +#define MDB_END_FREE 0x20 /**< free txn unless it is #MDB_env.%me_txn0 */ +#define MDB_END_SLOT MDB_NOTLS /**< release any reader slot if #MDB_NOTLS */ +static void mdb_txn_end(MDB_txn *txn, unsigned mode); + +static int mdb_page_get(MDB_cursor *mc, pgno_t pgno, MDB_page **mp, int *lvl); +static int mdb_page_search_root(MDB_cursor *mc, + MDB_val *key, int modify); +#define MDB_PS_MODIFY 1 +#define MDB_PS_ROOTONLY 2 +#define MDB_PS_FIRST 4 +#define MDB_PS_LAST 8 +static int mdb_page_search(MDB_cursor *mc, + MDB_val *key, int flags); +static int mdb_page_merge(MDB_cursor *csrc, MDB_cursor *cdst); + +#define MDB_SPLIT_REPLACE MDB_APPENDDUP /**< newkey is not new */ +static int mdb_page_split(MDB_cursor *mc, MDB_val *newkey, MDB_val *newdata, + pgno_t newpgno, unsigned int nflags); + +static int mdb_env_read_header(MDB_env *env, MDB_meta *meta); +static MDB_meta *mdb_env_pick_meta(const MDB_env *env); +static int mdb_env_write_meta(MDB_txn *txn); +#ifdef MDB_USE_POSIX_MUTEX /* Drop unused excl arg */ +# define mdb_env_close0(env, excl) mdb_env_close1(env) +#endif +static void mdb_env_close0(MDB_env *env, int excl); + +static MDB_node *mdb_node_search(MDB_cursor *mc, MDB_val *key, int *exactp); +static int mdb_node_add(MDB_cursor *mc, indx_t indx, + MDB_val *key, MDB_val *data, pgno_t pgno, unsigned int flags); +static void mdb_node_del(MDB_cursor *mc, int ksize); +static void mdb_node_shrink(MDB_page *mp, indx_t indx); +static int mdb_node_move(MDB_cursor *csrc, MDB_cursor *cdst, int fromleft); +static int mdb_node_read(MDB_cursor *mc, MDB_node *leaf, MDB_val *data); +static size_t mdb_leaf_size(MDB_env *env, MDB_val *key, MDB_val *data); +static size_t mdb_branch_size(MDB_env *env, MDB_val *key); + +static int mdb_rebalance(MDB_cursor *mc); +static int mdb_update_key(MDB_cursor *mc, MDB_val *key); + +static void mdb_cursor_pop(MDB_cursor *mc); +static int mdb_cursor_push(MDB_cursor *mc, MDB_page *mp); + +static int mdb_cursor_del0(MDB_cursor *mc); +static int mdb_del0(MDB_txn *txn, MDB_dbi dbi, MDB_val *key, MDB_val *data, unsigned flags); +static int mdb_cursor_sibling(MDB_cursor *mc, int move_right); +static int mdb_cursor_next(MDB_cursor *mc, MDB_val *key, MDB_val *data, MDB_cursor_op op); +static int mdb_cursor_prev(MDB_cursor *mc, MDB_val *key, MDB_val *data, MDB_cursor_op op); +static int mdb_cursor_set(MDB_cursor *mc, MDB_val *key, MDB_val *data, MDB_cursor_op op, + int *exactp); +static int mdb_cursor_first(MDB_cursor *mc, MDB_val *key, MDB_val *data); +static int mdb_cursor_last(MDB_cursor *mc, MDB_val *key, MDB_val *data); + +static void mdb_cursor_init(MDB_cursor *mc, MDB_txn *txn, MDB_dbi dbi, MDB_xcursor *mx); +static void mdb_xcursor_init0(MDB_cursor *mc); +static void mdb_xcursor_init1(MDB_cursor *mc, MDB_node *node); +static void mdb_xcursor_init2(MDB_cursor *mc, MDB_xcursor *src_mx, int force); + +static int mdb_drop0(MDB_cursor *mc, int subs); +static void mdb_default_cmp(MDB_txn *txn, MDB_dbi dbi); +static int mdb_reader_check0(MDB_env *env, int rlocked, int *dead); + +/** @cond */ +static MDB_cmp_func mdb_cmp_memn, mdb_cmp_memnr, mdb_cmp_int, mdb_cmp_cint, mdb_cmp_long; +/** @endcond */ + +/** Compare two items pointing at size_t's of unknown alignment. */ +#ifdef MISALIGNED_OK +# define mdb_cmp_clong mdb_cmp_long +#else +# define mdb_cmp_clong mdb_cmp_cint +#endif + +#ifdef _WIN32 +static SECURITY_DESCRIPTOR mdb_null_sd; +static SECURITY_ATTRIBUTES mdb_all_sa; +static int mdb_sec_inited; + +struct MDB_name; +static int utf8_to_utf16(const char *src, struct MDB_name *dst, int xtra); +#endif + +/** Return the library version info. */ +char * ESECT +mdb_version(int *major, int *minor, int *patch) +{ + if (major) *major = MDB_VERSION_MAJOR; + if (minor) *minor = MDB_VERSION_MINOR; + if (patch) *patch = MDB_VERSION_PATCH; + return MDB_VERSION_STRING; +} + +/** Table of descriptions for LMDB @ref errors */ +static char *const mdb_errstr[] = { + "MDB_KEYEXIST: Key/data pair already exists", + "MDB_NOTFOUND: No matching key/data pair found", + "MDB_PAGE_NOTFOUND: Requested page not found", + "MDB_CORRUPTED: Located page was wrong type", + "MDB_PANIC: Update of meta page failed or environment had fatal error", + "MDB_VERSION_MISMATCH: Database environment version mismatch", + "MDB_INVALID: File is not an LMDB file", + "MDB_MAP_FULL: Environment mapsize limit reached", + "MDB_DBS_FULL: Environment maxdbs limit reached", + "MDB_READERS_FULL: Environment maxreaders limit reached", + "MDB_TLS_FULL: Thread-local storage keys full - too many environments open", + "MDB_TXN_FULL: Transaction has too many dirty pages - transaction too big", + "MDB_CURSOR_FULL: Internal error - cursor stack limit reached", + "MDB_PAGE_FULL: Internal error - page has no more space", + "MDB_MAP_RESIZED: Database contents grew beyond environment mapsize", + "MDB_INCOMPATIBLE: Operation and DB incompatible, or DB flags changed", + "MDB_BAD_RSLOT: Invalid reuse of reader locktable slot", + "MDB_BAD_TXN: Transaction must abort, has a child, or is invalid", + "MDB_BAD_VALSIZE: Unsupported size of key/DB name/data, or wrong DUPFIXED size", + "MDB_BAD_DBI: The specified DBI handle was closed/changed unexpectedly", +}; + +char * +mdb_strerror(int err) +{ +#ifdef _WIN32 + /** HACK: pad 4KB on stack over the buf. Return system msgs in buf. + * This works as long as no function between the call to mdb_strerror + * and the actual use of the message uses more than 4K of stack. + */ +#define MSGSIZE 1024 +#define PADSIZE 4096 + char buf[MSGSIZE+PADSIZE], *ptr = buf; +#endif + int i; + if (!err) + return ("Successful return: 0"); + + if (err >= MDB_KEYEXIST && err <= MDB_LAST_ERRCODE) { + i = err - MDB_KEYEXIST; + return mdb_errstr[i]; + } + +#ifdef _WIN32 + /* These are the C-runtime error codes we use. The comment indicates + * their numeric value, and the Win32 error they would correspond to + * if the error actually came from a Win32 API. A major mess, we should + * have used LMDB-specific error codes for everything. + */ + switch(err) { + case ENOENT: /* 2, FILE_NOT_FOUND */ + case EIO: /* 5, ACCESS_DENIED */ + case ENOMEM: /* 12, INVALID_ACCESS */ + case EACCES: /* 13, INVALID_DATA */ + case EBUSY: /* 16, CURRENT_DIRECTORY */ + case EINVAL: /* 22, BAD_COMMAND */ + case ENOSPC: /* 28, OUT_OF_PAPER */ + return strerror(err); + default: + ; + } + buf[0] = 0; + FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, err, 0, ptr, MSGSIZE, (va_list *)buf+MSGSIZE); + return ptr; +#else + return strerror(err); +#endif +} + +/** assert(3) variant in cursor context */ +#define mdb_cassert(mc, expr) mdb_assert0((mc)->mc_txn->mt_env, expr, #expr) +/** assert(3) variant in transaction context */ +#define mdb_tassert(txn, expr) mdb_assert0((txn)->mt_env, expr, #expr) +/** assert(3) variant in environment context */ +#define mdb_eassert(env, expr) mdb_assert0(env, expr, #expr) + +#ifndef NDEBUG +# define mdb_assert0(env, expr, expr_txt) ((expr) ? (void)0 : \ + mdb_assert_fail(env, expr_txt, mdb_func_, __FILE__, __LINE__)) + +static void ESECT +mdb_assert_fail(MDB_env *env, const char *expr_txt, + const char *func, const char *file, int line) +{ + char buf[400]; + sprintf(buf, "%.100s:%d: Assertion '%.200s' failed in %.40s()", + file, line, expr_txt, func); + if (env->me_assert_func) + env->me_assert_func(env, buf); + fprintf(stderr, "%s\n", buf); + abort(); +} +#else +# define mdb_assert0(env, expr, expr_txt) ((void) 0) +#endif /* NDEBUG */ + +#if MDB_DEBUG +/** Return the page number of \b mp which may be sub-page, for debug output */ +static pgno_t +mdb_dbg_pgno(MDB_page *mp) +{ + pgno_t ret; + COPY_PGNO(ret, mp->mp_pgno); + return ret; +} + +/** Display a key in hexadecimal and return the address of the result. + * @param[in] key the key to display + * @param[in] buf the buffer to write into. Should always be #DKBUF. + * @return The key in hexadecimal form. + */ +char * +mdb_dkey(MDB_val *key, char *buf) +{ + char *ptr = buf; + unsigned char *c = key->mv_data; + unsigned int i; + + if (!key) + return ""; + + if (key->mv_size > DKBUF_MAXKEYSIZE) + return "MDB_MAXKEYSIZE"; + /* may want to make this a dynamic check: if the key is mostly + * printable characters, print it as-is instead of converting to hex. + */ +#if 1 + buf[0] = '\0'; + for (i=0; i<key->mv_size; i++) + ptr += sprintf(ptr, "%02x", *c++); +#else + sprintf(buf, "%.*s", key->mv_size, key->mv_data); +#endif + return buf; +} + +static const char * +mdb_leafnode_type(MDB_node *n) +{ + static char *const tp[2][2] = {{"", ": DB"}, {": sub-page", ": sub-DB"}}; + return F_ISSET(n->mn_flags, F_BIGDATA) ? ": overflow page" : + tp[F_ISSET(n->mn_flags, F_DUPDATA)][F_ISSET(n->mn_flags, F_SUBDATA)]; +} + +/** Display all the keys in the page. */ +void +mdb_page_list(MDB_page *mp) +{ + pgno_t pgno = mdb_dbg_pgno(mp); + const char *type, *state = (mp->mp_flags & P_DIRTY) ? ", dirty" : ""; + MDB_node *node; + unsigned int i, nkeys, nsize, total = 0; + MDB_val key; + DKBUF; + + switch (mp->mp_flags & (P_BRANCH|P_LEAF|P_LEAF2|P_META|P_OVERFLOW|P_SUBP)) { + case P_BRANCH: type = "Branch page"; break; + case P_LEAF: type = "Leaf page"; break; + case P_LEAF|P_SUBP: type = "Sub-page"; break; + case P_LEAF|P_LEAF2: type = "LEAF2 page"; break; + case P_LEAF|P_LEAF2|P_SUBP: type = "LEAF2 sub-page"; break; + case P_OVERFLOW: + fprintf(stderr, "Overflow page %"Z"u pages %u%s\n", + pgno, mp->mp_pages, state); + return; + case P_META: + fprintf(stderr, "Meta-page %"Z"u txnid %"Z"u\n", + pgno, ((MDB_meta *)METADATA(mp))->mm_txnid); + return; + default: + fprintf(stderr, "Bad page %"Z"u flags 0x%X\n", pgno, mp->mp_flags); + return; + } + + nkeys = NUMKEYS(mp); + fprintf(stderr, "%s %"Z"u numkeys %d%s\n", type, pgno, nkeys, state); + + for (i=0; i<nkeys; i++) { + if (IS_LEAF2(mp)) { /* LEAF2 pages have no mp_ptrs[] or node headers */ + key.mv_size = nsize = mp->mp_pad; + key.mv_data = LEAF2KEY(mp, i, nsize); + total += nsize; + fprintf(stderr, "key %d: nsize %d, %s\n", i, nsize, DKEY(&key)); + continue; + } + node = NODEPTR(mp, i); + key.mv_size = node->mn_ksize; + key.mv_data = node->mn_data; + nsize = NODESIZE + key.mv_size; + if (IS_BRANCH(mp)) { + fprintf(stderr, "key %d: page %"Z"u, %s\n", i, NODEPGNO(node), + DKEY(&key)); + total += nsize; + } else { + if (F_ISSET(node->mn_flags, F_BIGDATA)) + nsize += sizeof(pgno_t); + else + nsize += NODEDSZ(node); + total += nsize; + nsize += sizeof(indx_t); + fprintf(stderr, "key %d: nsize %d, %s%s\n", + i, nsize, DKEY(&key), mdb_leafnode_type(node)); + } + total = EVEN(total); + } + fprintf(stderr, "Total: header %d + contents %d + unused %d\n", + IS_LEAF2(mp) ? PAGEHDRSZ : PAGEBASE + mp->mp_lower, total, SIZELEFT(mp)); +} + +void +mdb_cursor_chk(MDB_cursor *mc) +{ + unsigned int i; + MDB_node *node; + MDB_page *mp; + + if (!mc->mc_snum || !(mc->mc_flags & C_INITIALIZED)) return; + for (i=0; i<mc->mc_top; i++) { + mp = mc->mc_pg[i]; + node = NODEPTR(mp, mc->mc_ki[i]); + if (NODEPGNO(node) != mc->mc_pg[i+1]->mp_pgno) + printf("oops!\n"); + } + if (mc->mc_ki[i] >= NUMKEYS(mc->mc_pg[i])) + printf("ack!\n"); + if (XCURSOR_INITED(mc)) { + node = NODEPTR(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]); + if (((node->mn_flags & (F_DUPDATA|F_SUBDATA)) == F_DUPDATA) && + mc->mc_xcursor->mx_cursor.mc_pg[0] != NODEDATA(node)) { + printf("blah!\n"); + } + } +} +#endif + +#if (MDB_DEBUG) > 2 +/** Count all the pages in each DB and in the freelist + * and make sure it matches the actual number of pages + * being used. + * All named DBs must be open for a correct count. + */ +static void mdb_audit(MDB_txn *txn) +{ + MDB_cursor mc; + MDB_val key, data; + MDB_ID freecount, count; + MDB_dbi i; + int rc; + + freecount = 0; + mdb_cursor_init(&mc, txn, FREE_DBI, NULL); + while ((rc = mdb_cursor_get(&mc, &key, &data, MDB_NEXT)) == 0) + freecount += *(MDB_ID *)data.mv_data; + mdb_tassert(txn, rc == MDB_NOTFOUND); + + count = 0; + for (i = 0; i<txn->mt_numdbs; i++) { + MDB_xcursor mx; + if (!(txn->mt_dbflags[i] & DB_VALID)) + continue; + mdb_cursor_init(&mc, txn, i, &mx); + if (txn->mt_dbs[i].md_root == P_INVALID) + continue; + count += txn->mt_dbs[i].md_branch_pages + + txn->mt_dbs[i].md_leaf_pages + + txn->mt_dbs[i].md_overflow_pages; + if (txn->mt_dbs[i].md_flags & MDB_DUPSORT) { + rc = mdb_page_search(&mc, NULL, MDB_PS_FIRST); + for (; rc == MDB_SUCCESS; rc = mdb_cursor_sibling(&mc, 1)) { + unsigned j; + MDB_page *mp; + mp = mc.mc_pg[mc.mc_top]; + for (j=0; j<NUMKEYS(mp); j++) { + MDB_node *leaf = NODEPTR(mp, j); + if (leaf->mn_flags & F_SUBDATA) { + MDB_db db; + memcpy(&db, NODEDATA(leaf), sizeof(db)); + count += db.md_branch_pages + db.md_leaf_pages + + db.md_overflow_pages; + } + } + } + mdb_tassert(txn, rc == MDB_NOTFOUND); + } + } + if (freecount + count + NUM_METAS != txn->mt_next_pgno) { + fprintf(stderr, "audit: %"Z"u freecount: %"Z"u count: %"Z"u total: %"Z"u next_pgno: %"Z"u\n", + txn->mt_txnid, freecount, count+NUM_METAS, + freecount+count+NUM_METAS, txn->mt_next_pgno); + } +} +#endif + +int +mdb_cmp(MDB_txn *txn, MDB_dbi dbi, const MDB_val *a, const MDB_val *b) +{ + return txn->mt_dbxs[dbi].md_cmp(a, b); +} + +int +mdb_dcmp(MDB_txn *txn, MDB_dbi dbi, const MDB_val *a, const MDB_val *b) +{ + MDB_cmp_func *dcmp = txn->mt_dbxs[dbi].md_dcmp; +#if UINT_MAX < SIZE_MAX + if (dcmp == mdb_cmp_int && a->mv_size == sizeof(size_t)) + dcmp = mdb_cmp_clong; +#endif + return dcmp(a, b); +} + +/** Allocate memory for a page. + * Re-use old malloc'd pages first for singletons, otherwise just malloc. + * Set #MDB_TXN_ERROR on failure. + */ +static MDB_page * +mdb_page_malloc(MDB_txn *txn, unsigned num) +{ + MDB_env *env = txn->mt_env; + MDB_page *ret = env->me_dpages; + size_t psize = env->me_psize, sz = psize, off; + /* For ! #MDB_NOMEMINIT, psize counts how much to init. + * For a single page alloc, we init everything after the page header. + * For multi-page, we init the final page; if the caller needed that + * many pages they will be filling in at least up to the last page. + */ + if (num == 1) { + if (ret) { + VGMEMP_ALLOC(env, ret, sz); + VGMEMP_DEFINED(ret, sizeof(ret->mp_next)); + env->me_dpages = ret->mp_next; + return ret; + } + psize -= off = PAGEHDRSZ; + } else { + sz *= num; + off = sz - psize; + } + if ((ret = malloc(sz)) != NULL) { + VGMEMP_ALLOC(env, ret, sz); + if (!(env->me_flags & MDB_NOMEMINIT)) { + memset((char *)ret + off, 0, psize); + ret->mp_pad = 0; + } + } else { + txn->mt_flags |= MDB_TXN_ERROR; + } + return ret; +} +/** Free a single page. + * Saves single pages to a list, for future reuse. + * (This is not used for multi-page overflow pages.) + */ +static void +mdb_page_free(MDB_env *env, MDB_page *mp) +{ + mp->mp_next = env->me_dpages; + VGMEMP_FREE(env, mp); + env->me_dpages = mp; +} + +/** Free a dirty page */ +static void +mdb_dpage_free(MDB_env *env, MDB_page *dp) +{ + if (!IS_OVERFLOW(dp) || dp->mp_pages == 1) { + mdb_page_free(env, dp); + } else { + /* large pages just get freed directly */ + VGMEMP_FREE(env, dp); + free(dp); + } +} + +/** Return all dirty pages to dpage list */ +static void +mdb_dlist_free(MDB_txn *txn) +{ + MDB_env *env = txn->mt_env; + MDB_ID2L dl = txn->mt_u.dirty_list; + unsigned i, n = dl[0].mid; + + for (i = 1; i <= n; i++) { + mdb_dpage_free(env, dl[i].mptr); + } + dl[0].mid = 0; +} + +/** Loosen or free a single page. + * Saves single pages to a list for future reuse + * in this same txn. It has been pulled from the freeDB + * and already resides on the dirty list, but has been + * deleted. Use these pages first before pulling again + * from the freeDB. + * + * If the page wasn't dirtied in this txn, just add it + * to this txn's free list. + */ +static int +mdb_page_loose(MDB_cursor *mc, MDB_page *mp) +{ + int loose = 0; + pgno_t pgno = mp->mp_pgno; + MDB_txn *txn = mc->mc_txn; + + if ((mp->mp_flags & P_DIRTY) && mc->mc_dbi != FREE_DBI) { + if (txn->mt_parent) { + MDB_ID2 *dl = txn->mt_u.dirty_list; + /* If txn has a parent, make sure the page is in our + * dirty list. + */ + if (dl[0].mid) { + unsigned x = mdb_mid2l_search(dl, pgno); + if (x <= dl[0].mid && dl[x].mid == pgno) { + if (mp != dl[x].mptr) { /* bad cursor? */ + mc->mc_flags &= ~(C_INITIALIZED|C_EOF); + txn->mt_flags |= MDB_TXN_ERROR; + return MDB_CORRUPTED; + } + /* ok, it's ours */ + loose = 1; + } + } + } else { + /* no parent txn, so it's just ours */ + loose = 1; + } + } + if (loose) { + DPRINTF(("loosen db %d page %"Z"u", DDBI(mc), + mp->mp_pgno)); + NEXT_LOOSE_PAGE(mp) = txn->mt_loose_pgs; + txn->mt_loose_pgs = mp; + txn->mt_loose_count++; + mp->mp_flags |= P_LOOSE; + } else { + int rc = mdb_midl_append(&txn->mt_free_pgs, pgno); + if (rc) + return rc; + } + + return MDB_SUCCESS; +} + +/** Set or clear P_KEEP in dirty, non-overflow, non-sub pages watched by txn. + * @param[in] mc A cursor handle for the current operation. + * @param[in] pflags Flags of the pages to update: + * P_DIRTY to set P_KEEP, P_DIRTY|P_KEEP to clear it. + * @param[in] all No shortcuts. Needed except after a full #mdb_page_flush(). + * @return 0 on success, non-zero on failure. + */ +static int +mdb_pages_xkeep(MDB_cursor *mc, unsigned pflags, int all) +{ + enum { Mask = P_SUBP|P_DIRTY|P_LOOSE|P_KEEP }; + MDB_txn *txn = mc->mc_txn; + MDB_cursor *m3, *m0 = mc; + MDB_xcursor *mx; + MDB_page *dp, *mp; + MDB_node *leaf; + unsigned i, j; + int rc = MDB_SUCCESS, level; + + /* Mark pages seen by cursors */ + if (mc->mc_flags & C_UNTRACK) + mc = NULL; /* will find mc in mt_cursors */ + for (i = txn->mt_numdbs;; mc = txn->mt_cursors[--i]) { + for (; mc; mc=mc->mc_next) { + if (!(mc->mc_flags & C_INITIALIZED)) + continue; + for (m3 = mc;; m3 = &mx->mx_cursor) { + mp = NULL; + for (j=0; j<m3->mc_snum; j++) { + mp = m3->mc_pg[j]; + if ((mp->mp_flags & Mask) == pflags) + mp->mp_flags ^= P_KEEP; + } + mx = m3->mc_xcursor; + /* Proceed to mx if it is at a sub-database */ + if (! (mx && (mx->mx_cursor.mc_flags & C_INITIALIZED))) + break; + if (! (mp && (mp->mp_flags & P_LEAF))) + break; + leaf = NODEPTR(mp, m3->mc_ki[j-1]); + if (!(leaf->mn_flags & F_SUBDATA)) + break; + } + } + if (i == 0) + break; + } + + if (all) { + /* Mark dirty root pages */ + for (i=0; i<txn->mt_numdbs; i++) { + if (txn->mt_dbflags[i] & DB_DIRTY) { + pgno_t pgno = txn->mt_dbs[i].md_root; + if (pgno == P_INVALID) + continue; + if ((rc = mdb_page_get(m0, pgno, &dp, &level)) != MDB_SUCCESS) + break; + if ((dp->mp_flags & Mask) == pflags && level <= 1) + dp->mp_flags ^= P_KEEP; + } + } + } + + return rc; +} + +static int mdb_page_flush(MDB_txn *txn, int keep); + +/** Spill pages from the dirty list back to disk. + * This is intended to prevent running into #MDB_TXN_FULL situations, + * but note that they may still occur in a few cases: + * 1) our estimate of the txn size could be too small. Currently this + * seems unlikely, except with a large number of #MDB_MULTIPLE items. + * 2) child txns may run out of space if their parents dirtied a + * lot of pages and never spilled them. TODO: we probably should do + * a preemptive spill during #mdb_txn_begin() of a child txn, if + * the parent's dirty_room is below a given threshold. + * + * Otherwise, if not using nested txns, it is expected that apps will + * not run into #MDB_TXN_FULL any more. The pages are flushed to disk + * the same way as for a txn commit, e.g. their P_DIRTY flag is cleared. + * If the txn never references them again, they can be left alone. + * If the txn only reads them, they can be used without any fuss. + * If the txn writes them again, they can be dirtied immediately without + * going thru all of the work of #mdb_page_touch(). Such references are + * handled by #mdb_page_unspill(). + * + * Also note, we never spill DB root pages, nor pages of active cursors, + * because we'll need these back again soon anyway. And in nested txns, + * we can't spill a page in a child txn if it was already spilled in a + * parent txn. That would alter the parent txns' data even though + * the child hasn't committed yet, and we'd have no way to undo it if + * the child aborted. + * + * @param[in] m0 cursor A cursor handle identifying the transaction and + * database for which we are checking space. + * @param[in] key For a put operation, the key being stored. + * @param[in] data For a put operation, the data being stored. + * @return 0 on success, non-zero on failure. + */ +static int +mdb_page_spill(MDB_cursor *m0, MDB_val *key, MDB_val *data) +{ + MDB_txn *txn = m0->mc_txn; + MDB_page *dp; + MDB_ID2L dl = txn->mt_u.dirty_list; + unsigned int i, j, need; + int rc; + + if (m0->mc_flags & C_SUB) + return MDB_SUCCESS; + + /* Estimate how much space this op will take */ + i = m0->mc_db->md_depth; + /* Named DBs also dirty the main DB */ + if (m0->mc_dbi >= CORE_DBS) + i += txn->mt_dbs[MAIN_DBI].md_depth; + /* For puts, roughly factor in the key+data size */ + if (key) + i += (LEAFSIZE(key, data) + txn->mt_env->me_psize) / txn->mt_env->me_psize; + i += i; /* double it for good measure */ + need = i; + + if (txn->mt_dirty_room > i) + return MDB_SUCCESS; + + if (!txn->mt_spill_pgs) { + txn->mt_spill_pgs = mdb_midl_alloc(MDB_IDL_UM_MAX); + if (!txn->mt_spill_pgs) + return ENOMEM; + } else { + /* purge deleted slots */ + MDB_IDL sl = txn->mt_spill_pgs; + unsigned int num = sl[0]; + j=0; + for (i=1; i<=num; i++) { + if (!(sl[i] & 1)) + sl[++j] = sl[i]; + } + sl[0] = j; + } + + /* Preserve pages which may soon be dirtied again */ + if ((rc = mdb_pages_xkeep(m0, P_DIRTY, 1)) != MDB_SUCCESS) + goto done; + + /* Less aggressive spill - we originally spilled the entire dirty list, + * with a few exceptions for cursor pages and DB root pages. But this + * turns out to be a lot of wasted effort because in a large txn many + * of those pages will need to be used again. So now we spill only 1/8th + * of the dirty pages. Testing revealed this to be a good tradeoff, + * better than 1/2, 1/4, or 1/10. + */ + if (need < MDB_IDL_UM_MAX / 8) + need = MDB_IDL_UM_MAX / 8; + + /* Save the page IDs of all the pages we're flushing */ + /* flush from the tail forward, this saves a lot of shifting later on. */ + for (i=dl[0].mid; i && need; i--) { + MDB_ID pn = dl[i].mid << 1; + dp = dl[i].mptr; + if (dp->mp_flags & (P_LOOSE|P_KEEP)) + continue; + /* Can't spill twice, make sure it's not already in a parent's + * spill list. + */ + if (txn->mt_parent) { + MDB_txn *tx2; + for (tx2 = txn->mt_parent; tx2; tx2 = tx2->mt_parent) { + if (tx2->mt_spill_pgs) { + j = mdb_midl_search(tx2->mt_spill_pgs, pn); + if (j <= tx2->mt_spill_pgs[0] && tx2->mt_spill_pgs[j] == pn) { + dp->mp_flags |= P_KEEP; + break; + } + } + } + if (tx2) + continue; + } + if ((rc = mdb_midl_append(&txn->mt_spill_pgs, pn))) + goto done; + need--; + } + mdb_midl_sort(txn->mt_spill_pgs); + + /* Flush the spilled part of dirty list */ + if ((rc = mdb_page_flush(txn, i)) != MDB_SUCCESS) + goto done; + + /* Reset any dirty pages we kept that page_flush didn't see */ + rc = mdb_pages_xkeep(m0, P_DIRTY|P_KEEP, i); + +done: + txn->mt_flags |= rc ? MDB_TXN_ERROR : MDB_TXN_SPILLS; + return rc; +} + +/** Find oldest txnid still referenced. Expects txn->mt_txnid > 0. */ +static txnid_t +mdb_find_oldest(MDB_txn *txn) +{ + int i; + txnid_t mr, oldest = txn->mt_txnid - 1; + if (txn->mt_env->me_txns) { + MDB_reader *r = txn->mt_env->me_txns->mti_readers; + for (i = txn->mt_env->me_txns->mti_numreaders; --i >= 0; ) { + if (r[i].mr_pid) { + mr = r[i].mr_txnid; + if (oldest > mr) + oldest = mr; + } + } + } + return oldest; +} + +/** Add a page to the txn's dirty list */ +static void +mdb_page_dirty(MDB_txn *txn, MDB_page *mp) +{ + MDB_ID2 mid; + int rc, (*insert)(MDB_ID2L, MDB_ID2 *); + + if (txn->mt_flags & MDB_TXN_WRITEMAP) { + insert = mdb_mid2l_append; + } else { + insert = mdb_mid2l_insert; + } + mid.mid = mp->mp_pgno; + mid.mptr = mp; + rc = insert(txn->mt_u.dirty_list, &mid); + mdb_tassert(txn, rc == 0); + txn->mt_dirty_room--; +} + +/** Allocate page numbers and memory for writing. Maintain me_pglast, + * me_pghead and mt_next_pgno. Set #MDB_TXN_ERROR on failure. + * + * If there are free pages available from older transactions, they + * are re-used first. Otherwise allocate a new page at mt_next_pgno. + * Do not modify the freedB, just merge freeDB records into me_pghead[] + * and move me_pglast to say which records were consumed. Only this + * function can create me_pghead and move me_pglast/mt_next_pgno. + * @param[in] mc cursor A cursor handle identifying the transaction and + * database for which we are allocating. + * @param[in] num the number of pages to allocate. + * @param[out] mp Address of the allocated page(s). Requests for multiple pages + * will always be satisfied by a single contiguous chunk of memory. + * @return 0 on success, non-zero on failure. + */ +static int +mdb_page_alloc(MDB_cursor *mc, int num, MDB_page **mp) +{ +#ifdef MDB_PARANOID /* Seems like we can ignore this now */ + /* Get at most <Max_retries> more freeDB records once me_pghead + * has enough pages. If not enough, use new pages from the map. + * If <Paranoid> and mc is updating the freeDB, only get new + * records if me_pghead is empty. Then the freelist cannot play + * catch-up with itself by growing while trying to save it. + */ + enum { Paranoid = 1, Max_retries = 500 }; +#else + enum { Paranoid = 0, Max_retries = INT_MAX /*infinite*/ }; +#endif + int rc, retry = num * 60; + MDB_txn *txn = mc->mc_txn; + MDB_env *env = txn->mt_env; + pgno_t pgno, *mop = env->me_pghead; + unsigned i, j, mop_len = mop ? mop[0] : 0, n2 = num-1; + MDB_page *np; + txnid_t oldest = 0, last; + MDB_cursor_op op; + MDB_cursor m2; + int found_old = 0; + + /* If there are any loose pages, just use them */ + if (num == 1 && txn->mt_loose_pgs) { + np = txn->mt_loose_pgs; + txn->mt_loose_pgs = NEXT_LOOSE_PAGE(np); + txn->mt_loose_count--; + DPRINTF(("db %d use loose page %"Z"u", DDBI(mc), + np->mp_pgno)); + *mp = np; + return MDB_SUCCESS; + } + + *mp = NULL; + + /* If our dirty list is already full, we can't do anything */ + if (txn->mt_dirty_room == 0) { + rc = MDB_TXN_FULL; + goto fail; + } + + for (op = MDB_FIRST;; op = MDB_NEXT) { + MDB_val key, data; + MDB_node *leaf; + pgno_t *idl; + + /* Seek a big enough contiguous page range. Prefer + * pages at the tail, just truncating the list. + */ + if (mop_len > n2) { + i = mop_len; + do { + pgno = mop[i]; + if (mop[i-n2] == pgno+n2) + goto search_done; + } while (--i > n2); + if (--retry < 0) + break; + } + + if (op == MDB_FIRST) { /* 1st iteration */ + /* Prepare to fetch more and coalesce */ + last = env->me_pglast; + oldest = env->me_pgoldest; + mdb_cursor_init(&m2, txn, FREE_DBI, NULL); + if (last) { + op = MDB_SET_RANGE; + key.mv_data = &last; /* will look up last+1 */ + key.mv_size = sizeof(last); + } + if (Paranoid && mc->mc_dbi == FREE_DBI) + retry = -1; + } + if (Paranoid && retry < 0 && mop_len) + break; + + last++; + /* Do not fetch more if the record will be too recent */ + if (oldest <= last) { + if (!found_old) { + oldest = mdb_find_oldest(txn); + env->me_pgoldest = oldest; + found_old = 1; + } + if (oldest <= last) + break; + } + rc = mdb_cursor_get(&m2, &key, NULL, op); + if (rc) { + if (rc == MDB_NOTFOUND) + break; + goto fail; + } + last = *(txnid_t*)key.mv_data; + if (oldest <= last) { + if (!found_old) { + oldest = mdb_find_oldest(txn); + env->me_pgoldest = oldest; + found_old = 1; + } + if (oldest <= last) + break; + } + np = m2.mc_pg[m2.mc_top]; + leaf = NODEPTR(np, m2.mc_ki[m2.mc_top]); + if ((rc = mdb_node_read(&m2, leaf, &data)) != MDB_SUCCESS) + goto fail; + + idl = (MDB_ID *) data.mv_data; + i = idl[0]; + if (!mop) { + if (!(env->me_pghead = mop = mdb_midl_alloc(i))) { + rc = ENOMEM; + goto fail; + } + } else { + if ((rc = mdb_midl_need(&env->me_pghead, i)) != 0) + goto fail; + mop = env->me_pghead; + } + env->me_pglast = last; +#if (MDB_DEBUG) > 1 + DPRINTF(("IDL read txn %"Z"u root %"Z"u num %u", + last, txn->mt_dbs[FREE_DBI].md_root, i)); + for (j = i; j; j--) + DPRINTF(("IDL %"Z"u", idl[j])); +#endif + /* Merge in descending sorted order */ + mdb_midl_xmerge(mop, idl); + mop_len = mop[0]; + } + + /* Use new pages from the map when nothing suitable in the freeDB */ + i = 0; + pgno = txn->mt_next_pgno; + if (pgno + num >= env->me_maxpg) { + DPUTS("DB size maxed out"); + rc = MDB_MAP_FULL; + goto fail; + } + +search_done: + if (env->me_flags & MDB_WRITEMAP) { + np = (MDB_page *)(env->me_map + env->me_psize * pgno); + } else { + if (!(np = mdb_page_malloc(txn, num))) { + rc = ENOMEM; + goto fail; + } + } + if (i) { + mop[0] = mop_len -= num; + /* Move any stragglers down */ + for (j = i-num; j < mop_len; ) + mop[++j] = mop[++i]; + } else { + txn->mt_next_pgno = pgno + num; + } + np->mp_pgno = pgno; + mdb_page_dirty(txn, np); + *mp = np; + + return MDB_SUCCESS; + +fail: + txn->mt_flags |= MDB_TXN_ERROR; + return rc; +} + +/** Copy the used portions of a non-overflow page. + * @param[in] dst page to copy into + * @param[in] src page to copy from + * @param[in] psize size of a page + */ +static void +mdb_page_copy(MDB_page *dst, MDB_page *src, unsigned int psize) +{ + enum { Align = sizeof(pgno_t) }; + indx_t upper = src->mp_upper, lower = src->mp_lower, unused = upper-lower; + + /* If page isn't full, just copy the used portion. Adjust + * alignment so memcpy may copy words instead of bytes. + */ + if ((unused &= -Align) && !IS_LEAF2(src)) { + upper = (upper + PAGEBASE) & -Align; + memcpy(dst, src, (lower + PAGEBASE + (Align-1)) & -Align); + memcpy((pgno_t *)((char *)dst+upper), (pgno_t *)((char *)src+upper), + psize - upper); + } else { + memcpy(dst, src, psize - unused); + } +} + +/** Pull a page off the txn's spill list, if present. + * If a page being referenced was spilled to disk in this txn, bring + * it back and make it dirty/writable again. + * @param[in] txn the transaction handle. + * @param[in] mp the page being referenced. It must not be dirty. + * @param[out] ret the writable page, if any. ret is unchanged if + * mp wasn't spilled. + */ +static int +mdb_page_unspill(MDB_txn *txn, MDB_page *mp, MDB_page **ret) +{ + MDB_env *env = txn->mt_env; + const MDB_txn *tx2; + unsigned x; + pgno_t pgno = mp->mp_pgno, pn = pgno << 1; + + for (tx2 = txn; tx2; tx2=tx2->mt_parent) { + if (!tx2->mt_spill_pgs) + continue; + x = mdb_midl_search(tx2->mt_spill_pgs, pn); + if (x <= tx2->mt_spill_pgs[0] && tx2->mt_spill_pgs[x] == pn) { + MDB_page *np; + int num; + if (txn->mt_dirty_room == 0) + return MDB_TXN_FULL; + if (IS_OVERFLOW(mp)) + num = mp->mp_pages; + else + num = 1; + if (env->me_flags & MDB_WRITEMAP) { + np = mp; + } else { + np = mdb_page_malloc(txn, num); + if (!np) + return ENOMEM; + if (num > 1) + memcpy(np, mp, num * env->me_psize); + else + mdb_page_copy(np, mp, env->me_psize); + } + if (tx2 == txn) { + /* If in current txn, this page is no longer spilled. + * If it happens to be the last page, truncate the spill list. + * Otherwise mark it as deleted by setting the LSB. + */ + if (x == txn->mt_spill_pgs[0]) + txn->mt_spill_pgs[0]--; + else + txn->mt_spill_pgs[x] |= 1; + } /* otherwise, if belonging to a parent txn, the + * page remains spilled until child commits + */ + + mdb_page_dirty(txn, np); + np->mp_flags |= P_DIRTY; + *ret = np; + break; + } + } + return MDB_SUCCESS; +} + +/** Touch a page: make it dirty and re-insert into tree with updated pgno. + * Set #MDB_TXN_ERROR on failure. + * @param[in] mc cursor pointing to the page to be touched + * @return 0 on success, non-zero on failure. + */ +static int +mdb_page_touch(MDB_cursor *mc) +{ + MDB_page *mp = mc->mc_pg[mc->mc_top], *np; + MDB_txn *txn = mc->mc_txn; + MDB_cursor *m2, *m3; + pgno_t pgno; + int rc; + + if (!F_ISSET(mp->mp_flags, P_DIRTY)) { + if (txn->mt_flags & MDB_TXN_SPILLS) { + np = NULL; + rc = mdb_page_unspill(txn, mp, &np); + if (rc) + goto fail; + if (np) + goto done; + } + if ((rc = mdb_midl_need(&txn->mt_free_pgs, 1)) || + (rc = mdb_page_alloc(mc, 1, &np))) + goto fail; + pgno = np->mp_pgno; + DPRINTF(("touched db %d page %"Z"u -> %"Z"u", DDBI(mc), + mp->mp_pgno, pgno)); + mdb_cassert(mc, mp->mp_pgno != pgno); + mdb_midl_xappend(txn->mt_free_pgs, mp->mp_pgno); + /* Update the parent page, if any, to point to the new page */ + if (mc->mc_top) { + MDB_page *parent = mc->mc_pg[mc->mc_top-1]; + MDB_node *node = NODEPTR(parent, mc->mc_ki[mc->mc_top-1]); + SETPGNO(node, pgno); + } else { + mc->mc_db->md_root = pgno; + } + } else if (txn->mt_parent && !IS_SUBP(mp)) { + MDB_ID2 mid, *dl = txn->mt_u.dirty_list; + pgno = mp->mp_pgno; + /* If txn has a parent, make sure the page is in our + * dirty list. + */ + if (dl[0].mid) { + unsigned x = mdb_mid2l_search(dl, pgno); + if (x <= dl[0].mid && dl[x].mid == pgno) { + if (mp != dl[x].mptr) { /* bad cursor? */ + mc->mc_flags &= ~(C_INITIALIZED|C_EOF); + txn->mt_flags |= MDB_TXN_ERROR; + return MDB_CORRUPTED; + } + return 0; + } + } + mdb_cassert(mc, dl[0].mid < MDB_IDL_UM_MAX); + /* No - copy it */ + np = mdb_page_malloc(txn, 1); + if (!np) + return ENOMEM; + mid.mid = pgno; + mid.mptr = np; + rc = mdb_mid2l_insert(dl, &mid); + mdb_cassert(mc, rc == 0); + } else { + return 0; + } + + mdb_page_copy(np, mp, txn->mt_env->me_psize); + np->mp_pgno = pgno; + np->mp_flags |= P_DIRTY; + +done: + /* Adjust cursors pointing to mp */ + mc->mc_pg[mc->mc_top] = np; + m2 = txn->mt_cursors[mc->mc_dbi]; + if (mc->mc_flags & C_SUB) { + for (; m2; m2=m2->mc_next) { + m3 = &m2->mc_xcursor->mx_cursor; + if (m3->mc_snum < mc->mc_snum) continue; + if (m3->mc_pg[mc->mc_top] == mp) + m3->mc_pg[mc->mc_top] = np; + } + } else { + for (; m2; m2=m2->mc_next) { + if (m2->mc_snum < mc->mc_snum) continue; + if (m2 == mc) continue; + if (m2->mc_pg[mc->mc_top] == mp) { + m2->mc_pg[mc->mc_top] = np; + if (IS_LEAF(np)) + XCURSOR_REFRESH(m2, mc->mc_top, np); + } + } + } + return 0; + +fail: + txn->mt_flags |= MDB_TXN_ERROR; + return rc; +} + +int +mdb_env_sync(MDB_env *env, int force) +{ + int rc = 0; + if (env->me_flags & MDB_RDONLY) + return EACCES; + if (force || !F_ISSET(env->me_flags, MDB_NOSYNC)) { + if (env->me_flags & MDB_WRITEMAP) { + int flags = ((env->me_flags & MDB_MAPASYNC) && !force) + ? MS_ASYNC : MS_SYNC; + if (MDB_MSYNC(env->me_map, env->me_mapsize, flags)) + rc = ErrCode(); +#ifdef _WIN32 + else if (flags == MS_SYNC && MDB_FDATASYNC(env->me_fd)) + rc = ErrCode(); +#endif + } else { +#ifdef BROKEN_FDATASYNC + if (env->me_flags & MDB_FSYNCONLY) { + if (fsync(env->me_fd)) + rc = ErrCode(); + } else +#endif + if (MDB_FDATASYNC(env->me_fd)) + rc = ErrCode(); + } + } + return rc; +} + +/** Back up parent txn's cursors, then grab the originals for tracking */ +static int +mdb_cursor_shadow(MDB_txn *src, MDB_txn *dst) +{ + MDB_cursor *mc, *bk; + MDB_xcursor *mx; + size_t size; + int i; + + for (i = src->mt_numdbs; --i >= 0; ) { + if ((mc = src->mt_cursors[i]) != NULL) { + size = sizeof(MDB_cursor); + if (mc->mc_xcursor) + size += sizeof(MDB_xcursor); + for (; mc; mc = bk->mc_next) { + bk = malloc(size); + if (!bk) + return ENOMEM; + *bk = *mc; + mc->mc_backup = bk; + mc->mc_db = &dst->mt_dbs[i]; + /* Kill pointers into src to reduce abuse: The + * user may not use mc until dst ends. But we need a valid + * txn pointer here for cursor fixups to keep working. + */ + mc->mc_txn = dst; + mc->mc_dbflag = &dst->mt_dbflags[i]; + if ((mx = mc->mc_xcursor) != NULL) { + *(MDB_xcursor *)(bk+1) = *mx; + mx->mx_cursor.mc_txn = dst; + } + mc->mc_next = dst->mt_cursors[i]; + dst->mt_cursors[i] = mc; + } + } + } + return MDB_SUCCESS; +} + +/** Close this write txn's cursors, give parent txn's cursors back to parent. + * @param[in] txn the transaction handle. + * @param[in] merge true to keep changes to parent cursors, false to revert. + * @return 0 on success, non-zero on failure. + */ +static void +mdb_cursors_close(MDB_txn *txn, unsigned merge) +{ + MDB_cursor **cursors = txn->mt_cursors, *mc, *next, *bk; + MDB_xcursor *mx; + int i; + + for (i = txn->mt_numdbs; --i >= 0; ) { + for (mc = cursors[i]; mc; mc = next) { + next = mc->mc_next; + if ((bk = mc->mc_backup) != NULL) { + if (merge) { + /* Commit changes to parent txn */ + mc->mc_next = bk->mc_next; + mc->mc_backup = bk->mc_backup; + mc->mc_txn = bk->mc_txn; + mc->mc_db = bk->mc_db; + mc->mc_dbflag = bk->mc_dbflag; + if ((mx = mc->mc_xcursor) != NULL) + mx->mx_cursor.mc_txn = bk->mc_txn; + } else { + /* Abort nested txn */ + *mc = *bk; + if ((mx = mc->mc_xcursor) != NULL) + *mx = *(MDB_xcursor *)(bk+1); + } + mc = bk; + } + /* Only malloced cursors are permanently tracked. */ + free(mc); + } + cursors[i] = NULL; + } +} + +#if !(MDB_PIDLOCK) /* Currently the same as defined(_WIN32) */ +enum Pidlock_op { + Pidset, Pidcheck +}; +#else +enum Pidlock_op { + Pidset = F_SETLK, Pidcheck = F_GETLK +}; +#endif + +/** Set or check a pid lock. Set returns 0 on success. + * Check returns 0 if the process is certainly dead, nonzero if it may + * be alive (the lock exists or an error happened so we do not know). + * + * On Windows Pidset is a no-op, we merely check for the existence + * of the process with the given pid. On POSIX we use a single byte + * lock on the lockfile, set at an offset equal to the pid. + */ +static int +mdb_reader_pid(MDB_env *env, enum Pidlock_op op, MDB_PID_T pid) +{ +#if !(MDB_PIDLOCK) /* Currently the same as defined(_WIN32) */ + int ret = 0; + HANDLE h; + if (op == Pidcheck) { + h = OpenProcess(env->me_pidquery, FALSE, pid); + /* No documented "no such process" code, but other program use this: */ + if (!h) + return ErrCode() != ERROR_INVALID_PARAMETER; + /* A process exists until all handles to it close. Has it exited? */ + ret = WaitForSingleObject(h, 0) != 0; + CloseHandle(h); + } + return ret; +#else + for (;;) { + int rc; + struct flock lock_info; + memset(&lock_info, 0, sizeof(lock_info)); + lock_info.l_type = F_WRLCK; + lock_info.l_whence = SEEK_SET; + lock_info.l_start = pid; + lock_info.l_len = 1; + if ((rc = fcntl(env->me_lfd, op, &lock_info)) == 0) { + if (op == F_GETLK && lock_info.l_type != F_UNLCK) + rc = -1; + } else if ((rc = ErrCode()) == EINTR) { + continue; + } + return rc; + } +#endif +} + +/** Common code for #mdb_txn_begin() and #mdb_txn_renew(). + * @param[in] txn the transaction handle to initialize + * @return 0 on success, non-zero on failure. + */ +static int +mdb_txn_renew0(MDB_txn *txn) +{ + MDB_env *env = txn->mt_env; + MDB_txninfo *ti = env->me_txns; + MDB_meta *meta; + unsigned int i, nr, flags = txn->mt_flags; + uint16_t x; + int rc, new_notls = 0; + + if ((flags &= MDB_TXN_RDONLY) != 0) { + if (!ti) { + meta = mdb_env_pick_meta(env); + txn->mt_txnid = meta->mm_txnid; + txn->mt_u.reader = NULL; + } else { + MDB_reader *r = (env->me_flags & MDB_NOTLS) ? txn->mt_u.reader : + pthread_getspecific(env->me_txkey); + if (r) { + if (r->mr_pid != env->me_pid || r->mr_txnid != (txnid_t)-1) + return MDB_BAD_RSLOT; + } else { + MDB_PID_T pid = env->me_pid; + MDB_THR_T tid = pthread_self(); + mdb_mutexref_t rmutex = env->me_rmutex; + + if (!env->me_live_reader) { + rc = mdb_reader_pid(env, Pidset, pid); + if (rc) + return rc; + env->me_live_reader = 1; + } + + if (LOCK_MUTEX(rc, env, rmutex)) + return rc; + nr = ti->mti_numreaders; + for (i=0; i<nr; i++) + if (ti->mti_readers[i].mr_pid == 0) + break; + if (i == env->me_maxreaders) { + UNLOCK_MUTEX(rmutex); + return MDB_READERS_FULL; + } + r = &ti->mti_readers[i]; + /* Claim the reader slot, carefully since other code + * uses the reader table un-mutexed: First reset the + * slot, next publish it in mti_numreaders. After + * that, it is safe for mdb_env_close() to touch it. + * When it will be closed, we can finally claim it. + */ + r->mr_pid = 0; + r->mr_txnid = (txnid_t)-1; + r->mr_tid = tid; + if (i == nr) + ti->mti_numreaders = ++nr; + env->me_close_readers = nr; + r->mr_pid = pid; + UNLOCK_MUTEX(rmutex); + + new_notls = (env->me_flags & MDB_NOTLS); + if (!new_notls && (rc=pthread_setspecific(env->me_txkey, r))) { + r->mr_pid = 0; + return rc; + } + } + do /* LY: Retry on a race, ITS#7970. */ + r->mr_txnid = ti->mti_txnid; + while(r->mr_txnid != ti->mti_txnid); + txn->mt_txnid = r->mr_txnid; + txn->mt_u.reader = r; + meta = env->me_metas[txn->mt_txnid & 1]; + } + + } else { + /* Not yet touching txn == env->me_txn0, it may be active */ + if (ti) { + if (LOCK_MUTEX(rc, env, env->me_wmutex)) + return rc; + txn->mt_txnid = ti->mti_txnid; + meta = env->me_metas[txn->mt_txnid & 1]; + } else { + meta = mdb_env_pick_meta(env); + txn->mt_txnid = meta->mm_txnid; + } + txn->mt_txnid++; +#if MDB_DEBUG + if (txn->mt_txnid == mdb_debug_start) + mdb_debug = 1; +#endif + txn->mt_child = NULL; + txn->mt_loose_pgs = NULL; + txn->mt_loose_count = 0; + txn->mt_dirty_room = MDB_IDL_UM_MAX; + txn->mt_u.dirty_list = env->me_dirty_list; + txn->mt_u.dirty_list[0].mid = 0; + txn->mt_free_pgs = env->me_free_pgs; + txn->mt_free_pgs[0] = 0; + txn->mt_spill_pgs = NULL; + env->me_txn = txn; + memcpy(txn->mt_dbiseqs, env->me_dbiseqs, env->me_maxdbs * sizeof(unsigned int)); + } + + /* Copy the DB info and flags */ + memcpy(txn->mt_dbs, meta->mm_dbs, CORE_DBS * sizeof(MDB_db)); + + /* Moved to here to avoid a data race in read TXNs */ + txn->mt_next_pgno = meta->mm_last_pg+1; + + txn->mt_flags = flags; + + /* Setup db info */ + txn->mt_numdbs = env->me_numdbs; + for (i=CORE_DBS; i<txn->mt_numdbs; i++) { + x = env->me_dbflags[i]; + txn->mt_dbs[i].md_flags = x & PERSISTENT_FLAGS; + txn->mt_dbflags[i] = (x & MDB_VALID) ? DB_VALID|DB_USRVALID|DB_STALE : 0; + } + txn->mt_dbflags[MAIN_DBI] = DB_VALID|DB_USRVALID; + txn->mt_dbflags[FREE_DBI] = DB_VALID; + + if (env->me_flags & MDB_FATAL_ERROR) { + DPUTS("environment had fatal error, must shutdown!"); + rc = MDB_PANIC; + } else if (env->me_maxpg < txn->mt_next_pgno) { + rc = MDB_MAP_RESIZED; + } else { + return MDB_SUCCESS; + } + mdb_txn_end(txn, new_notls /*0 or MDB_END_SLOT*/ | MDB_END_FAIL_BEGIN); + return rc; +} + +int +mdb_txn_renew(MDB_txn *txn) +{ + int rc; + + if (!txn || !F_ISSET(txn->mt_flags, MDB_TXN_RDONLY|MDB_TXN_FINISHED)) + return EINVAL; + + rc = mdb_txn_renew0(txn); + if (rc == MDB_SUCCESS) { + DPRINTF(("renew txn %"Z"u%c %p on mdbenv %p, root page %"Z"u", + txn->mt_txnid, (txn->mt_flags & MDB_TXN_RDONLY) ? 'r' : 'w', + (void *)txn, (void *)txn->mt_env, txn->mt_dbs[MAIN_DBI].md_root)); + } + return rc; +} + +int +mdb_txn_begin(MDB_env *env, MDB_txn *parent, unsigned int flags, MDB_txn **ret) +{ + MDB_txn *txn; + MDB_ntxn *ntxn; + int rc, size, tsize; + + flags &= MDB_TXN_BEGIN_FLAGS; + flags |= env->me_flags & MDB_WRITEMAP; + + if (env->me_flags & MDB_RDONLY & ~flags) /* write txn in RDONLY env */ + return EACCES; + + if (parent) { + /* Nested transactions: Max 1 child, write txns only, no writemap */ + flags |= parent->mt_flags; + if (flags & (MDB_RDONLY|MDB_WRITEMAP|MDB_TXN_BLOCKED)) { + return (parent->mt_flags & MDB_TXN_RDONLY) ? EINVAL : MDB_BAD_TXN; + } + /* Child txns save MDB_pgstate and use own copy of cursors */ + size = env->me_maxdbs * (sizeof(MDB_db)+sizeof(MDB_cursor *)+1); + size += tsize = sizeof(MDB_ntxn); + } else if (flags & MDB_RDONLY) { + size = env->me_maxdbs * (sizeof(MDB_db)+1); + size += tsize = sizeof(MDB_txn); + } else { + /* Reuse preallocated write txn. However, do not touch it until + * mdb_txn_renew0() succeeds, since it currently may be active. + */ + txn = env->me_txn0; + goto renew; + } + if ((txn = calloc(1, size)) == NULL) { + DPRINTF(("calloc: %s", strerror(errno))); + return ENOMEM; + } + txn->mt_dbxs = env->me_dbxs; /* static */ + txn->mt_dbs = (MDB_db *) ((char *)txn + tsize); + txn->mt_dbflags = (unsigned char *)txn + size - env->me_maxdbs; + txn->mt_flags = flags; + txn->mt_env = env; + + if (parent) { + unsigned int i; + txn->mt_cursors = (MDB_cursor **)(txn->mt_dbs + env->me_maxdbs); + txn->mt_dbiseqs = parent->mt_dbiseqs; + txn->mt_u.dirty_list = malloc(sizeof(MDB_ID2)*MDB_IDL_UM_SIZE); + if (!txn->mt_u.dirty_list || + !(txn->mt_free_pgs = mdb_midl_alloc(MDB_IDL_UM_MAX))) + { + free(txn->mt_u.dirty_list); + free(txn); + return ENOMEM; + } + txn->mt_txnid = parent->mt_txnid; + txn->mt_dirty_room = parent->mt_dirty_room; + txn->mt_u.dirty_list[0].mid = 0; + txn->mt_spill_pgs = NULL; + txn->mt_next_pgno = parent->mt_next_pgno; + parent->mt_flags |= MDB_TXN_HAS_CHILD; + parent->mt_child = txn; + txn->mt_parent = parent; + txn->mt_numdbs = parent->mt_numdbs; + memcpy(txn->mt_dbs, parent->mt_dbs, txn->mt_numdbs * sizeof(MDB_db)); + /* Copy parent's mt_dbflags, but clear DB_NEW */ + for (i=0; i<txn->mt_numdbs; i++) + txn->mt_dbflags[i] = parent->mt_dbflags[i] & ~DB_NEW; + rc = 0; + ntxn = (MDB_ntxn *)txn; + ntxn->mnt_pgstate = env->me_pgstate; /* save parent me_pghead & co */ + if (env->me_pghead) { + size = MDB_IDL_SIZEOF(env->me_pghead); + env->me_pghead = mdb_midl_alloc(env->me_pghead[0]); + if (env->me_pghead) + memcpy(env->me_pghead, ntxn->mnt_pgstate.mf_pghead, size); + else + rc = ENOMEM; + } + if (!rc) + rc = mdb_cursor_shadow(parent, txn); + if (rc) + mdb_txn_end(txn, MDB_END_FAIL_BEGINCHILD); + } else { /* MDB_RDONLY */ + txn->mt_dbiseqs = env->me_dbiseqs; +renew: + rc = mdb_txn_renew0(txn); + } + if (rc) { + if (txn != env->me_txn0) + free(txn); + } else { + txn->mt_flags |= flags; /* could not change txn=me_txn0 earlier */ + *ret = txn; + DPRINTF(("begin txn %"Z"u%c %p on mdbenv %p, root page %"Z"u", + txn->mt_txnid, (flags & MDB_RDONLY) ? 'r' : 'w', + (void *) txn, (void *) env, txn->mt_dbs[MAIN_DBI].md_root)); + } + + return rc; +} + +MDB_env * +mdb_txn_env(MDB_txn *txn) +{ + if(!txn) return NULL; + return txn->mt_env; +} + +size_t +mdb_txn_id(MDB_txn *txn) +{ + if(!txn) return 0; + return txn->mt_txnid; +} + +/** Export or close DBI handles opened in this txn. */ +static void +mdb_dbis_update(MDB_txn *txn, int keep) +{ + int i; + MDB_dbi n = txn->mt_numdbs; + MDB_env *env = txn->mt_env; + unsigned char *tdbflags = txn->mt_dbflags; + + for (i = n; --i >= CORE_DBS;) { + if (tdbflags[i] & DB_NEW) { + if (keep) { + env->me_dbflags[i] = txn->mt_dbs[i].md_flags | MDB_VALID; + } else { + char *ptr = env->me_dbxs[i].md_name.mv_data; + if (ptr) { + env->me_dbxs[i].md_name.mv_data = NULL; + env->me_dbxs[i].md_name.mv_size = 0; + env->me_dbflags[i] = 0; + env->me_dbiseqs[i]++; + free(ptr); + } + } + } + } + if (keep && env->me_numdbs < n) + env->me_numdbs = n; +} + +/** End a transaction, except successful commit of a nested transaction. + * May be called twice for readonly txns: First reset it, then abort. + * @param[in] txn the transaction handle to end + * @param[in] mode why and how to end the transaction + */ +static void +mdb_txn_end(MDB_txn *txn, unsigned mode) +{ + MDB_env *env = txn->mt_env; +#if MDB_DEBUG + static const char *const names[] = MDB_END_NAMES; +#endif + + /* Export or close DBI handles opened in this txn */ + mdb_dbis_update(txn, mode & MDB_END_UPDATE); + + DPRINTF(("%s txn %"Z"u%c %p on mdbenv %p, root page %"Z"u", + names[mode & MDB_END_OPMASK], + txn->mt_txnid, (txn->mt_flags & MDB_TXN_RDONLY) ? 'r' : 'w', + (void *) txn, (void *)env, txn->mt_dbs[MAIN_DBI].md_root)); + + if (F_ISSET(txn->mt_flags, MDB_TXN_RDONLY)) { + if (txn->mt_u.reader) { + txn->mt_u.reader->mr_txnid = (txnid_t)-1; + if (!(env->me_flags & MDB_NOTLS)) { + txn->mt_u.reader = NULL; /* txn does not own reader */ + } else if (mode & MDB_END_SLOT) { + txn->mt_u.reader->mr_pid = 0; + txn->mt_u.reader = NULL; + } /* else txn owns the slot until it does MDB_END_SLOT */ + } + txn->mt_numdbs = 0; /* prevent further DBI activity */ + txn->mt_flags |= MDB_TXN_FINISHED; + + } else if (!F_ISSET(txn->mt_flags, MDB_TXN_FINISHED)) { + pgno_t *pghead = env->me_pghead; + + if (!(mode & MDB_END_UPDATE)) /* !(already closed cursors) */ + mdb_cursors_close(txn, 0); + if (!(env->me_flags & MDB_WRITEMAP)) { + mdb_dlist_free(txn); + } + + txn->mt_numdbs = 0; + txn->mt_flags = MDB_TXN_FINISHED; + + if (!txn->mt_parent) { + mdb_midl_shrink(&txn->mt_free_pgs); + env->me_free_pgs = txn->mt_free_pgs; + /* me_pgstate: */ + env->me_pghead = NULL; + env->me_pglast = 0; + + env->me_txn = NULL; + mode = 0; /* txn == env->me_txn0, do not free() it */ + + /* The writer mutex was locked in mdb_txn_begin. */ + if (env->me_txns) + UNLOCK_MUTEX(env->me_wmutex); + } else { + txn->mt_parent->mt_child = NULL; + txn->mt_parent->mt_flags &= ~MDB_TXN_HAS_CHILD; + env->me_pgstate = ((MDB_ntxn *)txn)->mnt_pgstate; + mdb_midl_free(txn->mt_free_pgs); + mdb_midl_free(txn->mt_spill_pgs); + free(txn->mt_u.dirty_list); + } + + mdb_midl_free(pghead); + } + + if (mode & MDB_END_FREE) + free(txn); +} + +void +mdb_txn_reset(MDB_txn *txn) +{ + if (txn == NULL) + return; + + /* This call is only valid for read-only txns */ + if (!(txn->mt_flags & MDB_TXN_RDONLY)) + return; + + mdb_txn_end(txn, MDB_END_RESET); +} + +void +mdb_txn_abort(MDB_txn *txn) +{ + if (txn == NULL) + return; + + if (txn->mt_child) + mdb_txn_abort(txn->mt_child); + + mdb_txn_end(txn, MDB_END_ABORT|MDB_END_SLOT|MDB_END_FREE); +} + +/** Save the freelist as of this transaction to the freeDB. + * This changes the freelist. Keep trying until it stabilizes. + */ +static int +mdb_freelist_save(MDB_txn *txn) +{ + /* env->me_pghead[] can grow and shrink during this call. + * env->me_pglast and txn->mt_free_pgs[] can only grow. + * Page numbers cannot disappear from txn->mt_free_pgs[]. + */ + MDB_cursor mc; + MDB_env *env = txn->mt_env; + int rc, maxfree_1pg = env->me_maxfree_1pg, more = 1; + txnid_t pglast = 0, head_id = 0; + pgno_t freecnt = 0, *free_pgs, *mop; + ssize_t head_room = 0, total_room = 0, mop_len, clean_limit; + + mdb_cursor_init(&mc, txn, FREE_DBI, NULL); + + if (env->me_pghead) { + /* Make sure first page of freeDB is touched and on freelist */ + rc = mdb_page_search(&mc, NULL, MDB_PS_FIRST|MDB_PS_MODIFY); + if (rc && rc != MDB_NOTFOUND) + return rc; + } + + if (!env->me_pghead && txn->mt_loose_pgs) { + /* Put loose page numbers in mt_free_pgs, since + * we may be unable to return them to me_pghead. + */ + MDB_page *mp = txn->mt_loose_pgs; + if ((rc = mdb_midl_need(&txn->mt_free_pgs, txn->mt_loose_count)) != 0) + return rc; + for (; mp; mp = NEXT_LOOSE_PAGE(mp)) + mdb_midl_xappend(txn->mt_free_pgs, mp->mp_pgno); + txn->mt_loose_pgs = NULL; + txn->mt_loose_count = 0; + } + + /* MDB_RESERVE cancels meminit in ovpage malloc (when no WRITEMAP) */ + clean_limit = (env->me_flags & (MDB_NOMEMINIT|MDB_WRITEMAP)) + ? SSIZE_MAX : maxfree_1pg; + + for (;;) { + /* Come back here after each Put() in case freelist changed */ + MDB_val key, data; + pgno_t *pgs; + ssize_t j; + + /* If using records from freeDB which we have not yet + * deleted, delete them and any we reserved for me_pghead. + */ + while (pglast < env->me_pglast) { + rc = mdb_cursor_first(&mc, &key, NULL); + if (rc) + return rc; + pglast = head_id = *(txnid_t *)key.mv_data; + total_room = head_room = 0; + mdb_tassert(txn, pglast <= env->me_pglast); + rc = mdb_cursor_del(&mc, 0); + if (rc) + return rc; + } + + /* Save the IDL of pages freed by this txn, to a single record */ + if (freecnt < txn->mt_free_pgs[0]) { + if (!freecnt) { + /* Make sure last page of freeDB is touched and on freelist */ + rc = mdb_page_search(&mc, NULL, MDB_PS_LAST|MDB_PS_MODIFY); + if (rc && rc != MDB_NOTFOUND) + return rc; + } + free_pgs = txn->mt_free_pgs; + /* Write to last page of freeDB */ + key.mv_size = sizeof(txn->mt_txnid); + key.mv_data = &txn->mt_txnid; + do { + freecnt = free_pgs[0]; + data.mv_size = MDB_IDL_SIZEOF(free_pgs); + rc = mdb_cursor_put(&mc, &key, &data, MDB_RESERVE); + if (rc) + return rc; + /* Retry if mt_free_pgs[] grew during the Put() */ + free_pgs = txn->mt_free_pgs; + } while (freecnt < free_pgs[0]); + mdb_midl_sort(free_pgs); + memcpy(data.mv_data, free_pgs, data.mv_size); +#if (MDB_DEBUG) > 1 + { + unsigned int i = free_pgs[0]; + DPRINTF(("IDL write txn %"Z"u root %"Z"u num %u", + txn->mt_txnid, txn->mt_dbs[FREE_DBI].md_root, i)); + for (; i; i--) + DPRINTF(("IDL %"Z"u", free_pgs[i])); + } +#endif + continue; + } + + mop = env->me_pghead; + mop_len = (mop ? mop[0] : 0) + txn->mt_loose_count; + + /* Reserve records for me_pghead[]. Split it if multi-page, + * to avoid searching freeDB for a page range. Use keys in + * range [1,me_pglast]: Smaller than txnid of oldest reader. + */ + if (total_room >= mop_len) { + if (total_room == mop_len || --more < 0) + break; + } else if (head_room >= maxfree_1pg && head_id > 1) { + /* Keep current record (overflow page), add a new one */ + head_id--; + head_room = 0; + } + /* (Re)write {key = head_id, IDL length = head_room} */ + total_room -= head_room; + head_room = mop_len - total_room; + if (head_room > maxfree_1pg && head_id > 1) { + /* Overflow multi-page for part of me_pghead */ + head_room /= head_id; /* amortize page sizes */ + head_room += maxfree_1pg - head_room % (maxfree_1pg + 1); + } else if (head_room < 0) { + /* Rare case, not bothering to delete this record */ + head_room = 0; + } + key.mv_size = sizeof(head_id); + key.mv_data = &head_id; + data.mv_size = (head_room + 1) * sizeof(pgno_t); + rc = mdb_cursor_put(&mc, &key, &data, MDB_RESERVE); + if (rc) + return rc; + /* IDL is initially empty, zero out at least the length */ + pgs = (pgno_t *)data.mv_data; + j = head_room > clean_limit ? head_room : 0; + do { + pgs[j] = 0; + } while (--j >= 0); + total_room += head_room; + } + + /* Return loose page numbers to me_pghead, though usually none are + * left at this point. The pages themselves remain in dirty_list. + */ + if (txn->mt_loose_pgs) { + MDB_page *mp = txn->mt_loose_pgs; + unsigned count = txn->mt_loose_count; + MDB_IDL loose; + /* Room for loose pages + temp IDL with same */ + if ((rc = mdb_midl_need(&env->me_pghead, 2*count+1)) != 0) + return rc; + mop = env->me_pghead; + loose = mop + MDB_IDL_ALLOCLEN(mop) - count; + for (count = 0; mp; mp = NEXT_LOOSE_PAGE(mp)) + loose[ ++count ] = mp->mp_pgno; + loose[0] = count; + mdb_midl_sort(loose); + mdb_midl_xmerge(mop, loose); + txn->mt_loose_pgs = NULL; + txn->mt_loose_count = 0; + mop_len = mop[0]; + } + + /* Fill in the reserved me_pghead records */ + rc = MDB_SUCCESS; + if (mop_len) { + MDB_val key, data; + + mop += mop_len; + rc = mdb_cursor_first(&mc, &key, &data); + for (; !rc; rc = mdb_cursor_next(&mc, &key, &data, MDB_NEXT)) { + txnid_t id = *(txnid_t *)key.mv_data; + ssize_t len = (ssize_t)(data.mv_size / sizeof(MDB_ID)) - 1; + MDB_ID save; + + mdb_tassert(txn, len >= 0 && id <= env->me_pglast); + key.mv_data = &id; + if (len > mop_len) { + len = mop_len; + data.mv_size = (len + 1) * sizeof(MDB_ID); + } + data.mv_data = mop -= len; + save = mop[0]; + mop[0] = len; + rc = mdb_cursor_put(&mc, &key, &data, MDB_CURRENT); + mop[0] = save; + if (rc || !(mop_len -= len)) + break; + } + } + return rc; +} + +/** Flush (some) dirty pages to the map, after clearing their dirty flag. + * @param[in] txn the transaction that's being committed + * @param[in] keep number of initial pages in dirty_list to keep dirty. + * @return 0 on success, non-zero on failure. + */ +static int +mdb_page_flush(MDB_txn *txn, int keep) +{ + MDB_env *env = txn->mt_env; + MDB_ID2L dl = txn->mt_u.dirty_list; + unsigned psize = env->me_psize, j; + int i, pagecount = dl[0].mid, rc; + size_t size = 0, pos = 0; + pgno_t pgno = 0; + MDB_page *dp = NULL; +#ifdef _WIN32 + OVERLAPPED ov; +#else + struct iovec iov[MDB_COMMIT_PAGES]; + ssize_t wpos = 0, wsize = 0, wres; + size_t next_pos = 1; /* impossible pos, so pos != next_pos */ + int n = 0; +#endif + + j = i = keep; + + if (env->me_flags & MDB_WRITEMAP) { + /* Clear dirty flags */ + while (++i <= pagecount) { + dp = dl[i].mptr; + /* Don't flush this page yet */ + if (dp->mp_flags & (P_LOOSE|P_KEEP)) { + dp->mp_flags &= ~P_KEEP; + dl[++j] = dl[i]; + continue; + } + dp->mp_flags &= ~P_DIRTY; + } + goto done; + } + + /* Write the pages */ + for (;;) { + if (++i <= pagecount) { + dp = dl[i].mptr; + /* Don't flush this page yet */ + if (dp->mp_flags & (P_LOOSE|P_KEEP)) { + dp->mp_flags &= ~P_KEEP; + dl[i].mid = 0; + continue; + } + pgno = dl[i].mid; + /* clear dirty flag */ + dp->mp_flags &= ~P_DIRTY; + pos = pgno * psize; + size = psize; + if (IS_OVERFLOW(dp)) size *= dp->mp_pages; + } +#ifdef _WIN32 + else break; + + /* Windows actually supports scatter/gather I/O, but only on + * unbuffered file handles. Since we're relying on the OS page + * cache for all our data, that's self-defeating. So we just + * write pages one at a time. We use the ov structure to set + * the write offset, to at least save the overhead of a Seek + * system call. + */ + DPRINTF(("committing page %"Z"u", pgno)); + memset(&ov, 0, sizeof(ov)); + ov.Offset = pos & 0xffffffff; + ov.OffsetHigh = pos >> 16 >> 16; + if (!WriteFile(env->me_fd, dp, size, NULL, &ov)) { + rc = ErrCode(); + DPRINTF(("WriteFile: %d", rc)); + return rc; + } +#else + /* Write up to MDB_COMMIT_PAGES dirty pages at a time. */ + if (pos!=next_pos || n==MDB_COMMIT_PAGES || wsize+size>MAX_WRITE) { + if (n) { +retry_write: + /* Write previous page(s) */ +#ifdef MDB_USE_PWRITEV + wres = pwritev(env->me_fd, iov, n, wpos); +#else + if (n == 1) { + wres = pwrite(env->me_fd, iov[0].iov_base, wsize, wpos); + } else { +retry_seek: + if (lseek(env->me_fd, wpos, SEEK_SET) == -1) { + rc = ErrCode(); + if (rc == EINTR) + goto retry_seek; + DPRINTF(("lseek: %s", strerror(rc))); + return rc; + } + wres = writev(env->me_fd, iov, n); + } +#endif + if (wres != wsize) { + if (wres < 0) { + rc = ErrCode(); + if (rc == EINTR) + goto retry_write; + DPRINTF(("Write error: %s", strerror(rc))); + } else { + rc = EIO; /* TODO: Use which error code? */ + DPUTS("short write, filesystem full?"); + } + return rc; + } + n = 0; + } + if (i > pagecount) + break; + wpos = pos; + wsize = 0; + } + DPRINTF(("committing page %"Z"u", pgno)); + next_pos = pos + size; + iov[n].iov_len = size; + iov[n].iov_base = (char *)dp; + wsize += size; + n++; +#endif /* _WIN32 */ + } + + /* MIPS has cache coherency issues, this is a no-op everywhere else + * Note: for any size >= on-chip cache size, entire on-chip cache is + * flushed. + */ + CACHEFLUSH(env->me_map, txn->mt_next_pgno * env->me_psize, DCACHE); + + for (i = keep; ++i <= pagecount; ) { + dp = dl[i].mptr; + /* This is a page we skipped above */ + if (!dl[i].mid) { + dl[++j] = dl[i]; + dl[j].mid = dp->mp_pgno; + continue; + } + mdb_dpage_free(env, dp); + } + +done: + i--; + txn->mt_dirty_room += i - j; + dl[0].mid = j; + return MDB_SUCCESS; +} + +int +mdb_txn_commit(MDB_txn *txn) +{ + int rc; + unsigned int i, end_mode; + MDB_env *env; + + if (txn == NULL) + return EINVAL; + + /* mdb_txn_end() mode for a commit which writes nothing */ + end_mode = MDB_END_EMPTY_COMMIT|MDB_END_UPDATE|MDB_END_SLOT|MDB_END_FREE; + + if (txn->mt_child) { + rc = mdb_txn_commit(txn->mt_child); + if (rc) + goto fail; + } + + env = txn->mt_env; + + if (F_ISSET(txn->mt_flags, MDB_TXN_RDONLY)) { + goto done; + } + + if (txn->mt_flags & (MDB_TXN_FINISHED|MDB_TXN_ERROR)) { + DPUTS("txn has failed/finished, can't commit"); + if (txn->mt_parent) + txn->mt_parent->mt_flags |= MDB_TXN_ERROR; + rc = MDB_BAD_TXN; + goto fail; + } + + if (txn->mt_parent) { + MDB_txn *parent = txn->mt_parent; + MDB_page **lp; + MDB_ID2L dst, src; + MDB_IDL pspill; + unsigned x, y, len, ps_len; + + /* Append our free list to parent's */ + rc = mdb_midl_append_list(&parent->mt_free_pgs, txn->mt_free_pgs); + if (rc) + goto fail; + mdb_midl_free(txn->mt_free_pgs); + /* Failures after this must either undo the changes + * to the parent or set MDB_TXN_ERROR in the parent. + */ + + parent->mt_next_pgno = txn->mt_next_pgno; + parent->mt_flags = txn->mt_flags; + + /* Merge our cursors into parent's and close them */ + mdb_cursors_close(txn, 1); + + /* Update parent's DB table. */ + memcpy(parent->mt_dbs, txn->mt_dbs, txn->mt_numdbs * sizeof(MDB_db)); + parent->mt_numdbs = txn->mt_numdbs; + parent->mt_dbflags[FREE_DBI] = txn->mt_dbflags[FREE_DBI]; + parent->mt_dbflags[MAIN_DBI] = txn->mt_dbflags[MAIN_DBI]; + for (i=CORE_DBS; i<txn->mt_numdbs; i++) { + /* preserve parent's DB_NEW status */ + x = parent->mt_dbflags[i] & DB_NEW; + parent->mt_dbflags[i] = txn->mt_dbflags[i] | x; + } + + dst = parent->mt_u.dirty_list; + src = txn->mt_u.dirty_list; + /* Remove anything in our dirty list from parent's spill list */ + if ((pspill = parent->mt_spill_pgs) && (ps_len = pspill[0])) { + x = y = ps_len; + pspill[0] = (pgno_t)-1; + /* Mark our dirty pages as deleted in parent spill list */ + for (i=0, len=src[0].mid; ++i <= len; ) { + MDB_ID pn = src[i].mid << 1; + while (pn > pspill[x]) + x--; + if (pn == pspill[x]) { + pspill[x] = 1; + y = --x; + } + } + /* Squash deleted pagenums if we deleted any */ + for (x=y; ++x <= ps_len; ) + if (!(pspill[x] & 1)) + pspill[++y] = pspill[x]; + pspill[0] = y; + } + + /* Remove anything in our spill list from parent's dirty list */ + if (txn->mt_spill_pgs && txn->mt_spill_pgs[0]) { + for (i=1; i<=txn->mt_spill_pgs[0]; i++) { + MDB_ID pn = txn->mt_spill_pgs[i]; + if (pn & 1) + continue; /* deleted spillpg */ + pn >>= 1; + y = mdb_mid2l_search(dst, pn); + if (y <= dst[0].mid && dst[y].mid == pn) { + free(dst[y].mptr); + while (y < dst[0].mid) { + dst[y] = dst[y+1]; + y++; + } + dst[0].mid--; + } + } + } + + /* Find len = length of merging our dirty list with parent's */ + x = dst[0].mid; + dst[0].mid = 0; /* simplify loops */ + if (parent->mt_parent) { + len = x + src[0].mid; + y = mdb_mid2l_search(src, dst[x].mid + 1) - 1; + for (i = x; y && i; y--) { + pgno_t yp = src[y].mid; + while (yp < dst[i].mid) + i--; + if (yp == dst[i].mid) { + i--; + len--; + } + } + } else { /* Simplify the above for single-ancestor case */ + len = MDB_IDL_UM_MAX - txn->mt_dirty_room; + } + /* Merge our dirty list with parent's */ + y = src[0].mid; + for (i = len; y; dst[i--] = src[y--]) { + pgno_t yp = src[y].mid; + while (yp < dst[x].mid) + dst[i--] = dst[x--]; + if (yp == dst[x].mid) + free(dst[x--].mptr); + } + mdb_tassert(txn, i == x); + dst[0].mid = len; + free(txn->mt_u.dirty_list); + parent->mt_dirty_room = txn->mt_dirty_room; + if (txn->mt_spill_pgs) { + if (parent->mt_spill_pgs) { + /* TODO: Prevent failure here, so parent does not fail */ + rc = mdb_midl_append_list(&parent->mt_spill_pgs, txn->mt_spill_pgs); + if (rc) + parent->mt_flags |= MDB_TXN_ERROR; + mdb_midl_free(txn->mt_spill_pgs); + mdb_midl_sort(parent->mt_spill_pgs); + } else { + parent->mt_spill_pgs = txn->mt_spill_pgs; + } + } + + /* Append our loose page list to parent's */ + for (lp = &parent->mt_loose_pgs; *lp; lp = &NEXT_LOOSE_PAGE(*lp)) + ; + *lp = txn->mt_loose_pgs; + parent->mt_loose_count += txn->mt_loose_count; + + parent->mt_child = NULL; + mdb_midl_free(((MDB_ntxn *)txn)->mnt_pgstate.mf_pghead); + free(txn); + return rc; + } + + if (txn != env->me_txn) { + DPUTS("attempt to commit unknown transaction"); + rc = EINVAL; + goto fail; + } + + mdb_cursors_close(txn, 0); + + if (!txn->mt_u.dirty_list[0].mid && + !(txn->mt_flags & (MDB_TXN_DIRTY|MDB_TXN_SPILLS))) + goto done; + + DPRINTF(("committing txn %"Z"u %p on mdbenv %p, root page %"Z"u", + txn->mt_txnid, (void*)txn, (void*)env, txn->mt_dbs[MAIN_DBI].md_root)); + + /* Update DB root pointers */ + if (txn->mt_numdbs > CORE_DBS) { + MDB_cursor mc; + MDB_dbi i; + MDB_val data; + data.mv_size = sizeof(MDB_db); + + mdb_cursor_init(&mc, txn, MAIN_DBI, NULL); + for (i = CORE_DBS; i < txn->mt_numdbs; i++) { + if (txn->mt_dbflags[i] & DB_DIRTY) { + if (TXN_DBI_CHANGED(txn, i)) { + rc = MDB_BAD_DBI; + goto fail; + } + data.mv_data = &txn->mt_dbs[i]; + rc = mdb_cursor_put(&mc, &txn->mt_dbxs[i].md_name, &data, + F_SUBDATA); + if (rc) + goto fail; + } + } + } + + rc = mdb_freelist_save(txn); + if (rc) + goto fail; + + mdb_midl_free(env->me_pghead); + env->me_pghead = NULL; + mdb_midl_shrink(&txn->mt_free_pgs); + +#if (MDB_DEBUG) > 2 + mdb_audit(txn); +#endif + + if ((rc = mdb_page_flush(txn, 0)) || + (rc = mdb_env_sync(env, 0)) || + (rc = mdb_env_write_meta(txn))) + goto fail; + end_mode = MDB_END_COMMITTED|MDB_END_UPDATE; + +done: + mdb_txn_end(txn, end_mode); + return MDB_SUCCESS; + +fail: + mdb_txn_abort(txn); + return rc; +} + +/** Read the environment parameters of a DB environment before + * mapping it into memory. + * @param[in] env the environment handle + * @param[out] meta address of where to store the meta information + * @return 0 on success, non-zero on failure. + */ +static int ESECT +mdb_env_read_header(MDB_env *env, MDB_meta *meta) +{ + MDB_metabuf pbuf; + MDB_page *p; + MDB_meta *m; + int i, rc, off; + enum { Size = sizeof(pbuf) }; + + /* We don't know the page size yet, so use a minimum value. + * Read both meta pages so we can use the latest one. + */ + + for (i=off=0; i<NUM_METAS; i++, off += meta->mm_psize) { +#ifdef _WIN32 + DWORD len; + OVERLAPPED ov; + memset(&ov, 0, sizeof(ov)); + ov.Offset = off; + rc = ReadFile(env->me_fd, &pbuf, Size, &len, &ov) ? (int)len : -1; + if (rc == -1 && ErrCode() == ERROR_HANDLE_EOF) + rc = 0; +#else + rc = pread(env->me_fd, &pbuf, Size, off); +#endif + if (rc != Size) { + if (rc == 0 && off == 0) + return ENOENT; + rc = rc < 0 ? (int) ErrCode() : MDB_INVALID; + DPRINTF(("read: %s", mdb_strerror(rc))); + return rc; + } + + p = (MDB_page *)&pbuf; + + if (!F_ISSET(p->mp_flags, P_META)) { + DPRINTF(("page %"Z"u not a meta page", p->mp_pgno)); + return MDB_INVALID; + } + + m = METADATA(p); + if (m->mm_magic != MDB_MAGIC) { + DPUTS("meta has invalid magic"); + return MDB_INVALID; + } + + if (m->mm_version != MDB_DATA_VERSION) { + DPRINTF(("database is version %u, expected version %u", + m->mm_version, MDB_DATA_VERSION)); + return MDB_VERSION_MISMATCH; + } + + if (off == 0 || m->mm_txnid > meta->mm_txnid) + *meta = *m; + } + return 0; +} + +/** Fill in most of the zeroed #MDB_meta for an empty database environment */ +static void ESECT +mdb_env_init_meta0(MDB_env *env, MDB_meta *meta) +{ + meta->mm_magic = MDB_MAGIC; + meta->mm_version = MDB_DATA_VERSION; + meta->mm_mapsize = env->me_mapsize; + meta->mm_psize = env->me_psize; + meta->mm_last_pg = NUM_METAS-1; + meta->mm_flags = env->me_flags & 0xffff; + meta->mm_flags |= MDB_INTEGERKEY; /* this is mm_dbs[FREE_DBI].md_flags */ + meta->mm_dbs[FREE_DBI].md_root = P_INVALID; + meta->mm_dbs[MAIN_DBI].md_root = P_INVALID; +} + +/** Write the environment parameters of a freshly created DB environment. + * @param[in] env the environment handle + * @param[in] meta the #MDB_meta to write + * @return 0 on success, non-zero on failure. + */ +static int ESECT +mdb_env_init_meta(MDB_env *env, MDB_meta *meta) +{ + MDB_page *p, *q; + int rc; + unsigned int psize; +#ifdef _WIN32 + DWORD len; + OVERLAPPED ov; + memset(&ov, 0, sizeof(ov)); +#define DO_PWRITE(rc, fd, ptr, size, len, pos) do { \ + ov.Offset = pos; \ + rc = WriteFile(fd, ptr, size, &len, &ov); } while(0) +#else + int len; +#define DO_PWRITE(rc, fd, ptr, size, len, pos) do { \ + len = pwrite(fd, ptr, size, pos); \ + if (len == -1 && ErrCode() == EINTR) continue; \ + rc = (len >= 0); break; } while(1) +#endif + + DPUTS("writing new meta page"); + + psize = env->me_psize; + + p = calloc(NUM_METAS, psize); + if (!p) + return ENOMEM; + + p->mp_pgno = 0; + p->mp_flags = P_META; + *(MDB_meta *)METADATA(p) = *meta; + + q = (MDB_page *)((char *)p + psize); + q->mp_pgno = 1; + q->mp_flags = P_META; + *(MDB_meta *)METADATA(q) = *meta; + + DO_PWRITE(rc, env->me_fd, p, psize * NUM_METAS, len, 0); + if (!rc) + rc = ErrCode(); + else if ((unsigned) len == psize * NUM_METAS) + rc = MDB_SUCCESS; + else + rc = ENOSPC; + free(p); + return rc; +} + +/** Update the environment info to commit a transaction. + * @param[in] txn the transaction that's being committed + * @return 0 on success, non-zero on failure. + */ +static int +mdb_env_write_meta(MDB_txn *txn) +{ + MDB_env *env; + MDB_meta meta, metab, *mp; + unsigned flags; + size_t mapsize; + off_t off; + int rc, len, toggle; + char *ptr; + HANDLE mfd; +#ifdef _WIN32 + OVERLAPPED ov; +#else + int r2; +#endif + + toggle = txn->mt_txnid & 1; + DPRINTF(("writing meta page %d for root page %"Z"u", + toggle, txn->mt_dbs[MAIN_DBI].md_root)); + + env = txn->mt_env; + flags = env->me_flags; + mp = env->me_metas[toggle]; + mapsize = env->me_metas[toggle ^ 1]->mm_mapsize; + /* Persist any increases of mapsize config */ + if (mapsize < env->me_mapsize) + mapsize = env->me_mapsize; + + if (flags & MDB_WRITEMAP) { + mp->mm_mapsize = mapsize; + mp->mm_dbs[FREE_DBI] = txn->mt_dbs[FREE_DBI]; + mp->mm_dbs[MAIN_DBI] = txn->mt_dbs[MAIN_DBI]; + mp->mm_last_pg = txn->mt_next_pgno - 1; +#if (__GNUC__ * 100 + __GNUC_MINOR__ >= 404) && /* TODO: portability */ \ + !(defined(__i386__) || defined(__x86_64__)) + /* LY: issue a memory barrier, if not x86. ITS#7969 */ + __sync_synchronize(); +#endif + mp->mm_txnid = txn->mt_txnid; + if (!(flags & (MDB_NOMETASYNC|MDB_NOSYNC))) { + unsigned meta_size = env->me_psize; + rc = (env->me_flags & MDB_MAPASYNC) ? MS_ASYNC : MS_SYNC; + ptr = (char *)mp - PAGEHDRSZ; +#ifndef _WIN32 /* POSIX msync() requires ptr = start of OS page */ + r2 = (ptr - env->me_map) & (env->me_os_psize - 1); + ptr -= r2; + meta_size += r2; +#endif + if (MDB_MSYNC(ptr, meta_size, rc)) { + rc = ErrCode(); + goto fail; + } + } + goto done; + } + metab.mm_txnid = mp->mm_txnid; + metab.mm_last_pg = mp->mm_last_pg; + + meta.mm_mapsize = mapsize; + meta.mm_dbs[FREE_DBI] = txn->mt_dbs[FREE_DBI]; + meta.mm_dbs[MAIN_DBI] = txn->mt_dbs[MAIN_DBI]; + meta.mm_last_pg = txn->mt_next_pgno - 1; + meta.mm_txnid = txn->mt_txnid; + + off = offsetof(MDB_meta, mm_mapsize); + ptr = (char *)&meta + off; + len = sizeof(MDB_meta) - off; + off += (char *)mp - env->me_map; + + /* Write to the SYNC fd unless MDB_NOSYNC/MDB_NOMETASYNC. + * (me_mfd goes to the same file as me_fd, but writing to it + * also syncs to disk. Avoids a separate fdatasync() call.) + */ + mfd = (flags & (MDB_NOSYNC|MDB_NOMETASYNC)) ? env->me_fd : env->me_mfd; +#ifdef _WIN32 + { + memset(&ov, 0, sizeof(ov)); + ov.Offset = off; + if (!WriteFile(mfd, ptr, len, (DWORD *)&rc, &ov)) + rc = -1; + } +#else +retry_write: + rc = pwrite(mfd, ptr, len, off); +#endif + if (rc != len) { + rc = rc < 0 ? ErrCode() : EIO; +#ifndef _WIN32 + if (rc == EINTR) + goto retry_write; +#endif + DPUTS("write failed, disk error?"); + /* On a failure, the pagecache still contains the new data. + * Write some old data back, to prevent it from being used. + * Use the non-SYNC fd; we know it will fail anyway. + */ + meta.mm_last_pg = metab.mm_last_pg; + meta.mm_txnid = metab.mm_txnid; +#ifdef _WIN32 + memset(&ov, 0, sizeof(ov)); + ov.Offset = off; + WriteFile(env->me_fd, ptr, len, NULL, &ov); +#else + r2 = pwrite(env->me_fd, ptr, len, off); + (void)r2; /* Silence warnings. We don't care about pwrite's return value */ +#endif +fail: + env->me_flags |= MDB_FATAL_ERROR; + return rc; + } + /* MIPS has cache coherency issues, this is a no-op everywhere else */ + CACHEFLUSH(env->me_map + off, len, DCACHE); +done: + /* Memory ordering issues are irrelevant; since the entire writer + * is wrapped by wmutex, all of these changes will become visible + * after the wmutex is unlocked. Since the DB is multi-version, + * readers will get consistent data regardless of how fresh or + * how stale their view of these values is. + */ + if (env->me_txns) + env->me_txns->mti_txnid = txn->mt_txnid; + + return MDB_SUCCESS; +} + +/** Check both meta pages to see which one is newer. + * @param[in] env the environment handle + * @return newest #MDB_meta. + */ +static MDB_meta * +mdb_env_pick_meta(const MDB_env *env) +{ + MDB_meta *const *metas = env->me_metas; + return metas[ metas[0]->mm_txnid < metas[1]->mm_txnid ]; +} + +int ESECT +mdb_env_create(MDB_env **env) +{ + MDB_env *e; + + e = calloc(1, sizeof(MDB_env)); + if (!e) + return ENOMEM; + + e->me_maxreaders = DEFAULT_READERS; + e->me_maxdbs = e->me_numdbs = CORE_DBS; + e->me_fd = INVALID_HANDLE_VALUE; + e->me_lfd = INVALID_HANDLE_VALUE; + e->me_mfd = INVALID_HANDLE_VALUE; +#ifdef MDB_USE_POSIX_SEM + e->me_rmutex = SEM_FAILED; + e->me_wmutex = SEM_FAILED; +#endif + e->me_pid = getpid(); + GET_PAGESIZE(e->me_os_psize); + VGMEMP_CREATE(e,0,0); + *env = e; + return MDB_SUCCESS; +} + +static int ESECT +mdb_env_map(MDB_env *env, void *addr) +{ + MDB_page *p; + unsigned int flags = env->me_flags; +#ifdef _WIN32 + int rc; + HANDLE mh; + LONG sizelo, sizehi; + size_t msize; + + if (flags & MDB_RDONLY) { + /* Don't set explicit map size, use whatever exists */ + msize = 0; + sizelo = 0; + sizehi = 0; + } else { + msize = env->me_mapsize; + sizelo = msize & 0xffffffff; + sizehi = msize >> 16 >> 16; /* only needed on Win64 */ + + /* Windows won't create mappings for zero length files. + * and won't map more than the file size. + * Just set the maxsize right now. + */ + if (SetFilePointer(env->me_fd, sizelo, &sizehi, 0) != (DWORD)sizelo + || !SetEndOfFile(env->me_fd) + || SetFilePointer(env->me_fd, 0, NULL, 0) != 0) + return ErrCode(); + } + + mh = CreateFileMapping(env->me_fd, NULL, flags & MDB_WRITEMAP ? + PAGE_READWRITE : PAGE_READONLY, + sizehi, sizelo, NULL); + if (!mh) + return ErrCode(); + env->me_map = MapViewOfFileEx(mh, flags & MDB_WRITEMAP ? + FILE_MAP_WRITE : FILE_MAP_READ, + 0, 0, msize, addr); + rc = env->me_map ? 0 : ErrCode(); + CloseHandle(mh); + if (rc) + return rc; +#else + int prot = PROT_READ; + if (flags & MDB_WRITEMAP) { + prot |= PROT_WRITE; + if (ftruncate(env->me_fd, env->me_mapsize) < 0) + return ErrCode(); + } + env->me_map = mmap(addr, env->me_mapsize, prot, MAP_SHARED, + env->me_fd, 0); + if (env->me_map == MAP_FAILED) { + env->me_map = NULL; + return ErrCode(); + } + + if (flags & MDB_NORDAHEAD) { + /* Turn off readahead. It's harmful when the DB is larger than RAM. */ +#ifdef MADV_RANDOM + madvise(env->me_map, env->me_mapsize, MADV_RANDOM); +#else +#ifdef POSIX_MADV_RANDOM + posix_madvise(env->me_map, env->me_mapsize, POSIX_MADV_RANDOM); +#endif /* POSIX_MADV_RANDOM */ +#endif /* MADV_RANDOM */ + } +#endif /* _WIN32 */ + + /* Can happen because the address argument to mmap() is just a + * hint. mmap() can pick another, e.g. if the range is in use. + * The MAP_FIXED flag would prevent that, but then mmap could + * instead unmap existing pages to make room for the new map. + */ + if (addr && env->me_map != addr) + return EBUSY; /* TODO: Make a new MDB_* error code? */ + + p = (MDB_page *)env->me_map; + env->me_metas[0] = METADATA(p); + env->me_metas[1] = (MDB_meta *)((char *)env->me_metas[0] + env->me_psize); + + return MDB_SUCCESS; +} + +int ESECT +mdb_env_set_mapsize(MDB_env *env, size_t size) +{ + /* If env is already open, caller is responsible for making + * sure there are no active txns. + */ + if (env->me_map) { + int rc; + MDB_meta *meta; + void *old; + if (env->me_txn) + return EINVAL; + meta = mdb_env_pick_meta(env); + if (!size) + size = meta->mm_mapsize; + { + /* Silently round up to minimum if the size is too small */ + size_t minsize = (meta->mm_last_pg + 1) * env->me_psize; + if (size < minsize) + size = minsize; + } + munmap(env->me_map, env->me_mapsize); + env->me_mapsize = size; + old = (env->me_flags & MDB_FIXEDMAP) ? env->me_map : NULL; + rc = mdb_env_map(env, old); + if (rc) + return rc; + } + env->me_mapsize = size; + if (env->me_psize) + env->me_maxpg = env->me_mapsize / env->me_psize; + return MDB_SUCCESS; +} + +int ESECT +mdb_env_set_maxdbs(MDB_env *env, MDB_dbi dbs) +{ + if (env->me_map) + return EINVAL; + env->me_maxdbs = dbs + CORE_DBS; + return MDB_SUCCESS; +} + +int ESECT +mdb_env_set_maxreaders(MDB_env *env, unsigned int readers) +{ + if (env->me_map || readers < 1) + return EINVAL; + env->me_maxreaders = readers; + return MDB_SUCCESS; +} + +int ESECT +mdb_env_get_maxreaders(MDB_env *env, unsigned int *readers) +{ + if (!env || !readers) + return EINVAL; + *readers = env->me_maxreaders; + return MDB_SUCCESS; +} + +static int ESECT +mdb_fsize(HANDLE fd, size_t *size) +{ +#ifdef _WIN32 + LARGE_INTEGER fsize; + + if (!GetFileSizeEx(fd, &fsize)) + return ErrCode(); + + *size = fsize.QuadPart; +#else + struct stat st; + + if (fstat(fd, &st)) + return ErrCode(); + + *size = st.st_size; +#endif + return MDB_SUCCESS; +} + + +#ifdef _WIN32 +typedef wchar_t mdb_nchar_t; +# define MDB_NAME(str) L##str +# define mdb_name_cpy wcscpy +#else +/** Character type for file names: char on Unix, wchar_t on Windows */ +typedef char mdb_nchar_t; +# define MDB_NAME(str) str /**< #mdb_nchar_t[] string literal */ +# define mdb_name_cpy strcpy /**< Copy name (#mdb_nchar_t string) */ +#endif + +/** Filename - string of #mdb_nchar_t[] */ +typedef struct MDB_name { + int mn_len; /**< Length */ + int mn_alloced; /**< True if #mn_val was malloced */ + mdb_nchar_t *mn_val; /**< Contents */ +} MDB_name; + +/** Filename suffixes [datafile,lockfile][without,with MDB_NOSUBDIR] */ +static const mdb_nchar_t *const mdb_suffixes[2][2] = { + { MDB_NAME("/data.mdb"), MDB_NAME("") }, + { MDB_NAME("/lock.mdb"), MDB_NAME("-lock") } +}; + +#define MDB_SUFFLEN 9 /**< Max string length in #mdb_suffixes[] */ + +/** Set up filename + scratch area for filename suffix, for opening files. + * It should be freed with #mdb_fname_destroy(). + * On Windows, paths are converted from char *UTF-8 to wchar_t *UTF-16. + * + * @param[in] path Pathname for #mdb_env_open(). + * @param[in] envflags Whether a subdir and/or lockfile will be used. + * @param[out] fname Resulting filename, with room for a suffix if necessary. + */ +static int ESECT +mdb_fname_init(const char *path, unsigned envflags, MDB_name *fname) +{ + int no_suffix = F_ISSET(envflags, MDB_NOSUBDIR|MDB_NOLOCK); + fname->mn_alloced = 0; +#ifdef _WIN32 + return utf8_to_utf16(path, fname, no_suffix ? 0 : MDB_SUFFLEN); +#else + fname->mn_len = strlen(path); + if (no_suffix) + fname->mn_val = (char *) path; + else if ((fname->mn_val = malloc(fname->mn_len + MDB_SUFFLEN+1)) != NULL) { + fname->mn_alloced = 1; + strcpy(fname->mn_val, path); + } + else + return ENOMEM; + return MDB_SUCCESS; +#endif +} + +/** Destroy \b fname from #mdb_fname_init() */ +#define mdb_fname_destroy(fname) \ + do { if ((fname).mn_alloced) free((fname).mn_val); } while (0) + +#ifdef O_CLOEXEC /* POSIX.1-2008: Set FD_CLOEXEC atomically at open() */ +# define MDB_CLOEXEC O_CLOEXEC +#else +# define MDB_CLOEXEC 0 +#endif + +/** File type, access mode etc. for #mdb_fopen() */ +enum mdb_fopen_type { +#ifdef _WIN32 + MDB_O_RDONLY, MDB_O_RDWR, MDB_O_META, MDB_O_COPY, MDB_O_LOCKS +#else + /* A comment in mdb_fopen() explains some O_* flag choices. */ + MDB_O_RDONLY= O_RDONLY, /**< for RDONLY me_fd */ + MDB_O_RDWR = O_RDWR |O_CREAT, /**< for me_fd */ + MDB_O_META = O_WRONLY|MDB_DSYNC |MDB_CLOEXEC, /**< for me_mfd */ + MDB_O_COPY = O_WRONLY|O_CREAT|O_EXCL|MDB_CLOEXEC, /**< for #mdb_env_copy() */ + /** Bitmask for open() flags in enum #mdb_fopen_type. The other bits + * distinguish otherwise-equal MDB_O_* constants from each other. + */ + MDB_O_MASK = MDB_O_RDWR|MDB_CLOEXEC | MDB_O_RDONLY|MDB_O_META|MDB_O_COPY, + MDB_O_LOCKS = MDB_O_RDWR|MDB_CLOEXEC | ((MDB_O_MASK+1) & ~MDB_O_MASK) /**< for me_lfd */ +#endif +}; + +/** Open an LMDB file. + * @param[in] env The LMDB environment. + * @param[in,out] fname Path from from #mdb_fname_init(). A suffix is + * appended if necessary to create the filename, without changing mn_len. + * @param[in] which Determines file type, access mode, etc. + * @param[in] mode The Unix permissions for the file, if we create it. + * @param[out] res Resulting file handle. + * @return 0 on success, non-zero on failure. + */ +static int ESECT +mdb_fopen(const MDB_env *env, MDB_name *fname, + enum mdb_fopen_type which, mdb_mode_t mode, + HANDLE *res) +{ + int rc = MDB_SUCCESS; + HANDLE fd; +#ifdef _WIN32 + DWORD acc, share, disp, attrs; +#else + int flags; +#endif + + if (fname->mn_alloced) /* modifiable copy */ + mdb_name_cpy(fname->mn_val + fname->mn_len, + mdb_suffixes[which==MDB_O_LOCKS][F_ISSET(env->me_flags, MDB_NOSUBDIR)]); + + /* The directory must already exist. Usually the file need not. + * MDB_O_META requires the file because we already created it using + * MDB_O_RDWR. MDB_O_COPY must not overwrite an existing file. + * + * With MDB_O_COPY we do not want the OS to cache the writes, since + * the source data is already in the OS cache. + * + * The lockfile needs FD_CLOEXEC (close file descriptor on exec*()) + * to avoid the flock() issues noted under Caveats in lmdb.h. + * Also set it for other filehandles which the user cannot get at + * and close himself, which he may need after fork(). I.e. all but + * me_fd, which programs do use via mdb_env_get_fd(). + */ + +#ifdef _WIN32 + acc = GENERIC_READ|GENERIC_WRITE; + share = FILE_SHARE_READ|FILE_SHARE_WRITE; + disp = OPEN_ALWAYS; + attrs = FILE_ATTRIBUTE_NORMAL; + switch (which) { + case MDB_O_RDONLY: /* read-only datafile */ + acc = GENERIC_READ; + disp = OPEN_EXISTING; + break; + case MDB_O_META: /* for writing metapages */ + acc = GENERIC_WRITE; + disp = OPEN_EXISTING; + attrs = FILE_ATTRIBUTE_NORMAL|FILE_FLAG_WRITE_THROUGH; + break; + case MDB_O_COPY: /* mdb_env_copy() & co */ + acc = GENERIC_WRITE; + share = 0; + disp = CREATE_NEW; + attrs = FILE_FLAG_NO_BUFFERING|FILE_FLAG_WRITE_THROUGH; + break; + default: break; /* silence gcc -Wswitch (not all enum values handled) */ + } + fd = CreateFileW(fname->mn_val, acc, share, NULL, disp, attrs, NULL); +#else + fd = open(fname->mn_val, which & MDB_O_MASK, mode); +#endif + + if (fd == INVALID_HANDLE_VALUE) + rc = ErrCode(); +#ifndef _WIN32 + else { + if (which != MDB_O_RDONLY && which != MDB_O_RDWR) { + /* Set CLOEXEC if we could not pass it to open() */ + if (!MDB_CLOEXEC && (flags = fcntl(fd, F_GETFD)) != -1) + (void) fcntl(fd, F_SETFD, flags | FD_CLOEXEC); + } + if (which == MDB_O_COPY && env->me_psize >= env->me_os_psize) { + /* This may require buffer alignment. There is no portable + * way to ask how much, so we require OS pagesize alignment. + */ +# ifdef F_NOCACHE /* __APPLE__ */ + (void) fcntl(fd, F_NOCACHE, 1); +# elif defined O_DIRECT + /* open(...O_DIRECT...) would break on filesystems without + * O_DIRECT support (ITS#7682). Try to set it here instead. + */ + if ((flags = fcntl(fd, F_GETFL)) != -1) + (void) fcntl(fd, F_SETFL, flags | O_DIRECT); +# endif + } + } +#endif /* !_WIN32 */ + + *res = fd; + return rc; +} + + +#ifdef BROKEN_FDATASYNC +#include <sys/utsname.h> +#include <sys/vfs.h> +#endif + +/** Further setup required for opening an LMDB environment + */ +static int ESECT +mdb_env_open2(MDB_env *env) +{ + unsigned int flags = env->me_flags; + int i, newenv = 0, rc; + MDB_meta meta; + +#ifdef _WIN32 + /* See if we should use QueryLimited */ + rc = GetVersion(); + if ((rc & 0xff) > 5) + env->me_pidquery = MDB_PROCESS_QUERY_LIMITED_INFORMATION; + else + env->me_pidquery = PROCESS_QUERY_INFORMATION; +#endif /* _WIN32 */ + +#ifdef BROKEN_FDATASYNC + /* ext3/ext4 fdatasync is broken on some older Linux kernels. + * https://lkml.org/lkml/2012/9/3/83 + * Kernels after 3.6-rc6 are known good. + * https://lkml.org/lkml/2012/9/10/556 + * See if the DB is on ext3/ext4, then check for new enough kernel + * Kernels 2.6.32.60, 2.6.34.15, 3.2.30, and 3.5.4 are also known + * to be patched. + */ + { + struct statfs st; + fstatfs(env->me_fd, &st); + while (st.f_type == 0xEF53) { + struct utsname uts; + int i; + uname(&uts); + if (uts.release[0] < '3') { + if (!strncmp(uts.release, "2.6.32.", 7)) { + i = atoi(uts.release+7); + if (i >= 60) + break; /* 2.6.32.60 and newer is OK */ + } else if (!strncmp(uts.release, "2.6.34.", 7)) { + i = atoi(uts.release+7); + if (i >= 15) + break; /* 2.6.34.15 and newer is OK */ + } + } else if (uts.release[0] == '3') { + i = atoi(uts.release+2); + if (i > 5) + break; /* 3.6 and newer is OK */ + if (i == 5) { + i = atoi(uts.release+4); + if (i >= 4) + break; /* 3.5.4 and newer is OK */ + } else if (i == 2) { + i = atoi(uts.release+4); + if (i >= 30) + break; /* 3.2.30 and newer is OK */ + } + } else { /* 4.x and newer is OK */ + break; + } + env->me_flags |= MDB_FSYNCONLY; + break; + } + } +#endif + + if ((i = mdb_env_read_header(env, &meta)) != 0) { + if (i != ENOENT) + return i; + DPUTS("new mdbenv"); + newenv = 1; + env->me_psize = env->me_os_psize; + if (env->me_psize > MAX_PAGESIZE) + env->me_psize = MAX_PAGESIZE; + memset(&meta, 0, sizeof(meta)); + mdb_env_init_meta0(env, &meta); + meta.mm_mapsize = DEFAULT_MAPSIZE; + } else { + env->me_psize = meta.mm_psize; + } + + /* Was a mapsize configured? */ + if (!env->me_mapsize) { + env->me_mapsize = meta.mm_mapsize; + } + { + /* Make sure mapsize >= committed data size. Even when using + * mm_mapsize, which could be broken in old files (ITS#7789). + */ + size_t minsize = (meta.mm_last_pg + 1) * meta.mm_psize; + if (env->me_mapsize < minsize) + env->me_mapsize = minsize; + } + meta.mm_mapsize = env->me_mapsize; + + if (newenv && !(flags & MDB_FIXEDMAP)) { + /* mdb_env_map() may grow the datafile. Write the metapages + * first, so the file will be valid if initialization fails. + * Except with FIXEDMAP, since we do not yet know mm_address. + * We could fill in mm_address later, but then a different + * program might end up doing that - one with a memory layout + * and map address which does not suit the main program. + */ + rc = mdb_env_init_meta(env, &meta); + if (rc) + return rc; + newenv = 0; + } + + rc = mdb_env_map(env, (flags & MDB_FIXEDMAP) ? meta.mm_address : NULL); + if (rc) + return rc; + + if (newenv) { + if (flags & MDB_FIXEDMAP) + meta.mm_address = env->me_map; + i = mdb_env_init_meta(env, &meta); + if (i != MDB_SUCCESS) { + return i; + } + } + + env->me_maxfree_1pg = (env->me_psize - PAGEHDRSZ) / sizeof(pgno_t) - 1; + env->me_nodemax = (((env->me_psize - PAGEHDRSZ) / MDB_MINKEYS) & -2) + - sizeof(indx_t); +#if !(MDB_MAXKEYSIZE) + env->me_maxkey = env->me_nodemax - (NODESIZE + sizeof(MDB_db)); +#endif + env->me_maxpg = env->me_mapsize / env->me_psize; + +#if MDB_DEBUG + { + MDB_meta *meta = mdb_env_pick_meta(env); + MDB_db *db = &meta->mm_dbs[MAIN_DBI]; + + DPRINTF(("opened database version %u, pagesize %u", + meta->mm_version, env->me_psize)); + DPRINTF(("using meta page %d", (int) (meta->mm_txnid & 1))); + DPRINTF(("depth: %u", db->md_depth)); + DPRINTF(("entries: %"Z"u", db->md_entries)); + DPRINTF(("branch pages: %"Z"u", db->md_branch_pages)); + DPRINTF(("leaf pages: %"Z"u", db->md_leaf_pages)); + DPRINTF(("overflow pages: %"Z"u", db->md_overflow_pages)); + DPRINTF(("root: %"Z"u", db->md_root)); + } +#endif + + return MDB_SUCCESS; +} + + +/** Release a reader thread's slot in the reader lock table. + * This function is called automatically when a thread exits. + * @param[in] ptr This points to the slot in the reader lock table. + */ +static void +mdb_env_reader_dest(void *ptr) +{ + MDB_reader *reader = ptr; + +#ifndef _WIN32 + if (reader->mr_pid == getpid()) /* catch pthread_exit() in child process */ +#endif + /* We omit the mutex, so do this atomically (i.e. skip mr_txnid) */ + reader->mr_pid = 0; +} + +#ifdef _WIN32 +/** Junk for arranging thread-specific callbacks on Windows. This is + * necessarily platform and compiler-specific. Windows supports up + * to 1088 keys. Let's assume nobody opens more than 64 environments + * in a single process, for now. They can override this if needed. + */ +#ifndef MAX_TLS_KEYS +#define MAX_TLS_KEYS 64 +#endif +static pthread_key_t mdb_tls_keys[MAX_TLS_KEYS]; +static int mdb_tls_nkeys; + +static void NTAPI mdb_tls_callback(PVOID module, DWORD reason, PVOID ptr) +{ + int i; + switch(reason) { + case DLL_PROCESS_ATTACH: break; + case DLL_THREAD_ATTACH: break; + case DLL_THREAD_DETACH: + for (i=0; i<mdb_tls_nkeys; i++) { + MDB_reader *r = pthread_getspecific(mdb_tls_keys[i]); + if (r) { + mdb_env_reader_dest(r); + } + } + break; + case DLL_PROCESS_DETACH: break; + } +} +#ifdef __GNUC__ +#ifdef _WIN64 +const PIMAGE_TLS_CALLBACK mdb_tls_cbp __attribute__((section (".CRT$XLB"))) = mdb_tls_callback; +#else +PIMAGE_TLS_CALLBACK mdb_tls_cbp __attribute__((section (".CRT$XLB"))) = mdb_tls_callback; +#endif +#else +#ifdef _WIN64 +/* Force some symbol references. + * _tls_used forces the linker to create the TLS directory if not already done + * mdb_tls_cbp prevents whole-program-optimizer from dropping the symbol. + */ +#pragma comment(linker, "/INCLUDE:_tls_used") +#pragma comment(linker, "/INCLUDE:mdb_tls_cbp") +#pragma const_seg(".CRT$XLB") +extern const PIMAGE_TLS_CALLBACK mdb_tls_cbp; +const PIMAGE_TLS_CALLBACK mdb_tls_cbp = mdb_tls_callback; +#pragma const_seg() +#else /* _WIN32 */ +#pragma comment(linker, "/INCLUDE:__tls_used") +#pragma comment(linker, "/INCLUDE:_mdb_tls_cbp") +#pragma data_seg(".CRT$XLB") +PIMAGE_TLS_CALLBACK mdb_tls_cbp = mdb_tls_callback; +#pragma data_seg() +#endif /* WIN 32/64 */ +#endif /* !__GNUC__ */ +#endif + +/** Downgrade the exclusive lock on the region back to shared */ +static int ESECT +mdb_env_share_locks(MDB_env *env, int *excl) +{ + int rc = 0; + MDB_meta *meta = mdb_env_pick_meta(env); + + env->me_txns->mti_txnid = meta->mm_txnid; + +#ifdef _WIN32 + { + OVERLAPPED ov; + /* First acquire a shared lock. The Unlock will + * then release the existing exclusive lock. + */ + memset(&ov, 0, sizeof(ov)); + if (!LockFileEx(env->me_lfd, 0, 0, 1, 0, &ov)) { + rc = ErrCode(); + } else { + UnlockFile(env->me_lfd, 0, 0, 1, 0); + *excl = 0; + } + } +#else + { + struct flock lock_info; + /* The shared lock replaces the existing lock */ + memset((void *)&lock_info, 0, sizeof(lock_info)); + lock_info.l_type = F_RDLCK; + lock_info.l_whence = SEEK_SET; + lock_info.l_start = 0; + lock_info.l_len = 1; + while ((rc = fcntl(env->me_lfd, F_SETLK, &lock_info)) && + (rc = ErrCode()) == EINTR) ; + *excl = rc ? -1 : 0; /* error may mean we lost the lock */ + } +#endif + + return rc; +} + +/** Try to get exclusive lock, otherwise shared. + * Maintain *excl = -1: no/unknown lock, 0: shared, 1: exclusive. + */ +static int ESECT +mdb_env_excl_lock(MDB_env *env, int *excl) +{ + int rc = 0; +#ifdef _WIN32 + if (LockFile(env->me_lfd, 0, 0, 1, 0)) { + *excl = 1; + } else { + OVERLAPPED ov; + memset(&ov, 0, sizeof(ov)); + if (LockFileEx(env->me_lfd, 0, 0, 1, 0, &ov)) { + *excl = 0; + } else { + rc = ErrCode(); + } + } +#else + struct flock lock_info; + memset((void *)&lock_info, 0, sizeof(lock_info)); + lock_info.l_type = F_WRLCK; + lock_info.l_whence = SEEK_SET; + lock_info.l_start = 0; + lock_info.l_len = 1; + while ((rc = fcntl(env->me_lfd, F_SETLK, &lock_info)) && + (rc = ErrCode()) == EINTR) ; + if (!rc) { + *excl = 1; + } else +# ifndef MDB_USE_POSIX_MUTEX + if (*excl < 0) /* always true when MDB_USE_POSIX_MUTEX */ +# endif + { + lock_info.l_type = F_RDLCK; + while ((rc = fcntl(env->me_lfd, F_SETLKW, &lock_info)) && + (rc = ErrCode()) == EINTR) ; + if (rc == 0) + *excl = 0; + } +#endif + return rc; +} + +#ifdef MDB_USE_HASH +/* + * hash_64 - 64 bit Fowler/Noll/Vo-0 FNV-1a hash code + * + * @(#) $Revision: 5.1 $ + * @(#) $Id: hash_64a.c,v 5.1 2009/06/30 09:01:38 chongo Exp $ + * @(#) $Source: /usr/local/src/cmd/fnv/RCS/hash_64a.c,v $ + * + * http://www.isthe.com/chongo/tech/comp/fnv/index.html + * + *** + * + * Please do not copyright this code. This code is in the public domain. + * + * LANDON CURT NOLL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO + * EVENT SHALL LANDON CURT NOLL BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF + * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + * + * By: + * chongo <Landon Curt Noll> /\oo/\ + * http://www.isthe.com/chongo/ + * + * Share and Enjoy! :-) + */ + +typedef unsigned long long mdb_hash_t; +#define MDB_HASH_INIT ((mdb_hash_t)0xcbf29ce484222325ULL) + +/** perform a 64 bit Fowler/Noll/Vo FNV-1a hash on a buffer + * @param[in] val value to hash + * @param[in] hval initial value for hash + * @return 64 bit hash + * + * NOTE: To use the recommended 64 bit FNV-1a hash, use MDB_HASH_INIT as the + * hval arg on the first call. + */ +static mdb_hash_t +mdb_hash_val(MDB_val *val, mdb_hash_t hval) +{ + unsigned char *s = (unsigned char *)val->mv_data; /* unsigned string */ + unsigned char *end = s + val->mv_size; + /* + * FNV-1a hash each octet of the string + */ + while (s < end) { + /* xor the bottom with the current octet */ + hval ^= (mdb_hash_t)*s++; + + /* multiply by the 64 bit FNV magic prime mod 2^64 */ + hval += (hval << 1) + (hval << 4) + (hval << 5) + + (hval << 7) + (hval << 8) + (hval << 40); + } + /* return our new hash value */ + return hval; +} + +/** Hash the string and output the encoded hash. + * This uses modified RFC1924 Ascii85 encoding to accommodate systems with + * very short name limits. We don't care about the encoding being reversible, + * we just want to preserve as many bits of the input as possible in a + * small printable string. + * @param[in] str string to hash + * @param[out] encbuf an array of 11 chars to hold the hash + */ +static const char mdb_a85[]= "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~"; + +static void ESECT +mdb_pack85(unsigned long l, char *out) +{ + int i; + + for (i=0; i<5; i++) { + *out++ = mdb_a85[l % 85]; + l /= 85; + } +} + +static void ESECT +mdb_hash_enc(MDB_val *val, char *encbuf) +{ + mdb_hash_t h = mdb_hash_val(val, MDB_HASH_INIT); + + mdb_pack85(h, encbuf); + mdb_pack85(h>>32, encbuf+5); + encbuf[10] = '\0'; +} +#endif + +/** Open and/or initialize the lock region for the environment. + * @param[in] env The LMDB environment. + * @param[in] fname Filename + scratch area, from #mdb_fname_init(). + * @param[in] mode The Unix permissions for the file, if we create it. + * @param[in,out] excl In -1, out lock type: -1 none, 0 shared, 1 exclusive + * @return 0 on success, non-zero on failure. + */ +static int ESECT +mdb_env_setup_locks(MDB_env *env, MDB_name *fname, int mode, int *excl) +{ +#ifdef _WIN32 +# define MDB_ERRCODE_ROFS ERROR_WRITE_PROTECT +#else +# define MDB_ERRCODE_ROFS EROFS +#endif + int rc; + off_t size, rsize; + + rc = mdb_fopen(env, fname, MDB_O_LOCKS, mode, &env->me_lfd); + if (rc) { + /* Omit lockfile if read-only env on read-only filesystem */ + if (rc == MDB_ERRCODE_ROFS && (env->me_flags & MDB_RDONLY)) { + return MDB_SUCCESS; + } + goto fail; + } + + if (!(env->me_flags & MDB_NOTLS)) { + rc = pthread_key_create(&env->me_txkey, mdb_env_reader_dest); + if (rc) + goto fail; + env->me_flags |= MDB_ENV_TXKEY; +#ifdef _WIN32 + /* Windows TLS callbacks need help finding their TLS info. */ + if (mdb_tls_nkeys >= MAX_TLS_KEYS) { + rc = MDB_TLS_FULL; + goto fail; + } + mdb_tls_keys[mdb_tls_nkeys++] = env->me_txkey; +#endif + } + + /* Try to get exclusive lock. If we succeed, then + * nobody is using the lock region and we should initialize it. + */ + if ((rc = mdb_env_excl_lock(env, excl))) goto fail; + +#ifdef _WIN32 + size = GetFileSize(env->me_lfd, NULL); +#else + size = lseek(env->me_lfd, 0, SEEK_END); + if (size == -1) goto fail_errno; +#endif + rsize = (env->me_maxreaders-1) * sizeof(MDB_reader) + sizeof(MDB_txninfo); + if (size < rsize && *excl > 0) { +#ifdef _WIN32 + if (SetFilePointer(env->me_lfd, rsize, NULL, FILE_BEGIN) != (DWORD)rsize + || !SetEndOfFile(env->me_lfd)) + goto fail_errno; +#else + if (ftruncate(env->me_lfd, rsize) != 0) goto fail_errno; +#endif + } else { + rsize = size; + size = rsize - sizeof(MDB_txninfo); + env->me_maxreaders = size/sizeof(MDB_reader) + 1; + } + { +#ifdef _WIN32 + HANDLE mh; + mh = CreateFileMapping(env->me_lfd, NULL, PAGE_READWRITE, + 0, 0, NULL); + if (!mh) goto fail_errno; + env->me_txns = MapViewOfFileEx(mh, FILE_MAP_WRITE, 0, 0, rsize, NULL); + CloseHandle(mh); + if (!env->me_txns) goto fail_errno; +#else + void *m = mmap(NULL, rsize, PROT_READ|PROT_WRITE, MAP_SHARED, + env->me_lfd, 0); + if (m == MAP_FAILED) goto fail_errno; + env->me_txns = m; +#endif + } + if (*excl > 0) { +#ifdef _WIN32 + BY_HANDLE_FILE_INFORMATION stbuf; + struct { + DWORD volume; + DWORD nhigh; + DWORD nlow; + } idbuf; + MDB_val val; + char encbuf[11]; + + if (!mdb_sec_inited) { + InitializeSecurityDescriptor(&mdb_null_sd, + SECURITY_DESCRIPTOR_REVISION); + SetSecurityDescriptorDacl(&mdb_null_sd, TRUE, 0, FALSE); + mdb_all_sa.nLength = sizeof(SECURITY_ATTRIBUTES); + mdb_all_sa.bInheritHandle = FALSE; + mdb_all_sa.lpSecurityDescriptor = &mdb_null_sd; + mdb_sec_inited = 1; + } + if (!GetFileInformationByHandle(env->me_lfd, &stbuf)) goto fail_errno; + idbuf.volume = stbuf.dwVolumeSerialNumber; + idbuf.nhigh = stbuf.nFileIndexHigh; + idbuf.nlow = stbuf.nFileIndexLow; + val.mv_data = &idbuf; + val.mv_size = sizeof(idbuf); + mdb_hash_enc(&val, encbuf); + sprintf(env->me_txns->mti_rmname, "Global\\MDBr%s", encbuf); + sprintf(env->me_txns->mti_wmname, "Global\\MDBw%s", encbuf); + env->me_rmutex = CreateMutexA(&mdb_all_sa, FALSE, env->me_txns->mti_rmname); + if (!env->me_rmutex) goto fail_errno; + env->me_wmutex = CreateMutexA(&mdb_all_sa, FALSE, env->me_txns->mti_wmname); + if (!env->me_wmutex) goto fail_errno; +#elif defined(MDB_USE_POSIX_SEM) + struct stat stbuf; + struct { + dev_t dev; + ino_t ino; + } idbuf; + MDB_val val; + char encbuf[11]; + +#if defined(__NetBSD__) +#define MDB_SHORT_SEMNAMES 1 /* limited to 14 chars */ +#endif + if (fstat(env->me_lfd, &stbuf)) goto fail_errno; + idbuf.dev = stbuf.st_dev; + idbuf.ino = stbuf.st_ino; + val.mv_data = &idbuf; + val.mv_size = sizeof(idbuf); + mdb_hash_enc(&val, encbuf); +#ifdef MDB_SHORT_SEMNAMES + encbuf[9] = '\0'; /* drop name from 15 chars to 14 chars */ +#endif + sprintf(env->me_txns->mti_rmname, "/MDBr%s", encbuf); + sprintf(env->me_txns->mti_wmname, "/MDBw%s", encbuf); + /* Clean up after a previous run, if needed: Try to + * remove both semaphores before doing anything else. + */ + sem_unlink(env->me_txns->mti_rmname); + sem_unlink(env->me_txns->mti_wmname); + env->me_rmutex = sem_open(env->me_txns->mti_rmname, + O_CREAT|O_EXCL, mode, 1); + if (env->me_rmutex == SEM_FAILED) goto fail_errno; + env->me_wmutex = sem_open(env->me_txns->mti_wmname, + O_CREAT|O_EXCL, mode, 1); + if (env->me_wmutex == SEM_FAILED) goto fail_errno; +#else /* MDB_USE_POSIX_MUTEX: */ + pthread_mutexattr_t mattr; + + /* Solaris needs this before initing a robust mutex. Otherwise + * it may skip the init and return EBUSY "seems someone already + * inited" or EINVAL "it was inited differently". + */ + memset(env->me_txns->mti_rmutex, 0, sizeof(*env->me_txns->mti_rmutex)); + memset(env->me_txns->mti_wmutex, 0, sizeof(*env->me_txns->mti_wmutex)); + + if ((rc = pthread_mutexattr_init(&mattr))) + goto fail; + + rc = pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED); +#ifdef MDB_ROBUST_SUPPORTED + if (!rc) rc = pthread_mutexattr_setrobust(&mattr, PTHREAD_MUTEX_ROBUST); +#endif + if (!rc) rc = pthread_mutex_init(env->me_txns->mti_rmutex, &mattr); + if (!rc) rc = pthread_mutex_init(env->me_txns->mti_wmutex, &mattr); + pthread_mutexattr_destroy(&mattr); + if (rc) + goto fail; +#endif /* _WIN32 || MDB_USE_POSIX_SEM */ + + env->me_txns->mti_magic = MDB_MAGIC; + env->me_txns->mti_format = MDB_LOCK_FORMAT; + env->me_txns->mti_txnid = 0; + env->me_txns->mti_numreaders = 0; + + } else { + if (env->me_txns->mti_magic != MDB_MAGIC) { + DPUTS("lock region has invalid magic"); + rc = MDB_INVALID; + goto fail; + } + if (env->me_txns->mti_format != MDB_LOCK_FORMAT) { + DPRINTF(("lock region has format+version 0x%x, expected 0x%x", + env->me_txns->mti_format, MDB_LOCK_FORMAT)); + rc = MDB_VERSION_MISMATCH; + goto fail; + } + rc = ErrCode(); + if (rc && rc != EACCES && rc != EAGAIN) { + goto fail; + } +#ifdef _WIN32 + env->me_rmutex = OpenMutexA(SYNCHRONIZE, FALSE, env->me_txns->mti_rmname); + if (!env->me_rmutex) goto fail_errno; + env->me_wmutex = OpenMutexA(SYNCHRONIZE, FALSE, env->me_txns->mti_wmname); + if (!env->me_wmutex) goto fail_errno; +#elif defined(MDB_USE_POSIX_SEM) + env->me_rmutex = sem_open(env->me_txns->mti_rmname, 0); + if (env->me_rmutex == SEM_FAILED) goto fail_errno; + env->me_wmutex = sem_open(env->me_txns->mti_wmname, 0); + if (env->me_wmutex == SEM_FAILED) goto fail_errno; +#endif + } + return MDB_SUCCESS; + +fail_errno: + rc = ErrCode(); +fail: + return rc; +} + + /** Only a subset of the @ref mdb_env flags can be changed + * at runtime. Changing other flags requires closing the + * environment and re-opening it with the new flags. + */ +#define CHANGEABLE (MDB_NOSYNC|MDB_NOMETASYNC|MDB_MAPASYNC|MDB_NOMEMINIT) +#define CHANGELESS (MDB_FIXEDMAP|MDB_NOSUBDIR|MDB_RDONLY| \ + MDB_WRITEMAP|MDB_NOTLS|MDB_NOLOCK|MDB_NORDAHEAD) + +#if VALID_FLAGS & PERSISTENT_FLAGS & (CHANGEABLE|CHANGELESS) +# error "Persistent DB flags & env flags overlap, but both go in mm_flags" +#endif + +int ESECT +mdb_env_open(MDB_env *env, const char *path, unsigned int flags, mdb_mode_t mode) +{ + int rc, excl = -1; + MDB_name fname; + + if (env->me_fd!=INVALID_HANDLE_VALUE || (flags & ~(CHANGEABLE|CHANGELESS))) + return EINVAL; + + flags |= env->me_flags; + + rc = mdb_fname_init(path, flags, &fname); + if (rc) + return rc; + + if (flags & MDB_RDONLY) { + /* silently ignore WRITEMAP when we're only getting read access */ + flags &= ~MDB_WRITEMAP; + } else { + if (!((env->me_free_pgs = mdb_midl_alloc(MDB_IDL_UM_MAX)) && + (env->me_dirty_list = calloc(MDB_IDL_UM_SIZE, sizeof(MDB_ID2))))) + rc = ENOMEM; + } + env->me_flags = flags |= MDB_ENV_ACTIVE; + if (rc) + goto leave; + + env->me_path = strdup(path); + env->me_dbxs = calloc(env->me_maxdbs, sizeof(MDB_dbx)); + env->me_dbflags = calloc(env->me_maxdbs, sizeof(uint16_t)); + env->me_dbiseqs = calloc(env->me_maxdbs, sizeof(unsigned int)); + if (!(env->me_dbxs && env->me_path && env->me_dbflags && env->me_dbiseqs)) { + rc = ENOMEM; + goto leave; + } + env->me_dbxs[FREE_DBI].md_cmp = mdb_cmp_long; /* aligned MDB_INTEGERKEY */ + + /* For RDONLY, get lockfile after we know datafile exists */ + if (!(flags & (MDB_RDONLY|MDB_NOLOCK))) { + rc = mdb_env_setup_locks(env, &fname, mode, &excl); + if (rc) + goto leave; + } + + rc = mdb_fopen(env, &fname, + (flags & MDB_RDONLY) ? MDB_O_RDONLY : MDB_O_RDWR, + mode, &env->me_fd); + if (rc) + goto leave; + + if ((flags & (MDB_RDONLY|MDB_NOLOCK)) == MDB_RDONLY) { + rc = mdb_env_setup_locks(env, &fname, mode, &excl); + if (rc) + goto leave; + } + + if ((rc = mdb_env_open2(env)) == MDB_SUCCESS) { + if (!(flags & (MDB_RDONLY|MDB_WRITEMAP))) { + /* Synchronous fd for meta writes. Needed even with + * MDB_NOSYNC/MDB_NOMETASYNC, in case these get reset. + */ + rc = mdb_fopen(env, &fname, MDB_O_META, mode, &env->me_mfd); + if (rc) + goto leave; + } + DPRINTF(("opened dbenv %p", (void *) env)); + if (excl > 0) { + rc = mdb_env_share_locks(env, &excl); + if (rc) + goto leave; + } + if (!(flags & MDB_RDONLY)) { + MDB_txn *txn; + int tsize = sizeof(MDB_txn), size = tsize + env->me_maxdbs * + (sizeof(MDB_db)+sizeof(MDB_cursor *)+sizeof(unsigned int)+1); + if ((env->me_pbuf = calloc(1, env->me_psize)) && + (txn = calloc(1, size))) + { + txn->mt_dbs = (MDB_db *)((char *)txn + tsize); + txn->mt_cursors = (MDB_cursor **)(txn->mt_dbs + env->me_maxdbs); + txn->mt_dbiseqs = (unsigned int *)(txn->mt_cursors + env->me_maxdbs); + txn->mt_dbflags = (unsigned char *)(txn->mt_dbiseqs + env->me_maxdbs); + txn->mt_env = env; + txn->mt_dbxs = env->me_dbxs; + txn->mt_flags = MDB_TXN_FINISHED; + env->me_txn0 = txn; + } else { + rc = ENOMEM; + } + } + } + +leave: + if (rc) { + mdb_env_close0(env, excl); + } + mdb_fname_destroy(fname); + return rc; +} + +/** Destroy resources from mdb_env_open(), clear our readers & DBIs */ +static void ESECT +mdb_env_close0(MDB_env *env, int excl) +{ + int i; + + if (!(env->me_flags & MDB_ENV_ACTIVE)) + return; + + /* Doing this here since me_dbxs may not exist during mdb_env_close */ + if (env->me_dbxs) { + for (i = env->me_maxdbs; --i >= CORE_DBS; ) + free(env->me_dbxs[i].md_name.mv_data); + free(env->me_dbxs); + } + + free(env->me_pbuf); + free(env->me_dbiseqs); + free(env->me_dbflags); + free(env->me_path); + free(env->me_dirty_list); + free(env->me_txn0); + mdb_midl_free(env->me_free_pgs); + + if (env->me_flags & MDB_ENV_TXKEY) { + pthread_key_delete(env->me_txkey); +#ifdef _WIN32 + /* Delete our key from the global list */ + for (i=0; i<mdb_tls_nkeys; i++) + if (mdb_tls_keys[i] == env->me_txkey) { + mdb_tls_keys[i] = mdb_tls_keys[mdb_tls_nkeys-1]; + mdb_tls_nkeys--; + break; + } +#endif + } + + if (env->me_map) { + munmap(env->me_map, env->me_mapsize); + } + if (env->me_mfd != INVALID_HANDLE_VALUE) + (void) close(env->me_mfd); + if (env->me_fd != INVALID_HANDLE_VALUE) + (void) close(env->me_fd); + if (env->me_txns) { + MDB_PID_T pid = env->me_pid; + /* Clearing readers is done in this function because + * me_txkey with its destructor must be disabled first. + * + * We skip the the reader mutex, so we touch only + * data owned by this process (me_close_readers and + * our readers), and clear each reader atomically. + */ + for (i = env->me_close_readers; --i >= 0; ) + if (env->me_txns->mti_readers[i].mr_pid == pid) + env->me_txns->mti_readers[i].mr_pid = 0; +#ifdef _WIN32 + if (env->me_rmutex) { + CloseHandle(env->me_rmutex); + if (env->me_wmutex) CloseHandle(env->me_wmutex); + } + /* Windows automatically destroys the mutexes when + * the last handle closes. + */ +#elif defined(MDB_USE_POSIX_SEM) + if (env->me_rmutex != SEM_FAILED) { + sem_close(env->me_rmutex); + if (env->me_wmutex != SEM_FAILED) + sem_close(env->me_wmutex); + /* If we have the filelock: If we are the + * only remaining user, clean up semaphores. + */ + if (excl == 0) + mdb_env_excl_lock(env, &excl); + if (excl > 0) { + sem_unlink(env->me_txns->mti_rmname); + sem_unlink(env->me_txns->mti_wmname); + } + } +#endif + munmap((void *)env->me_txns, (env->me_maxreaders-1)*sizeof(MDB_reader)+sizeof(MDB_txninfo)); + } + if (env->me_lfd != INVALID_HANDLE_VALUE) { +#ifdef _WIN32 + if (excl >= 0) { + /* Unlock the lockfile. Windows would have unlocked it + * after closing anyway, but not necessarily at once. + */ + UnlockFile(env->me_lfd, 0, 0, 1, 0); + } +#endif + (void) close(env->me_lfd); + } + + env->me_flags &= ~(MDB_ENV_ACTIVE|MDB_ENV_TXKEY); +} + +void ESECT +mdb_env_close(MDB_env *env) +{ + MDB_page *dp; + + if (env == NULL) + return; + + VGMEMP_DESTROY(env); + while ((dp = env->me_dpages) != NULL) { + VGMEMP_DEFINED(&dp->mp_next, sizeof(dp->mp_next)); + env->me_dpages = dp->mp_next; + free(dp); + } + + mdb_env_close0(env, 0); + free(env); +} + +/** Compare two items pointing at aligned size_t's */ +static int +mdb_cmp_long(const MDB_val *a, const MDB_val *b) +{ + return (*(size_t *)a->mv_data < *(size_t *)b->mv_data) ? -1 : + *(size_t *)a->mv_data > *(size_t *)b->mv_data; +} + +/** Compare two items pointing at aligned unsigned int's. + * + * This is also set as #MDB_INTEGERDUP|#MDB_DUPFIXED's #MDB_dbx.%md_dcmp, + * but #mdb_cmp_clong() is called instead if the data type is size_t. + */ +static int +mdb_cmp_int(const MDB_val *a, const MDB_val *b) +{ + return (*(unsigned int *)a->mv_data < *(unsigned int *)b->mv_data) ? -1 : + *(unsigned int *)a->mv_data > *(unsigned int *)b->mv_data; +} + +/** Compare two items pointing at unsigned ints of unknown alignment. + * Nodes and keys are guaranteed to be 2-byte aligned. + */ +static int +mdb_cmp_cint(const MDB_val *a, const MDB_val *b) +{ +#if BYTE_ORDER == LITTLE_ENDIAN + unsigned short *u, *c; + int x; + + u = (unsigned short *) ((char *) a->mv_data + a->mv_size); + c = (unsigned short *) ((char *) b->mv_data + a->mv_size); + do { + x = *--u - *--c; + } while(!x && u > (unsigned short *)a->mv_data); + return x; +#else + unsigned short *u, *c, *end; + int x; + + end = (unsigned short *) ((char *) a->mv_data + a->mv_size); + u = (unsigned short *)a->mv_data; + c = (unsigned short *)b->mv_data; + do { + x = *u++ - *c++; + } while(!x && u < end); + return x; +#endif +} + +/** Compare two items lexically */ +static int +mdb_cmp_memn(const MDB_val *a, const MDB_val *b) +{ + int diff; + ssize_t len_diff; + unsigned int len; + + len = a->mv_size; + len_diff = (ssize_t) a->mv_size - (ssize_t) b->mv_size; + if (len_diff > 0) { + len = b->mv_size; + len_diff = 1; + } + + diff = memcmp(a->mv_data, b->mv_data, len); + return diff ? diff : len_diff<0 ? -1 : len_diff; +} + +/** Compare two items in reverse byte order */ +static int +mdb_cmp_memnr(const MDB_val *a, const MDB_val *b) +{ + const unsigned char *p1, *p2, *p1_lim; + ssize_t len_diff; + int diff; + + p1_lim = (const unsigned char *)a->mv_data; + p1 = (const unsigned char *)a->mv_data + a->mv_size; + p2 = (const unsigned char *)b->mv_data + b->mv_size; + + len_diff = (ssize_t) a->mv_size - (ssize_t) b->mv_size; + if (len_diff > 0) { + p1_lim += len_diff; + len_diff = 1; + } + + while (p1 > p1_lim) { + diff = *--p1 - *--p2; + if (diff) + return diff; + } + return len_diff<0 ? -1 : len_diff; +} + +/** Search for key within a page, using binary search. + * Returns the smallest entry larger or equal to the key. + * If exactp is non-null, stores whether the found entry was an exact match + * in *exactp (1 or 0). + * Updates the cursor index with the index of the found entry. + * If no entry larger or equal to the key is found, returns NULL. + */ +static MDB_node * +mdb_node_search(MDB_cursor *mc, MDB_val *key, int *exactp) +{ + unsigned int i = 0, nkeys; + int low, high; + int rc = 0; + MDB_page *mp = mc->mc_pg[mc->mc_top]; + MDB_node *node = NULL; + MDB_val nodekey; + MDB_cmp_func *cmp; + DKBUF; + + nkeys = NUMKEYS(mp); + + DPRINTF(("searching %u keys in %s %spage %"Z"u", + nkeys, IS_LEAF(mp) ? "leaf" : "branch", IS_SUBP(mp) ? "sub-" : "", + mdb_dbg_pgno(mp))); + + low = IS_LEAF(mp) ? 0 : 1; + high = nkeys - 1; + cmp = mc->mc_dbx->md_cmp; + + /* Branch pages have no data, so if using integer keys, + * alignment is guaranteed. Use faster mdb_cmp_int. + */ + if (cmp == mdb_cmp_cint && IS_BRANCH(mp)) { + if (NODEPTR(mp, 1)->mn_ksize == sizeof(size_t)) + cmp = mdb_cmp_long; + else + cmp = mdb_cmp_int; + } + + if (IS_LEAF2(mp)) { + nodekey.mv_size = mc->mc_db->md_pad; + node = NODEPTR(mp, 0); /* fake */ + while (low <= high) { + i = (low + high) >> 1; + nodekey.mv_data = LEAF2KEY(mp, i, nodekey.mv_size); + rc = cmp(key, &nodekey); + DPRINTF(("found leaf index %u [%s], rc = %i", + i, DKEY(&nodekey), rc)); + if (rc == 0) + break; + if (rc > 0) + low = i + 1; + else + high = i - 1; + } + } else { + while (low <= high) { + i = (low + high) >> 1; + + node = NODEPTR(mp, i); + nodekey.mv_size = NODEKSZ(node); + nodekey.mv_data = NODEKEY(node); + + rc = cmp(key, &nodekey); +#if MDB_DEBUG + if (IS_LEAF(mp)) + DPRINTF(("found leaf index %u [%s], rc = %i", + i, DKEY(&nodekey), rc)); + else + DPRINTF(("found branch index %u [%s -> %"Z"u], rc = %i", + i, DKEY(&nodekey), NODEPGNO(node), rc)); +#endif + if (rc == 0) + break; + if (rc > 0) + low = i + 1; + else + high = i - 1; + } + } + + if (rc > 0) { /* Found entry is less than the key. */ + i++; /* Skip to get the smallest entry larger than key. */ + if (!IS_LEAF2(mp)) + node = NODEPTR(mp, i); + } + if (exactp) + *exactp = (rc == 0 && nkeys > 0); + /* store the key index */ + mc->mc_ki[mc->mc_top] = i; + if (i >= nkeys) + /* There is no entry larger or equal to the key. */ + return NULL; + + /* nodeptr is fake for LEAF2 */ + return node; +} + +#if 0 +static void +mdb_cursor_adjust(MDB_cursor *mc, func) +{ + MDB_cursor *m2; + + for (m2 = mc->mc_txn->mt_cursors[mc->mc_dbi]; m2; m2=m2->mc_next) { + if (m2->mc_pg[m2->mc_top] == mc->mc_pg[mc->mc_top]) { + func(mc, m2); + } + } +} +#endif + +/** Pop a page off the top of the cursor's stack. */ +static void +mdb_cursor_pop(MDB_cursor *mc) +{ + if (mc->mc_snum) { + DPRINTF(("popping page %"Z"u off db %d cursor %p", + mc->mc_pg[mc->mc_top]->mp_pgno, DDBI(mc), (void *) mc)); + + mc->mc_snum--; + if (mc->mc_snum) { + mc->mc_top--; + } else { + mc->mc_flags &= ~C_INITIALIZED; + } + } +} + +/** Push a page onto the top of the cursor's stack. + * Set #MDB_TXN_ERROR on failure. + */ +static int +mdb_cursor_push(MDB_cursor *mc, MDB_page *mp) +{ + DPRINTF(("pushing page %"Z"u on db %d cursor %p", mp->mp_pgno, + DDBI(mc), (void *) mc)); + + if (mc->mc_snum >= CURSOR_STACK) { + mc->mc_txn->mt_flags |= MDB_TXN_ERROR; + return MDB_CURSOR_FULL; + } + + mc->mc_top = mc->mc_snum++; + mc->mc_pg[mc->mc_top] = mp; + mc->mc_ki[mc->mc_top] = 0; + + return MDB_SUCCESS; +} + +/** Find the address of the page corresponding to a given page number. + * Set #MDB_TXN_ERROR on failure. + * @param[in] mc the cursor accessing the page. + * @param[in] pgno the page number for the page to retrieve. + * @param[out] ret address of a pointer where the page's address will be stored. + * @param[out] lvl dirty_list inheritance level of found page. 1=current txn, 0=mapped page. + * @return 0 on success, non-zero on failure. + */ +static int +mdb_page_get(MDB_cursor *mc, pgno_t pgno, MDB_page **ret, int *lvl) +{ + MDB_txn *txn = mc->mc_txn; + MDB_env *env = txn->mt_env; + MDB_page *p = NULL; + int level; + + if (! (txn->mt_flags & (MDB_TXN_RDONLY|MDB_TXN_WRITEMAP))) { + MDB_txn *tx2 = txn; + level = 1; + do { + MDB_ID2L dl = tx2->mt_u.dirty_list; + unsigned x; + /* Spilled pages were dirtied in this txn and flushed + * because the dirty list got full. Bring this page + * back in from the map (but don't unspill it here, + * leave that unless page_touch happens again). + */ + if (tx2->mt_spill_pgs) { + MDB_ID pn = pgno << 1; + x = mdb_midl_search(tx2->mt_spill_pgs, pn); + if (x <= tx2->mt_spill_pgs[0] && tx2->mt_spill_pgs[x] == pn) { + p = (MDB_page *)(env->me_map + env->me_psize * pgno); + goto done; + } + } + if (dl[0].mid) { + unsigned x = mdb_mid2l_search(dl, pgno); + if (x <= dl[0].mid && dl[x].mid == pgno) { + p = dl[x].mptr; + goto done; + } + } + level++; + } while ((tx2 = tx2->mt_parent) != NULL); + } + + if (pgno < txn->mt_next_pgno) { + level = 0; + p = (MDB_page *)(env->me_map + env->me_psize * pgno); + } else { + DPRINTF(("page %"Z"u not found", pgno)); + txn->mt_flags |= MDB_TXN_ERROR; + return MDB_PAGE_NOTFOUND; + } + +done: + *ret = p; + if (lvl) + *lvl = level; + return MDB_SUCCESS; +} + +/** Finish #mdb_page_search() / #mdb_page_search_lowest(). + * The cursor is at the root page, set up the rest of it. + */ +static int +mdb_page_search_root(MDB_cursor *mc, MDB_val *key, int flags) +{ + MDB_page *mp = mc->mc_pg[mc->mc_top]; + int rc; + DKBUF; + + while (IS_BRANCH(mp)) { + MDB_node *node; + indx_t i; + + DPRINTF(("branch page %"Z"u has %u keys", mp->mp_pgno, NUMKEYS(mp))); + /* Don't assert on branch pages in the FreeDB. We can get here + * while in the process of rebalancing a FreeDB branch page; we must + * let that proceed. ITS#8336 + */ + mdb_cassert(mc, !mc->mc_dbi || NUMKEYS(mp) > 1); + DPRINTF(("found index 0 to page %"Z"u", NODEPGNO(NODEPTR(mp, 0)))); + + if (flags & (MDB_PS_FIRST|MDB_PS_LAST)) { + i = 0; + if (flags & MDB_PS_LAST) { + i = NUMKEYS(mp) - 1; + /* if already init'd, see if we're already in right place */ + if (mc->mc_flags & C_INITIALIZED) { + if (mc->mc_ki[mc->mc_top] == i) { + mc->mc_top = mc->mc_snum++; + mp = mc->mc_pg[mc->mc_top]; + goto ready; + } + } + } + } else { + int exact; + node = mdb_node_search(mc, key, &exact); + if (node == NULL) + i = NUMKEYS(mp) - 1; + else { + i = mc->mc_ki[mc->mc_top]; + if (!exact) { + mdb_cassert(mc, i > 0); + i--; + } + } + DPRINTF(("following index %u for key [%s]", i, DKEY(key))); + } + + mdb_cassert(mc, i < NUMKEYS(mp)); + node = NODEPTR(mp, i); + + if ((rc = mdb_page_get(mc, NODEPGNO(node), &mp, NULL)) != 0) + return rc; + + mc->mc_ki[mc->mc_top] = i; + if ((rc = mdb_cursor_push(mc, mp))) + return rc; + +ready: + if (flags & MDB_PS_MODIFY) { + if ((rc = mdb_page_touch(mc)) != 0) + return rc; + mp = mc->mc_pg[mc->mc_top]; + } + } + + if (!IS_LEAF(mp)) { + DPRINTF(("internal error, index points to a %02X page!?", + mp->mp_flags)); + mc->mc_txn->mt_flags |= MDB_TXN_ERROR; + return MDB_CORRUPTED; + } + + DPRINTF(("found leaf page %"Z"u for key [%s]", mp->mp_pgno, + key ? DKEY(key) : "null")); + mc->mc_flags |= C_INITIALIZED; + mc->mc_flags &= ~C_EOF; + + return MDB_SUCCESS; +} + +/** Search for the lowest key under the current branch page. + * This just bypasses a NUMKEYS check in the current page + * before calling mdb_page_search_root(), because the callers + * are all in situations where the current page is known to + * be underfilled. + */ +static int +mdb_page_search_lowest(MDB_cursor *mc) +{ + MDB_page *mp = mc->mc_pg[mc->mc_top]; + MDB_node *node = NODEPTR(mp, 0); + int rc; + + if ((rc = mdb_page_get(mc, NODEPGNO(node), &mp, NULL)) != 0) + return rc; + + mc->mc_ki[mc->mc_top] = 0; + if ((rc = mdb_cursor_push(mc, mp))) + return rc; + return mdb_page_search_root(mc, NULL, MDB_PS_FIRST); +} + +/** Search for the page a given key should be in. + * Push it and its parent pages on the cursor stack. + * @param[in,out] mc the cursor for this operation. + * @param[in] key the key to search for, or NULL for first/last page. + * @param[in] flags If MDB_PS_MODIFY is set, visited pages in the DB + * are touched (updated with new page numbers). + * If MDB_PS_FIRST or MDB_PS_LAST is set, find first or last leaf. + * This is used by #mdb_cursor_first() and #mdb_cursor_last(). + * If MDB_PS_ROOTONLY set, just fetch root node, no further lookups. + * @return 0 on success, non-zero on failure. + */ +static int +mdb_page_search(MDB_cursor *mc, MDB_val *key, int flags) +{ + int rc; + pgno_t root; + + /* Make sure the txn is still viable, then find the root from + * the txn's db table and set it as the root of the cursor's stack. + */ + if (mc->mc_txn->mt_flags & MDB_TXN_BLOCKED) { + DPUTS("transaction may not be used now"); + return MDB_BAD_TXN; + } else { + /* Make sure we're using an up-to-date root */ + if (*mc->mc_dbflag & DB_STALE) { + MDB_cursor mc2; + if (TXN_DBI_CHANGED(mc->mc_txn, mc->mc_dbi)) + return MDB_BAD_DBI; + mdb_cursor_init(&mc2, mc->mc_txn, MAIN_DBI, NULL); + rc = mdb_page_search(&mc2, &mc->mc_dbx->md_name, 0); + if (rc) + return rc; + { + MDB_val data; + int exact = 0; + uint16_t flags; + MDB_node *leaf = mdb_node_search(&mc2, + &mc->mc_dbx->md_name, &exact); + if (!exact) + return MDB_NOTFOUND; + if ((leaf->mn_flags & (F_DUPDATA|F_SUBDATA)) != F_SUBDATA) + return MDB_INCOMPATIBLE; /* not a named DB */ + rc = mdb_node_read(&mc2, leaf, &data); + if (rc) + return rc; + memcpy(&flags, ((char *) data.mv_data + offsetof(MDB_db, md_flags)), + sizeof(uint16_t)); + /* The txn may not know this DBI, or another process may + * have dropped and recreated the DB with other flags. + */ + if ((mc->mc_db->md_flags & PERSISTENT_FLAGS) != flags) + return MDB_INCOMPATIBLE; + memcpy(mc->mc_db, data.mv_data, sizeof(MDB_db)); + } + *mc->mc_dbflag &= ~DB_STALE; + } + root = mc->mc_db->md_root; + + if (root == P_INVALID) { /* Tree is empty. */ + DPUTS("tree is empty"); + return MDB_NOTFOUND; + } + } + + mdb_cassert(mc, root > 1); + if (!mc->mc_pg[0] || mc->mc_pg[0]->mp_pgno != root) + if ((rc = mdb_page_get(mc, root, &mc->mc_pg[0], NULL)) != 0) + return rc; + + mc->mc_snum = 1; + mc->mc_top = 0; + + DPRINTF(("db %d root page %"Z"u has flags 0x%X", + DDBI(mc), root, mc->mc_pg[0]->mp_flags)); + + if (flags & MDB_PS_MODIFY) { + if ((rc = mdb_page_touch(mc))) + return rc; + } + + if (flags & MDB_PS_ROOTONLY) + return MDB_SUCCESS; + + return mdb_page_search_root(mc, key, flags); +} + +static int +mdb_ovpage_free(MDB_cursor *mc, MDB_page *mp) +{ + MDB_txn *txn = mc->mc_txn; + pgno_t pg = mp->mp_pgno; + unsigned x = 0, ovpages = mp->mp_pages; + MDB_env *env = txn->mt_env; + MDB_IDL sl = txn->mt_spill_pgs; + MDB_ID pn = pg << 1; + int rc; + + DPRINTF(("free ov page %"Z"u (%d)", pg, ovpages)); + /* If the page is dirty or on the spill list we just acquired it, + * so we should give it back to our current free list, if any. + * Otherwise put it onto the list of pages we freed in this txn. + * + * Won't create me_pghead: me_pglast must be inited along with it. + * Unsupported in nested txns: They would need to hide the page + * range in ancestor txns' dirty and spilled lists. + */ + if (env->me_pghead && + !txn->mt_parent && + ((mp->mp_flags & P_DIRTY) || + (sl && (x = mdb_midl_search(sl, pn)) <= sl[0] && sl[x] == pn))) + { + unsigned i, j; + pgno_t *mop; + MDB_ID2 *dl, ix, iy; + rc = mdb_midl_need(&env->me_pghead, ovpages); + if (rc) + return rc; + if (!(mp->mp_flags & P_DIRTY)) { + /* This page is no longer spilled */ + if (x == sl[0]) + sl[0]--; + else + sl[x] |= 1; + goto release; + } + /* Remove from dirty list */ + dl = txn->mt_u.dirty_list; + x = dl[0].mid--; + for (ix = dl[x]; ix.mptr != mp; ix = iy) { + if (x > 1) { + x--; + iy = dl[x]; + dl[x] = ix; + } else { + mdb_cassert(mc, x > 1); + j = ++(dl[0].mid); + dl[j] = ix; /* Unsorted. OK when MDB_TXN_ERROR. */ + txn->mt_flags |= MDB_TXN_ERROR; + return MDB_CORRUPTED; + } + } + txn->mt_dirty_room++; + if (!(env->me_flags & MDB_WRITEMAP)) + mdb_dpage_free(env, mp); +release: + /* Insert in me_pghead */ + mop = env->me_pghead; + j = mop[0] + ovpages; + for (i = mop[0]; i && mop[i] < pg; i--) + mop[j--] = mop[i]; + while (j>i) + mop[j--] = pg++; + mop[0] += ovpages; + } else { + rc = mdb_midl_append_range(&txn->mt_free_pgs, pg, ovpages); + if (rc) + return rc; + } + mc->mc_db->md_overflow_pages -= ovpages; + return 0; +} + +/** Return the data associated with a given node. + * @param[in] mc The cursor for this operation. + * @param[in] leaf The node being read. + * @param[out] data Updated to point to the node's data. + * @return 0 on success, non-zero on failure. + */ +static int +mdb_node_read(MDB_cursor *mc, MDB_node *leaf, MDB_val *data) +{ + MDB_page *omp; /* overflow page */ + pgno_t pgno; + int rc; + + if (!F_ISSET(leaf->mn_flags, F_BIGDATA)) { + data->mv_size = NODEDSZ(leaf); + data->mv_data = NODEDATA(leaf); + return MDB_SUCCESS; + } + + /* Read overflow data. + */ + data->mv_size = NODEDSZ(leaf); + memcpy(&pgno, NODEDATA(leaf), sizeof(pgno)); + if ((rc = mdb_page_get(mc, pgno, &omp, NULL)) != 0) { + DPRINTF(("read overflow page %"Z"u failed", pgno)); + return rc; + } + data->mv_data = METADATA(omp); + + return MDB_SUCCESS; +} + +int +mdb_get(MDB_txn *txn, MDB_dbi dbi, + MDB_val *key, MDB_val *data) +{ + MDB_cursor mc; + MDB_xcursor mx; + int exact = 0; + DKBUF; + + DPRINTF(("===> get db %u key [%s]", dbi, DKEY(key))); + + if (!key || !data || !TXN_DBI_EXIST(txn, dbi, DB_USRVALID)) + return EINVAL; + + if (txn->mt_flags & MDB_TXN_BLOCKED) + return MDB_BAD_TXN; + + mdb_cursor_init(&mc, txn, dbi, &mx); + return mdb_cursor_set(&mc, key, data, MDB_SET, &exact); +} + +/** Find a sibling for a page. + * Replaces the page at the top of the cursor's stack with the + * specified sibling, if one exists. + * @param[in] mc The cursor for this operation. + * @param[in] move_right Non-zero if the right sibling is requested, + * otherwise the left sibling. + * @return 0 on success, non-zero on failure. + */ +static int +mdb_cursor_sibling(MDB_cursor *mc, int move_right) +{ + int rc; + MDB_node *indx; + MDB_page *mp; + + if (mc->mc_snum < 2) { + return MDB_NOTFOUND; /* root has no siblings */ + } + + mdb_cursor_pop(mc); + DPRINTF(("parent page is page %"Z"u, index %u", + mc->mc_pg[mc->mc_top]->mp_pgno, mc->mc_ki[mc->mc_top])); + + if (move_right ? (mc->mc_ki[mc->mc_top] + 1u >= NUMKEYS(mc->mc_pg[mc->mc_top])) + : (mc->mc_ki[mc->mc_top] == 0)) { + DPRINTF(("no more keys left, moving to %s sibling", + move_right ? "right" : "left")); + if ((rc = mdb_cursor_sibling(mc, move_right)) != MDB_SUCCESS) { + /* undo cursor_pop before returning */ + mc->mc_top++; + mc->mc_snum++; + return rc; + } + } else { + if (move_right) + mc->mc_ki[mc->mc_top]++; + else + mc->mc_ki[mc->mc_top]--; + DPRINTF(("just moving to %s index key %u", + move_right ? "right" : "left", mc->mc_ki[mc->mc_top])); + } + mdb_cassert(mc, IS_BRANCH(mc->mc_pg[mc->mc_top])); + + indx = NODEPTR(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]); + if ((rc = mdb_page_get(mc, NODEPGNO(indx), &mp, NULL)) != 0) { + /* mc will be inconsistent if caller does mc_snum++ as above */ + mc->mc_flags &= ~(C_INITIALIZED|C_EOF); + return rc; + } + + mdb_cursor_push(mc, mp); + if (!move_right) + mc->mc_ki[mc->mc_top] = NUMKEYS(mp)-1; + + return MDB_SUCCESS; +} + +/** Move the cursor to the next data item. */ +static int +mdb_cursor_next(MDB_cursor *mc, MDB_val *key, MDB_val *data, MDB_cursor_op op) +{ + MDB_page *mp; + MDB_node *leaf; + int rc; + + if ((mc->mc_flags & C_DEL && op == MDB_NEXT_DUP)) + return MDB_NOTFOUND; + + if (!(mc->mc_flags & C_INITIALIZED)) + return mdb_cursor_first(mc, key, data); + + mp = mc->mc_pg[mc->mc_top]; + + if (mc->mc_flags & C_EOF) { + if (mc->mc_ki[mc->mc_top] >= NUMKEYS(mp)-1) + return MDB_NOTFOUND; + mc->mc_flags ^= C_EOF; + } + + if (mc->mc_db->md_flags & MDB_DUPSORT) { + leaf = NODEPTR(mp, mc->mc_ki[mc->mc_top]); + if (F_ISSET(leaf->mn_flags, F_DUPDATA)) { + if (op == MDB_NEXT || op == MDB_NEXT_DUP) { + rc = mdb_cursor_next(&mc->mc_xcursor->mx_cursor, data, NULL, MDB_NEXT); + if (op != MDB_NEXT || rc != MDB_NOTFOUND) { + if (rc == MDB_SUCCESS) + MDB_GET_KEY(leaf, key); + return rc; + } + } + } else { + mc->mc_xcursor->mx_cursor.mc_flags &= ~(C_INITIALIZED|C_EOF); + if (op == MDB_NEXT_DUP) + return MDB_NOTFOUND; + } + } + + DPRINTF(("cursor_next: top page is %"Z"u in cursor %p", + mdb_dbg_pgno(mp), (void *) mc)); + if (mc->mc_flags & C_DEL) { + mc->mc_flags ^= C_DEL; + goto skip; + } + + if (mc->mc_ki[mc->mc_top] + 1u >= NUMKEYS(mp)) { + DPUTS("=====> move to next sibling page"); + if ((rc = mdb_cursor_sibling(mc, 1)) != MDB_SUCCESS) { + mc->mc_flags |= C_EOF; + return rc; + } + mp = mc->mc_pg[mc->mc_top]; + DPRINTF(("next page is %"Z"u, key index %u", mp->mp_pgno, mc->mc_ki[mc->mc_top])); + } else + mc->mc_ki[mc->mc_top]++; + +skip: + DPRINTF(("==> cursor points to page %"Z"u with %u keys, key index %u", + mdb_dbg_pgno(mp), NUMKEYS(mp), mc->mc_ki[mc->mc_top])); + + if (IS_LEAF2(mp)) { + key->mv_size = mc->mc_db->md_pad; + key->mv_data = LEAF2KEY(mp, mc->mc_ki[mc->mc_top], key->mv_size); + return MDB_SUCCESS; + } + + mdb_cassert(mc, IS_LEAF(mp)); + leaf = NODEPTR(mp, mc->mc_ki[mc->mc_top]); + + if (F_ISSET(leaf->mn_flags, F_DUPDATA)) { + mdb_xcursor_init1(mc, leaf); + } + if (data) { + if ((rc = mdb_node_read(mc, leaf, data)) != MDB_SUCCESS) + return rc; + + if (F_ISSET(leaf->mn_flags, F_DUPDATA)) { + rc = mdb_cursor_first(&mc->mc_xcursor->mx_cursor, data, NULL); + if (rc != MDB_SUCCESS) + return rc; + } + } + + MDB_GET_KEY(leaf, key); + return MDB_SUCCESS; +} + +/** Move the cursor to the previous data item. */ +static int +mdb_cursor_prev(MDB_cursor *mc, MDB_val *key, MDB_val *data, MDB_cursor_op op) +{ + MDB_page *mp; + MDB_node *leaf; + int rc; + + if (!(mc->mc_flags & C_INITIALIZED)) { + rc = mdb_cursor_last(mc, key, data); + if (rc) + return rc; + mc->mc_ki[mc->mc_top]++; + } + + mp = mc->mc_pg[mc->mc_top]; + + if (mc->mc_db->md_flags & MDB_DUPSORT) { + leaf = NODEPTR(mp, mc->mc_ki[mc->mc_top]); + if (F_ISSET(leaf->mn_flags, F_DUPDATA)) { + if (op == MDB_PREV || op == MDB_PREV_DUP) { + rc = mdb_cursor_prev(&mc->mc_xcursor->mx_cursor, data, NULL, MDB_PREV); + if (op != MDB_PREV || rc != MDB_NOTFOUND) { + if (rc == MDB_SUCCESS) { + MDB_GET_KEY(leaf, key); + mc->mc_flags &= ~C_EOF; + } + return rc; + } + } + } else { + mc->mc_xcursor->mx_cursor.mc_flags &= ~(C_INITIALIZED|C_EOF); + if (op == MDB_PREV_DUP) + return MDB_NOTFOUND; + } + } + + DPRINTF(("cursor_prev: top page is %"Z"u in cursor %p", + mdb_dbg_pgno(mp), (void *) mc)); + + mc->mc_flags &= ~(C_EOF|C_DEL); + + if (mc->mc_ki[mc->mc_top] == 0) { + DPUTS("=====> move to prev sibling page"); + if ((rc = mdb_cursor_sibling(mc, 0)) != MDB_SUCCESS) { + return rc; + } + mp = mc->mc_pg[mc->mc_top]; + mc->mc_ki[mc->mc_top] = NUMKEYS(mp) - 1; + DPRINTF(("prev page is %"Z"u, key index %u", mp->mp_pgno, mc->mc_ki[mc->mc_top])); + } else + mc->mc_ki[mc->mc_top]--; + + DPRINTF(("==> cursor points to page %"Z"u with %u keys, key index %u", + mdb_dbg_pgno(mp), NUMKEYS(mp), mc->mc_ki[mc->mc_top])); + + if (IS_LEAF2(mp)) { + key->mv_size = mc->mc_db->md_pad; + key->mv_data = LEAF2KEY(mp, mc->mc_ki[mc->mc_top], key->mv_size); + return MDB_SUCCESS; + } + + mdb_cassert(mc, IS_LEAF(mp)); + leaf = NODEPTR(mp, mc->mc_ki[mc->mc_top]); + + if (F_ISSET(leaf->mn_flags, F_DUPDATA)) { + mdb_xcursor_init1(mc, leaf); + } + if (data) { + if ((rc = mdb_node_read(mc, leaf, data)) != MDB_SUCCESS) + return rc; + + if (F_ISSET(leaf->mn_flags, F_DUPDATA)) { + rc = mdb_cursor_last(&mc->mc_xcursor->mx_cursor, data, NULL); + if (rc != MDB_SUCCESS) + return rc; + } + } + + MDB_GET_KEY(leaf, key); + return MDB_SUCCESS; +} + +/** Set the cursor on a specific data item. */ +static int +mdb_cursor_set(MDB_cursor *mc, MDB_val *key, MDB_val *data, + MDB_cursor_op op, int *exactp) +{ + int rc; + MDB_page *mp; + MDB_node *leaf = NULL; + DKBUF; + + if (key->mv_size == 0) + return MDB_BAD_VALSIZE; + + if (mc->mc_xcursor) + mc->mc_xcursor->mx_cursor.mc_flags &= ~(C_INITIALIZED|C_EOF); + + /* See if we're already on the right page */ + if (mc->mc_flags & C_INITIALIZED) { + MDB_val nodekey; + + mp = mc->mc_pg[mc->mc_top]; + if (!NUMKEYS(mp)) { + mc->mc_ki[mc->mc_top] = 0; + return MDB_NOTFOUND; + } + if (mp->mp_flags & P_LEAF2) { + nodekey.mv_size = mc->mc_db->md_pad; + nodekey.mv_data = LEAF2KEY(mp, 0, nodekey.mv_size); + } else { + leaf = NODEPTR(mp, 0); + MDB_GET_KEY2(leaf, nodekey); + } + rc = mc->mc_dbx->md_cmp(key, &nodekey); + if (rc == 0) { + /* Probably happens rarely, but first node on the page + * was the one we wanted. + */ + mc->mc_ki[mc->mc_top] = 0; + if (exactp) + *exactp = 1; + goto set1; + } + if (rc > 0) { + unsigned int i; + unsigned int nkeys = NUMKEYS(mp); + if (nkeys > 1) { + if (mp->mp_flags & P_LEAF2) { + nodekey.mv_data = LEAF2KEY(mp, + nkeys-1, nodekey.mv_size); + } else { + leaf = NODEPTR(mp, nkeys-1); + MDB_GET_KEY2(leaf, nodekey); + } + rc = mc->mc_dbx->md_cmp(key, &nodekey); + if (rc == 0) { + /* last node was the one we wanted */ + mc->mc_ki[mc->mc_top] = nkeys-1; + if (exactp) + *exactp = 1; + goto set1; + } + if (rc < 0) { + if (mc->mc_ki[mc->mc_top] < NUMKEYS(mp)) { + /* This is definitely the right page, skip search_page */ + if (mp->mp_flags & P_LEAF2) { + nodekey.mv_data = LEAF2KEY(mp, + mc->mc_ki[mc->mc_top], nodekey.mv_size); + } else { + leaf = NODEPTR(mp, mc->mc_ki[mc->mc_top]); + MDB_GET_KEY2(leaf, nodekey); + } + rc = mc->mc_dbx->md_cmp(key, &nodekey); + if (rc == 0) { + /* current node was the one we wanted */ + if (exactp) + *exactp = 1; + goto set1; + } + } + rc = 0; + mc->mc_flags &= ~C_EOF; + goto set2; + } + } + /* If any parents have right-sibs, search. + * Otherwise, there's nothing further. + */ + for (i=0; i<mc->mc_top; i++) + if (mc->mc_ki[i] < + NUMKEYS(mc->mc_pg[i])-1) + break; + if (i == mc->mc_top) { + /* There are no other pages */ + mc->mc_ki[mc->mc_top] = nkeys; + return MDB_NOTFOUND; + } + } + if (!mc->mc_top) { + /* There are no other pages */ + mc->mc_ki[mc->mc_top] = 0; + if (op == MDB_SET_RANGE && !exactp) { + rc = 0; + goto set1; + } else + return MDB_NOTFOUND; + } + } else { + mc->mc_pg[0] = 0; + } + + rc = mdb_page_search(mc, key, 0); + if (rc != MDB_SUCCESS) + return rc; + + mp = mc->mc_pg[mc->mc_top]; + mdb_cassert(mc, IS_LEAF(mp)); + +set2: + leaf = mdb_node_search(mc, key, exactp); + if (exactp != NULL && !*exactp) { + /* MDB_SET specified and not an exact match. */ + return MDB_NOTFOUND; + } + + if (leaf == NULL) { + DPUTS("===> inexact leaf not found, goto sibling"); + if ((rc = mdb_cursor_sibling(mc, 1)) != MDB_SUCCESS) { + mc->mc_flags |= C_EOF; + return rc; /* no entries matched */ + } + mp = mc->mc_pg[mc->mc_top]; + mdb_cassert(mc, IS_LEAF(mp)); + leaf = NODEPTR(mp, 0); + } + +set1: + mc->mc_flags |= C_INITIALIZED; + mc->mc_flags &= ~C_EOF; + + if (IS_LEAF2(mp)) { + if (op == MDB_SET_RANGE || op == MDB_SET_KEY) { + key->mv_size = mc->mc_db->md_pad; + key->mv_data = LEAF2KEY(mp, mc->mc_ki[mc->mc_top], key->mv_size); + } + return MDB_SUCCESS; + } + + if (F_ISSET(leaf->mn_flags, F_DUPDATA)) { + mdb_xcursor_init1(mc, leaf); + } + if (data) { + if (F_ISSET(leaf->mn_flags, F_DUPDATA)) { + if (op == MDB_SET || op == MDB_SET_KEY || op == MDB_SET_RANGE) { + rc = mdb_cursor_first(&mc->mc_xcursor->mx_cursor, data, NULL); + } else { + int ex2, *ex2p; + if (op == MDB_GET_BOTH) { + ex2p = &ex2; + ex2 = 0; + } else { + ex2p = NULL; + } + rc = mdb_cursor_set(&mc->mc_xcursor->mx_cursor, data, NULL, MDB_SET_RANGE, ex2p); + if (rc != MDB_SUCCESS) + return rc; + } + } else if (op == MDB_GET_BOTH || op == MDB_GET_BOTH_RANGE) { + MDB_val olddata; + MDB_cmp_func *dcmp; + if ((rc = mdb_node_read(mc, leaf, &olddata)) != MDB_SUCCESS) + return rc; + dcmp = mc->mc_dbx->md_dcmp; +#if UINT_MAX < SIZE_MAX + if (dcmp == mdb_cmp_int && olddata.mv_size == sizeof(size_t)) + dcmp = mdb_cmp_clong; +#endif + rc = dcmp(data, &olddata); + if (rc) { + if (op == MDB_GET_BOTH || rc > 0) + return MDB_NOTFOUND; + rc = 0; + } + *data = olddata; + + } else { + if (mc->mc_xcursor) + mc->mc_xcursor->mx_cursor.mc_flags &= ~(C_INITIALIZED|C_EOF); + if ((rc = mdb_node_read(mc, leaf, data)) != MDB_SUCCESS) + return rc; + } + } + + /* The key already matches in all other cases */ + if (op == MDB_SET_RANGE || op == MDB_SET_KEY) + MDB_GET_KEY(leaf, key); + DPRINTF(("==> cursor placed on key [%s]", DKEY(key))); + + return rc; +} + +/** Move the cursor to the first item in the database. */ +static int +mdb_cursor_first(MDB_cursor *mc, MDB_val *key, MDB_val *data) +{ + int rc; + MDB_node *leaf; + + if (mc->mc_xcursor) + mc->mc_xcursor->mx_cursor.mc_flags &= ~(C_INITIALIZED|C_EOF); + + if (!(mc->mc_flags & C_INITIALIZED) || mc->mc_top) { + rc = mdb_page_search(mc, NULL, MDB_PS_FIRST); + if (rc != MDB_SUCCESS) + return rc; + } + mdb_cassert(mc, IS_LEAF(mc->mc_pg[mc->mc_top])); + + leaf = NODEPTR(mc->mc_pg[mc->mc_top], 0); + mc->mc_flags |= C_INITIALIZED; + mc->mc_flags &= ~C_EOF; + + mc->mc_ki[mc->mc_top] = 0; + + if (IS_LEAF2(mc->mc_pg[mc->mc_top])) { + key->mv_size = mc->mc_db->md_pad; + key->mv_data = LEAF2KEY(mc->mc_pg[mc->mc_top], 0, key->mv_size); + return MDB_SUCCESS; + } + + if (data) { + if (F_ISSET(leaf->mn_flags, F_DUPDATA)) { + mdb_xcursor_init1(mc, leaf); + rc = mdb_cursor_first(&mc->mc_xcursor->mx_cursor, data, NULL); + if (rc) + return rc; + } else { + if ((rc = mdb_node_read(mc, leaf, data)) != MDB_SUCCESS) + return rc; + } + } + MDB_GET_KEY(leaf, key); + return MDB_SUCCESS; +} + +/** Move the cursor to the last item in the database. */ +static int +mdb_cursor_last(MDB_cursor *mc, MDB_val *key, MDB_val *data) +{ + int rc; + MDB_node *leaf; + + if (mc->mc_xcursor) + mc->mc_xcursor->mx_cursor.mc_flags &= ~(C_INITIALIZED|C_EOF); + + if (!(mc->mc_flags & C_INITIALIZED) || mc->mc_top) { + rc = mdb_page_search(mc, NULL, MDB_PS_LAST); + if (rc != MDB_SUCCESS) + return rc; + } + mdb_cassert(mc, IS_LEAF(mc->mc_pg[mc->mc_top])); + + mc->mc_ki[mc->mc_top] = NUMKEYS(mc->mc_pg[mc->mc_top]) - 1; + mc->mc_flags |= C_INITIALIZED|C_EOF; + leaf = NODEPTR(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]); + + if (IS_LEAF2(mc->mc_pg[mc->mc_top])) { + key->mv_size = mc->mc_db->md_pad; + key->mv_data = LEAF2KEY(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top], key->mv_size); + return MDB_SUCCESS; + } + + if (data) { + if (F_ISSET(leaf->mn_flags, F_DUPDATA)) { + mdb_xcursor_init1(mc, leaf); + rc = mdb_cursor_last(&mc->mc_xcursor->mx_cursor, data, NULL); + if (rc) + return rc; + } else { + if ((rc = mdb_node_read(mc, leaf, data)) != MDB_SUCCESS) + return rc; + } + } + + MDB_GET_KEY(leaf, key); + return MDB_SUCCESS; +} + +int +mdb_cursor_get(MDB_cursor *mc, MDB_val *key, MDB_val *data, + MDB_cursor_op op) +{ + int rc; + int exact = 0; + int (*mfunc)(MDB_cursor *mc, MDB_val *key, MDB_val *data); + + if (mc == NULL) + return EINVAL; + + if (mc->mc_txn->mt_flags & MDB_TXN_BLOCKED) + return MDB_BAD_TXN; + + switch (op) { + case MDB_GET_CURRENT: + if (!(mc->mc_flags & C_INITIALIZED)) { + rc = EINVAL; + } else { + MDB_page *mp = mc->mc_pg[mc->mc_top]; + int nkeys = NUMKEYS(mp); + if (!nkeys || mc->mc_ki[mc->mc_top] >= nkeys) { + mc->mc_ki[mc->mc_top] = nkeys; + rc = MDB_NOTFOUND; + break; + } + rc = MDB_SUCCESS; + if (IS_LEAF2(mp)) { + key->mv_size = mc->mc_db->md_pad; + key->mv_data = LEAF2KEY(mp, mc->mc_ki[mc->mc_top], key->mv_size); + } else { + MDB_node *leaf = NODEPTR(mp, mc->mc_ki[mc->mc_top]); + MDB_GET_KEY(leaf, key); + if (data) { + if (F_ISSET(leaf->mn_flags, F_DUPDATA)) { + rc = mdb_cursor_get(&mc->mc_xcursor->mx_cursor, data, NULL, MDB_GET_CURRENT); + } else { + rc = mdb_node_read(mc, leaf, data); + } + } + } + } + break; + case MDB_GET_BOTH: + case MDB_GET_BOTH_RANGE: + if (data == NULL) { + rc = EINVAL; + break; + } + if (mc->mc_xcursor == NULL) { + rc = MDB_INCOMPATIBLE; + break; + } + /* FALLTHRU */ + case MDB_SET: + case MDB_SET_KEY: + case MDB_SET_RANGE: + if (key == NULL) { + rc = EINVAL; + } else { + rc = mdb_cursor_set(mc, key, data, op, + op == MDB_SET_RANGE ? NULL : &exact); + } + break; + case MDB_GET_MULTIPLE: + if (data == NULL || !(mc->mc_flags & C_INITIALIZED)) { + rc = EINVAL; + break; + } + if (!(mc->mc_db->md_flags & MDB_DUPFIXED)) { + rc = MDB_INCOMPATIBLE; + break; + } + rc = MDB_SUCCESS; + if (!(mc->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED) || + (mc->mc_xcursor->mx_cursor.mc_flags & C_EOF)) + break; + goto fetchm; + case MDB_NEXT_MULTIPLE: + if (data == NULL) { + rc = EINVAL; + break; + } + if (!(mc->mc_db->md_flags & MDB_DUPFIXED)) { + rc = MDB_INCOMPATIBLE; + break; + } + rc = mdb_cursor_next(mc, key, data, MDB_NEXT_DUP); + if (rc == MDB_SUCCESS) { + if (mc->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED) { + MDB_cursor *mx; +fetchm: + mx = &mc->mc_xcursor->mx_cursor; + data->mv_size = NUMKEYS(mx->mc_pg[mx->mc_top]) * + mx->mc_db->md_pad; + data->mv_data = METADATA(mx->mc_pg[mx->mc_top]); + mx->mc_ki[mx->mc_top] = NUMKEYS(mx->mc_pg[mx->mc_top])-1; + } else { + rc = MDB_NOTFOUND; + } + } + break; + case MDB_PREV_MULTIPLE: + if (data == NULL) { + rc = EINVAL; + break; + } + if (!(mc->mc_db->md_flags & MDB_DUPFIXED)) { + rc = MDB_INCOMPATIBLE; + break; + } + if (!(mc->mc_flags & C_INITIALIZED)) + rc = mdb_cursor_last(mc, key, data); + else + rc = MDB_SUCCESS; + if (rc == MDB_SUCCESS) { + MDB_cursor *mx = &mc->mc_xcursor->mx_cursor; + if (mx->mc_flags & C_INITIALIZED) { + rc = mdb_cursor_sibling(mx, 0); + if (rc == MDB_SUCCESS) + goto fetchm; + } else { + rc = MDB_NOTFOUND; + } + } + break; + case MDB_NEXT: + case MDB_NEXT_DUP: + case MDB_NEXT_NODUP: + rc = mdb_cursor_next(mc, key, data, op); + break; + case MDB_PREV: + case MDB_PREV_DUP: + case MDB_PREV_NODUP: + rc = mdb_cursor_prev(mc, key, data, op); + break; + case MDB_FIRST: + rc = mdb_cursor_first(mc, key, data); + break; + case MDB_FIRST_DUP: + mfunc = mdb_cursor_first; + mmove: + if (data == NULL || !(mc->mc_flags & C_INITIALIZED)) { + rc = EINVAL; + break; + } + if (mc->mc_xcursor == NULL) { + rc = MDB_INCOMPATIBLE; + break; + } + if (mc->mc_ki[mc->mc_top] >= NUMKEYS(mc->mc_pg[mc->mc_top])) { + mc->mc_ki[mc->mc_top] = NUMKEYS(mc->mc_pg[mc->mc_top]); + rc = MDB_NOTFOUND; + break; + } + { + MDB_node *leaf = NODEPTR(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]); + if (!F_ISSET(leaf->mn_flags, F_DUPDATA)) { + MDB_GET_KEY(leaf, key); + rc = mdb_node_read(mc, leaf, data); + break; + } + } + if (!(mc->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED)) { + rc = EINVAL; + break; + } + rc = mfunc(&mc->mc_xcursor->mx_cursor, data, NULL); + break; + case MDB_LAST: + rc = mdb_cursor_last(mc, key, data); + break; + case MDB_LAST_DUP: + mfunc = mdb_cursor_last; + goto mmove; + default: + DPRINTF(("unhandled/unimplemented cursor operation %u", op)); + rc = EINVAL; + break; + } + + if (mc->mc_flags & C_DEL) + mc->mc_flags ^= C_DEL; + + return rc; +} + +/** Touch all the pages in the cursor stack. Set mc_top. + * Makes sure all the pages are writable, before attempting a write operation. + * @param[in] mc The cursor to operate on. + */ +static int +mdb_cursor_touch(MDB_cursor *mc) +{ + int rc = MDB_SUCCESS; + + if (mc->mc_dbi >= CORE_DBS && !(*mc->mc_dbflag & (DB_DIRTY|DB_DUPDATA))) { + /* Touch DB record of named DB */ + MDB_cursor mc2; + MDB_xcursor mcx; + if (TXN_DBI_CHANGED(mc->mc_txn, mc->mc_dbi)) + return MDB_BAD_DBI; + mdb_cursor_init(&mc2, mc->mc_txn, MAIN_DBI, &mcx); + rc = mdb_page_search(&mc2, &mc->mc_dbx->md_name, MDB_PS_MODIFY); + if (rc) + return rc; + *mc->mc_dbflag |= DB_DIRTY; + } + mc->mc_top = 0; + if (mc->mc_snum) { + do { + rc = mdb_page_touch(mc); + } while (!rc && ++(mc->mc_top) < mc->mc_snum); + mc->mc_top = mc->mc_snum-1; + } + return rc; +} + +/** Do not spill pages to disk if txn is getting full, may fail instead */ +#define MDB_NOSPILL 0x8000 + +int +mdb_cursor_put(MDB_cursor *mc, MDB_val *key, MDB_val *data, + unsigned int flags) +{ + MDB_env *env; + MDB_node *leaf = NULL; + MDB_page *fp, *mp, *sub_root = NULL; + uint16_t fp_flags; + MDB_val xdata, *rdata, dkey, olddata; + MDB_db dummy; + int do_sub = 0, insert_key, insert_data; + unsigned int mcount = 0, dcount = 0, nospill; + size_t nsize; + int rc, rc2; + unsigned int nflags; + DKBUF; + + if (mc == NULL || key == NULL) + return EINVAL; + + env = mc->mc_txn->mt_env; + + /* Check this first so counter will always be zero on any + * early failures. + */ + if (flags & MDB_MULTIPLE) { + dcount = data[1].mv_size; + data[1].mv_size = 0; + if (!F_ISSET(mc->mc_db->md_flags, MDB_DUPFIXED)) + return MDB_INCOMPATIBLE; + } + + nospill = flags & MDB_NOSPILL; + flags &= ~MDB_NOSPILL; + + if (mc->mc_txn->mt_flags & (MDB_TXN_RDONLY|MDB_TXN_BLOCKED)) + return (mc->mc_txn->mt_flags & MDB_TXN_RDONLY) ? EACCES : MDB_BAD_TXN; + + if (key->mv_size-1 >= ENV_MAXKEY(env)) + return MDB_BAD_VALSIZE; + +#if SIZE_MAX > MAXDATASIZE + if (data->mv_size > ((mc->mc_db->md_flags & MDB_DUPSORT) ? ENV_MAXKEY(env) : MAXDATASIZE)) + return MDB_BAD_VALSIZE; +#else + if ((mc->mc_db->md_flags & MDB_DUPSORT) && data->mv_size > ENV_MAXKEY(env)) + return MDB_BAD_VALSIZE; +#endif + + DPRINTF(("==> put db %d key [%s], size %"Z"u, data size %"Z"u", + DDBI(mc), DKEY(key), key ? key->mv_size : 0, data->mv_size)); + + dkey.mv_size = 0; + + if (flags == MDB_CURRENT) { + if (!(mc->mc_flags & C_INITIALIZED)) + return EINVAL; + rc = MDB_SUCCESS; + } else if (mc->mc_db->md_root == P_INVALID) { + /* new database, cursor has nothing to point to */ + mc->mc_snum = 0; + mc->mc_top = 0; + mc->mc_flags &= ~C_INITIALIZED; + rc = MDB_NO_ROOT; + } else { + int exact = 0; + MDB_val d2; + if (flags & MDB_APPEND) { + MDB_val k2; + rc = mdb_cursor_last(mc, &k2, &d2); + if (rc == 0) { + rc = mc->mc_dbx->md_cmp(key, &k2); + if (rc > 0) { + rc = MDB_NOTFOUND; + mc->mc_ki[mc->mc_top]++; + } else { + /* new key is <= last key */ + rc = MDB_KEYEXIST; + } + } + } else { + rc = mdb_cursor_set(mc, key, &d2, MDB_SET, &exact); + } + if ((flags & MDB_NOOVERWRITE) && rc == 0) { + DPRINTF(("duplicate key [%s]", DKEY(key))); + *data = d2; + return MDB_KEYEXIST; + } + if (rc && rc != MDB_NOTFOUND) + return rc; + } + + if (mc->mc_flags & C_DEL) + mc->mc_flags ^= C_DEL; + + /* Cursor is positioned, check for room in the dirty list */ + if (!nospill) { + if (flags & MDB_MULTIPLE) { + rdata = &xdata; + xdata.mv_size = data->mv_size * dcount; + } else { + rdata = data; + } + if ((rc2 = mdb_page_spill(mc, key, rdata))) + return rc2; + } + + if (rc == MDB_NO_ROOT) { + MDB_page *np; + /* new database, write a root leaf page */ + DPUTS("allocating new root leaf page"); + if ((rc2 = mdb_page_new(mc, P_LEAF, 1, &np))) { + return rc2; + } + mdb_cursor_push(mc, np); + mc->mc_db->md_root = np->mp_pgno; + mc->mc_db->md_depth++; + *mc->mc_dbflag |= DB_DIRTY; + if ((mc->mc_db->md_flags & (MDB_DUPSORT|MDB_DUPFIXED)) + == MDB_DUPFIXED) + np->mp_flags |= P_LEAF2; + mc->mc_flags |= C_INITIALIZED; + } else { + /* make sure all cursor pages are writable */ + rc2 = mdb_cursor_touch(mc); + if (rc2) + return rc2; + } + + insert_key = insert_data = rc; + if (insert_key) { + /* The key does not exist */ + DPRINTF(("inserting key at index %i", mc->mc_ki[mc->mc_top])); + if ((mc->mc_db->md_flags & MDB_DUPSORT) && + LEAFSIZE(key, data) > env->me_nodemax) + { + /* Too big for a node, insert in sub-DB. Set up an empty + * "old sub-page" for prep_subDB to expand to a full page. + */ + fp_flags = P_LEAF|P_DIRTY; + fp = env->me_pbuf; + fp->mp_pad = data->mv_size; /* used if MDB_DUPFIXED */ + fp->mp_lower = fp->mp_upper = (PAGEHDRSZ-PAGEBASE); + olddata.mv_size = PAGEHDRSZ; + goto prep_subDB; + } + } else { + /* there's only a key anyway, so this is a no-op */ + if (IS_LEAF2(mc->mc_pg[mc->mc_top])) { + char *ptr; + unsigned int ksize = mc->mc_db->md_pad; + if (key->mv_size != ksize) + return MDB_BAD_VALSIZE; + ptr = LEAF2KEY(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top], ksize); + memcpy(ptr, key->mv_data, ksize); +fix_parent: + /* if overwriting slot 0 of leaf, need to + * update branch key if there is a parent page + */ + if (mc->mc_top && !mc->mc_ki[mc->mc_top]) { + unsigned short dtop = 1; + mc->mc_top--; + /* slot 0 is always an empty key, find real slot */ + while (mc->mc_top && !mc->mc_ki[mc->mc_top]) { + mc->mc_top--; + dtop++; + } + if (mc->mc_ki[mc->mc_top]) + rc2 = mdb_update_key(mc, key); + else + rc2 = MDB_SUCCESS; + mc->mc_top += dtop; + if (rc2) + return rc2; + } + return MDB_SUCCESS; + } + +more: + leaf = NODEPTR(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]); + olddata.mv_size = NODEDSZ(leaf); + olddata.mv_data = NODEDATA(leaf); + + /* DB has dups? */ + if (F_ISSET(mc->mc_db->md_flags, MDB_DUPSORT)) { + /* Prepare (sub-)page/sub-DB to accept the new item, + * if needed. fp: old sub-page or a header faking + * it. mp: new (sub-)page. offset: growth in page + * size. xdata: node data with new page or DB. + */ + unsigned i, offset = 0; + mp = fp = xdata.mv_data = env->me_pbuf; + mp->mp_pgno = mc->mc_pg[mc->mc_top]->mp_pgno; + + /* Was a single item before, must convert now */ + if (!F_ISSET(leaf->mn_flags, F_DUPDATA)) { + MDB_cmp_func *dcmp; + /* Just overwrite the current item */ + if (flags == MDB_CURRENT) + goto current; + dcmp = mc->mc_dbx->md_dcmp; +#if UINT_MAX < SIZE_MAX + if (dcmp == mdb_cmp_int && olddata.mv_size == sizeof(size_t)) + dcmp = mdb_cmp_clong; +#endif + /* does data match? */ + if (!dcmp(data, &olddata)) { + if (flags & (MDB_NODUPDATA|MDB_APPENDDUP)) + return MDB_KEYEXIST; + /* overwrite it */ + goto current; + } + + /* Back up original data item */ + dkey.mv_size = olddata.mv_size; + dkey.mv_data = memcpy(fp+1, olddata.mv_data, olddata.mv_size); + + /* Make sub-page header for the dup items, with dummy body */ + fp->mp_flags = P_LEAF|P_DIRTY|P_SUBP; + fp->mp_lower = (PAGEHDRSZ-PAGEBASE); + xdata.mv_size = PAGEHDRSZ + dkey.mv_size + data->mv_size; + if (mc->mc_db->md_flags & MDB_DUPFIXED) { + fp->mp_flags |= P_LEAF2; + fp->mp_pad = data->mv_size; + xdata.mv_size += 2 * data->mv_size; /* leave space for 2 more */ + } else { + xdata.mv_size += 2 * (sizeof(indx_t) + NODESIZE) + + (dkey.mv_size & 1) + (data->mv_size & 1); + } + fp->mp_upper = xdata.mv_size - PAGEBASE; + olddata.mv_size = xdata.mv_size; /* pretend olddata is fp */ + } else if (leaf->mn_flags & F_SUBDATA) { + /* Data is on sub-DB, just store it */ + flags |= F_DUPDATA|F_SUBDATA; + goto put_sub; + } else { + /* Data is on sub-page */ + fp = olddata.mv_data; + switch (flags) { + default: + if (!(mc->mc_db->md_flags & MDB_DUPFIXED)) { + offset = EVEN(NODESIZE + sizeof(indx_t) + + data->mv_size); + break; + } + offset = fp->mp_pad; + if (SIZELEFT(fp) < offset) { + offset *= 4; /* space for 4 more */ + break; + } + /* FALLTHRU: Big enough MDB_DUPFIXED sub-page */ + case MDB_CURRENT: + fp->mp_flags |= P_DIRTY; + COPY_PGNO(fp->mp_pgno, mp->mp_pgno); + mc->mc_xcursor->mx_cursor.mc_pg[0] = fp; + flags |= F_DUPDATA; + goto put_sub; + } + xdata.mv_size = olddata.mv_size + offset; + } + + fp_flags = fp->mp_flags; + if (NODESIZE + NODEKSZ(leaf) + xdata.mv_size > env->me_nodemax) { + /* Too big for a sub-page, convert to sub-DB */ + fp_flags &= ~P_SUBP; +prep_subDB: + if (mc->mc_db->md_flags & MDB_DUPFIXED) { + fp_flags |= P_LEAF2; + dummy.md_pad = fp->mp_pad; + dummy.md_flags = MDB_DUPFIXED; + if (mc->mc_db->md_flags & MDB_INTEGERDUP) + dummy.md_flags |= MDB_INTEGERKEY; + } else { + dummy.md_pad = 0; + dummy.md_flags = 0; + } + dummy.md_depth = 1; + dummy.md_branch_pages = 0; + dummy.md_leaf_pages = 1; + dummy.md_overflow_pages = 0; + dummy.md_entries = NUMKEYS(fp); + xdata.mv_size = sizeof(MDB_db); + xdata.mv_data = &dummy; + if ((rc = mdb_page_alloc(mc, 1, &mp))) + return rc; + offset = env->me_psize - olddata.mv_size; + flags |= F_DUPDATA|F_SUBDATA; + dummy.md_root = mp->mp_pgno; + sub_root = mp; + } + if (mp != fp) { + mp->mp_flags = fp_flags | P_DIRTY; + mp->mp_pad = fp->mp_pad; + mp->mp_lower = fp->mp_lower; + mp->mp_upper = fp->mp_upper + offset; + if (fp_flags & P_LEAF2) { + memcpy(METADATA(mp), METADATA(fp), NUMKEYS(fp) * fp->mp_pad); + } else { + memcpy((char *)mp + mp->mp_upper + PAGEBASE, (char *)fp + fp->mp_upper + PAGEBASE, + olddata.mv_size - fp->mp_upper - PAGEBASE); + memcpy((char *)(&mp->mp_ptrs), (char *)(&fp->mp_ptrs), NUMKEYS(fp) * sizeof(mp->mp_ptrs[0])); + for (i=0; i<NUMKEYS(fp); i++) + mp->mp_ptrs[i] += offset; + } + } + + rdata = &xdata; + flags |= F_DUPDATA; + do_sub = 1; + if (!insert_key) + mdb_node_del(mc, 0); + goto new_sub; + } +current: + /* LMDB passes F_SUBDATA in 'flags' to write a DB record */ + if ((leaf->mn_flags ^ flags) & F_SUBDATA) + return MDB_INCOMPATIBLE; + /* overflow page overwrites need special handling */ + if (F_ISSET(leaf->mn_flags, F_BIGDATA)) { + MDB_page *omp; + pgno_t pg; + int level, ovpages, dpages = OVPAGES(data->mv_size, env->me_psize); + + memcpy(&pg, olddata.mv_data, sizeof(pg)); + if ((rc2 = mdb_page_get(mc, pg, &omp, &level)) != 0) + return rc2; + ovpages = omp->mp_pages; + + /* Is the ov page large enough? */ + if (ovpages >= dpages) { + if (!(omp->mp_flags & P_DIRTY) && + (level || (env->me_flags & MDB_WRITEMAP))) + { + rc = mdb_page_unspill(mc->mc_txn, omp, &omp); + if (rc) + return rc; + level = 0; /* dirty in this txn or clean */ + } + /* Is it dirty? */ + if (omp->mp_flags & P_DIRTY) { + /* yes, overwrite it. Note in this case we don't + * bother to try shrinking the page if the new data + * is smaller than the overflow threshold. + */ + if (level > 1) { + /* It is writable only in a parent txn */ + size_t sz = (size_t) env->me_psize * ovpages, off; + MDB_page *np = mdb_page_malloc(mc->mc_txn, ovpages); + MDB_ID2 id2; + if (!np) + return ENOMEM; + id2.mid = pg; + id2.mptr = np; + /* Note - this page is already counted in parent's dirty_room */ + rc2 = mdb_mid2l_insert(mc->mc_txn->mt_u.dirty_list, &id2); + mdb_cassert(mc, rc2 == 0); + /* Currently we make the page look as with put() in the + * parent txn, in case the user peeks at MDB_RESERVEd + * or unused parts. Some users treat ovpages specially. + */ + if (!(flags & MDB_RESERVE)) { + /* Skip the part where LMDB will put *data. + * Copy end of page, adjusting alignment so + * compiler may copy words instead of bytes. + */ + off = (PAGEHDRSZ + data->mv_size) & -sizeof(size_t); + memcpy((size_t *)((char *)np + off), + (size_t *)((char *)omp + off), sz - off); + sz = PAGEHDRSZ; + } + memcpy(np, omp, sz); /* Copy beginning of page */ + omp = np; + } + SETDSZ(leaf, data->mv_size); + if (F_ISSET(flags, MDB_RESERVE)) + data->mv_data = METADATA(omp); + else + memcpy(METADATA(omp), data->mv_data, data->mv_size); + return MDB_SUCCESS; + } + } + if ((rc2 = mdb_ovpage_free(mc, omp)) != MDB_SUCCESS) + return rc2; + } else if (data->mv_size == olddata.mv_size) { + /* same size, just replace it. Note that we could + * also reuse this node if the new data is smaller, + * but instead we opt to shrink the node in that case. + */ + if (F_ISSET(flags, MDB_RESERVE)) + data->mv_data = olddata.mv_data; + else if (!(mc->mc_flags & C_SUB)) + memcpy(olddata.mv_data, data->mv_data, data->mv_size); + else { + memcpy(NODEKEY(leaf), key->mv_data, key->mv_size); + goto fix_parent; + } + return MDB_SUCCESS; + } + mdb_node_del(mc, 0); + } + + rdata = data; + +new_sub: + nflags = flags & NODE_ADD_FLAGS; + nsize = IS_LEAF2(mc->mc_pg[mc->mc_top]) ? key->mv_size : mdb_leaf_size(env, key, rdata); + if (SIZELEFT(mc->mc_pg[mc->mc_top]) < nsize) { + if (( flags & (F_DUPDATA|F_SUBDATA)) == F_DUPDATA ) + nflags &= ~MDB_APPEND; /* sub-page may need room to grow */ + if (!insert_key) + nflags |= MDB_SPLIT_REPLACE; + rc = mdb_page_split(mc, key, rdata, P_INVALID, nflags); + } else { + /* There is room already in this leaf page. */ + rc = mdb_node_add(mc, mc->mc_ki[mc->mc_top], key, rdata, 0, nflags); + if (rc == 0) { + /* Adjust other cursors pointing to mp */ + MDB_cursor *m2, *m3; + MDB_dbi dbi = mc->mc_dbi; + unsigned i = mc->mc_top; + MDB_page *mp = mc->mc_pg[i]; + + for (m2 = mc->mc_txn->mt_cursors[dbi]; m2; m2=m2->mc_next) { + if (mc->mc_flags & C_SUB) + m3 = &m2->mc_xcursor->mx_cursor; + else + m3 = m2; + if (m3 == mc || m3->mc_snum < mc->mc_snum || m3->mc_pg[i] != mp) continue; + if (m3->mc_ki[i] >= mc->mc_ki[i] && insert_key) { + m3->mc_ki[i]++; + } + XCURSOR_REFRESH(m3, i, mp); + } + } + } + + if (rc == MDB_SUCCESS) { + /* Now store the actual data in the child DB. Note that we're + * storing the user data in the keys field, so there are strict + * size limits on dupdata. The actual data fields of the child + * DB are all zero size. + */ + if (do_sub) { + int xflags, new_dupdata; + size_t ecount; +put_sub: + xdata.mv_size = 0; + xdata.mv_data = ""; + leaf = NODEPTR(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]); + if (flags & MDB_CURRENT) { + xflags = MDB_CURRENT|MDB_NOSPILL; + } else { + mdb_xcursor_init1(mc, leaf); + xflags = (flags & MDB_NODUPDATA) ? + MDB_NOOVERWRITE|MDB_NOSPILL : MDB_NOSPILL; + } + if (sub_root) + mc->mc_xcursor->mx_cursor.mc_pg[0] = sub_root; + new_dupdata = (int)dkey.mv_size; + /* converted, write the original data first */ + if (dkey.mv_size) { + rc = mdb_cursor_put(&mc->mc_xcursor->mx_cursor, &dkey, &xdata, xflags); + if (rc) + goto bad_sub; + /* we've done our job */ + dkey.mv_size = 0; + } + if (!(leaf->mn_flags & F_SUBDATA) || sub_root) { + /* Adjust other cursors pointing to mp */ + MDB_cursor *m2; + MDB_xcursor *mx = mc->mc_xcursor; + unsigned i = mc->mc_top; + MDB_page *mp = mc->mc_pg[i]; + + for (m2 = mc->mc_txn->mt_cursors[mc->mc_dbi]; m2; m2=m2->mc_next) { + if (m2 == mc || m2->mc_snum < mc->mc_snum) continue; + if (!(m2->mc_flags & C_INITIALIZED)) continue; + if (m2->mc_pg[i] == mp) { + if (m2->mc_ki[i] == mc->mc_ki[i]) { + mdb_xcursor_init2(m2, mx, new_dupdata); + } else if (!insert_key) { + XCURSOR_REFRESH(m2, i, mp); + } + } + } + } + ecount = mc->mc_xcursor->mx_db.md_entries; + if (flags & MDB_APPENDDUP) + xflags |= MDB_APPEND; + rc = mdb_cursor_put(&mc->mc_xcursor->mx_cursor, data, &xdata, xflags); + if (flags & F_SUBDATA) { + void *db = NODEDATA(leaf); + memcpy(db, &mc->mc_xcursor->mx_db, sizeof(MDB_db)); + } + insert_data = mc->mc_xcursor->mx_db.md_entries - ecount; + } + /* Increment count unless we just replaced an existing item. */ + if (insert_data) + mc->mc_db->md_entries++; + if (insert_key) { + /* Invalidate txn if we created an empty sub-DB */ + if (rc) + goto bad_sub; + /* If we succeeded and the key didn't exist before, + * make sure the cursor is marked valid. + */ + mc->mc_flags |= C_INITIALIZED; + } + if (flags & MDB_MULTIPLE) { + if (!rc) { + mcount++; + /* let caller know how many succeeded, if any */ + data[1].mv_size = mcount; + if (mcount < dcount) { + data[0].mv_data = (char *)data[0].mv_data + data[0].mv_size; + insert_key = insert_data = 0; + goto more; + } + } + } + return rc; +bad_sub: + if (rc == MDB_KEYEXIST) /* should not happen, we deleted that item */ + rc = MDB_CORRUPTED; + } + mc->mc_txn->mt_flags |= MDB_TXN_ERROR; + return rc; +} + +int +mdb_cursor_del(MDB_cursor *mc, unsigned int flags) +{ + MDB_node *leaf; + MDB_page *mp; + int rc; + + if (mc->mc_txn->mt_flags & (MDB_TXN_RDONLY|MDB_TXN_BLOCKED)) + return (mc->mc_txn->mt_flags & MDB_TXN_RDONLY) ? EACCES : MDB_BAD_TXN; + + if (!(mc->mc_flags & C_INITIALIZED)) + return EINVAL; + + if (mc->mc_ki[mc->mc_top] >= NUMKEYS(mc->mc_pg[mc->mc_top])) + return MDB_NOTFOUND; + + if (!(flags & MDB_NOSPILL) && (rc = mdb_page_spill(mc, NULL, NULL))) + return rc; + + rc = mdb_cursor_touch(mc); + if (rc) + return rc; + + mp = mc->mc_pg[mc->mc_top]; + if (IS_LEAF2(mp)) + goto del_key; + leaf = NODEPTR(mp, mc->mc_ki[mc->mc_top]); + + if (F_ISSET(leaf->mn_flags, F_DUPDATA)) { + if (flags & MDB_NODUPDATA) { + /* mdb_cursor_del0() will subtract the final entry */ + mc->mc_db->md_entries -= mc->mc_xcursor->mx_db.md_entries - 1; + mc->mc_xcursor->mx_cursor.mc_flags &= ~C_INITIALIZED; + } else { + if (!F_ISSET(leaf->mn_flags, F_SUBDATA)) { + mc->mc_xcursor->mx_cursor.mc_pg[0] = NODEDATA(leaf); + } + rc = mdb_cursor_del(&mc->mc_xcursor->mx_cursor, MDB_NOSPILL); + if (rc) + return rc; + /* If sub-DB still has entries, we're done */ + if (mc->mc_xcursor->mx_db.md_entries) { + if (leaf->mn_flags & F_SUBDATA) { + /* update subDB info */ + void *db = NODEDATA(leaf); + memcpy(db, &mc->mc_xcursor->mx_db, sizeof(MDB_db)); + } else { + MDB_cursor *m2; + /* shrink fake page */ + mdb_node_shrink(mp, mc->mc_ki[mc->mc_top]); + leaf = NODEPTR(mp, mc->mc_ki[mc->mc_top]); + mc->mc_xcursor->mx_cursor.mc_pg[0] = NODEDATA(leaf); + /* fix other sub-DB cursors pointed at fake pages on this page */ + for (m2 = mc->mc_txn->mt_cursors[mc->mc_dbi]; m2; m2=m2->mc_next) { + if (m2 == mc || m2->mc_snum < mc->mc_snum) continue; + if (!(m2->mc_flags & C_INITIALIZED)) continue; + if (m2->mc_pg[mc->mc_top] == mp) { + XCURSOR_REFRESH(m2, mc->mc_top, mp); + } + } + } + mc->mc_db->md_entries--; + return rc; + } else { + mc->mc_xcursor->mx_cursor.mc_flags &= ~C_INITIALIZED; + } + /* otherwise fall thru and delete the sub-DB */ + } + + if (leaf->mn_flags & F_SUBDATA) { + /* add all the child DB's pages to the free list */ + rc = mdb_drop0(&mc->mc_xcursor->mx_cursor, 0); + if (rc) + goto fail; + } + } + /* LMDB passes F_SUBDATA in 'flags' to delete a DB record */ + else if ((leaf->mn_flags ^ flags) & F_SUBDATA) { + rc = MDB_INCOMPATIBLE; + goto fail; + } + + /* add overflow pages to free list */ + if (F_ISSET(leaf->mn_flags, F_BIGDATA)) { + MDB_page *omp; + pgno_t pg; + + memcpy(&pg, NODEDATA(leaf), sizeof(pg)); + if ((rc = mdb_page_get(mc, pg, &omp, NULL)) || + (rc = mdb_ovpage_free(mc, omp))) + goto fail; + } + +del_key: + return mdb_cursor_del0(mc); + +fail: + mc->mc_txn->mt_flags |= MDB_TXN_ERROR; + return rc; +} + +/** Allocate and initialize new pages for a database. + * Set #MDB_TXN_ERROR on failure. + * @param[in] mc a cursor on the database being added to. + * @param[in] flags flags defining what type of page is being allocated. + * @param[in] num the number of pages to allocate. This is usually 1, + * unless allocating overflow pages for a large record. + * @param[out] mp Address of a page, or NULL on failure. + * @return 0 on success, non-zero on failure. + */ +static int +mdb_page_new(MDB_cursor *mc, uint32_t flags, int num, MDB_page **mp) +{ + MDB_page *np; + int rc; + + if ((rc = mdb_page_alloc(mc, num, &np))) + return rc; + DPRINTF(("allocated new mpage %"Z"u, page size %u", + np->mp_pgno, mc->mc_txn->mt_env->me_psize)); + np->mp_flags = flags | P_DIRTY; + np->mp_lower = (PAGEHDRSZ-PAGEBASE); + np->mp_upper = mc->mc_txn->mt_env->me_psize - PAGEBASE; + + if (IS_BRANCH(np)) + mc->mc_db->md_branch_pages++; + else if (IS_LEAF(np)) + mc->mc_db->md_leaf_pages++; + else if (IS_OVERFLOW(np)) { + mc->mc_db->md_overflow_pages += num; + np->mp_pages = num; + } + *mp = np; + + return 0; +} + +/** Calculate the size of a leaf node. + * The size depends on the environment's page size; if a data item + * is too large it will be put onto an overflow page and the node + * size will only include the key and not the data. Sizes are always + * rounded up to an even number of bytes, to guarantee 2-byte alignment + * of the #MDB_node headers. + * @param[in] env The environment handle. + * @param[in] key The key for the node. + * @param[in] data The data for the node. + * @return The number of bytes needed to store the node. + */ +static size_t +mdb_leaf_size(MDB_env *env, MDB_val *key, MDB_val *data) +{ + size_t sz; + + sz = LEAFSIZE(key, data); + if (sz > env->me_nodemax) { + /* put on overflow page */ + sz -= data->mv_size - sizeof(pgno_t); + } + + return EVEN(sz + sizeof(indx_t)); +} + +/** Calculate the size of a branch node. + * The size should depend on the environment's page size but since + * we currently don't support spilling large keys onto overflow + * pages, it's simply the size of the #MDB_node header plus the + * size of the key. Sizes are always rounded up to an even number + * of bytes, to guarantee 2-byte alignment of the #MDB_node headers. + * @param[in] env The environment handle. + * @param[in] key The key for the node. + * @return The number of bytes needed to store the node. + */ +static size_t +mdb_branch_size(MDB_env *env, MDB_val *key) +{ + size_t sz; + + sz = INDXSIZE(key); + if (sz > env->me_nodemax) { + /* put on overflow page */ + /* not implemented */ + /* sz -= key->size - sizeof(pgno_t); */ + } + + return sz + sizeof(indx_t); +} + +/** Add a node to the page pointed to by the cursor. + * Set #MDB_TXN_ERROR on failure. + * @param[in] mc The cursor for this operation. + * @param[in] indx The index on the page where the new node should be added. + * @param[in] key The key for the new node. + * @param[in] data The data for the new node, if any. + * @param[in] pgno The page number, if adding a branch node. + * @param[in] flags Flags for the node. + * @return 0 on success, non-zero on failure. Possible errors are: + * <ul> + * <li>ENOMEM - failed to allocate overflow pages for the node. + * <li>MDB_PAGE_FULL - there is insufficient room in the page. This error + * should never happen since all callers already calculate the + * page's free space before calling this function. + * </ul> + */ +static int +mdb_node_add(MDB_cursor *mc, indx_t indx, + MDB_val *key, MDB_val *data, pgno_t pgno, unsigned int flags) +{ + unsigned int i; + size_t node_size = NODESIZE; + ssize_t room; + indx_t ofs; + MDB_node *node; + MDB_page *mp = mc->mc_pg[mc->mc_top]; + MDB_page *ofp = NULL; /* overflow page */ + void *ndata; + DKBUF; + + mdb_cassert(mc, mp->mp_upper >= mp->mp_lower); + + DPRINTF(("add to %s %spage %"Z"u index %i, data size %"Z"u key size %"Z"u [%s]", + IS_LEAF(mp) ? "leaf" : "branch", + IS_SUBP(mp) ? "sub-" : "", + mdb_dbg_pgno(mp), indx, data ? data->mv_size : 0, + key ? key->mv_size : 0, key ? DKEY(key) : "null")); + + if (IS_LEAF2(mp)) { + /* Move higher keys up one slot. */ + int ksize = mc->mc_db->md_pad, dif; + char *ptr = LEAF2KEY(mp, indx, ksize); + dif = NUMKEYS(mp) - indx; + if (dif > 0) + memmove(ptr+ksize, ptr, dif*ksize); + /* insert new key */ + memcpy(ptr, key->mv_data, ksize); + + /* Just using these for counting */ + mp->mp_lower += sizeof(indx_t); + mp->mp_upper -= ksize - sizeof(indx_t); + return MDB_SUCCESS; + } + + room = (ssize_t)SIZELEFT(mp) - (ssize_t)sizeof(indx_t); + if (key != NULL) + node_size += key->mv_size; + if (IS_LEAF(mp)) { + mdb_cassert(mc, key && data); + if (F_ISSET(flags, F_BIGDATA)) { + /* Data already on overflow page. */ + node_size += sizeof(pgno_t); + } else if (node_size + data->mv_size > mc->mc_txn->mt_env->me_nodemax) { + int ovpages = OVPAGES(data->mv_size, mc->mc_txn->mt_env->me_psize); + int rc; + /* Put data on overflow page. */ + DPRINTF(("data size is %"Z"u, node would be %"Z"u, put data on overflow page", + data->mv_size, node_size+data->mv_size)); + node_size = EVEN(node_size + sizeof(pgno_t)); + if ((ssize_t)node_size > room) + goto full; + if ((rc = mdb_page_new(mc, P_OVERFLOW, ovpages, &ofp))) + return rc; + DPRINTF(("allocated overflow page %"Z"u", ofp->mp_pgno)); + flags |= F_BIGDATA; + goto update; + } else { + node_size += data->mv_size; + } + } + node_size = EVEN(node_size); + if ((ssize_t)node_size > room) + goto full; + +update: + /* Move higher pointers up one slot. */ + for (i = NUMKEYS(mp); i > indx; i--) + mp->mp_ptrs[i] = mp->mp_ptrs[i - 1]; + + /* Adjust free space offsets. */ + ofs = mp->mp_upper - node_size; + mdb_cassert(mc, ofs >= mp->mp_lower + sizeof(indx_t)); + mp->mp_ptrs[indx] = ofs; + mp->mp_upper = ofs; + mp->mp_lower += sizeof(indx_t); + + /* Write the node data. */ + node = NODEPTR(mp, indx); + node->mn_ksize = (key == NULL) ? 0 : key->mv_size; + node->mn_flags = flags; + if (IS_LEAF(mp)) + SETDSZ(node,data->mv_size); + else + SETPGNO(node,pgno); + + if (key) + memcpy(NODEKEY(node), key->mv_data, key->mv_size); + + if (IS_LEAF(mp)) { + ndata = NODEDATA(node); + if (ofp == NULL) { + if (F_ISSET(flags, F_BIGDATA)) + memcpy(ndata, data->mv_data, sizeof(pgno_t)); + else if (F_ISSET(flags, MDB_RESERVE)) + data->mv_data = ndata; + else + memcpy(ndata, data->mv_data, data->mv_size); + } else { + memcpy(ndata, &ofp->mp_pgno, sizeof(pgno_t)); + ndata = METADATA(ofp); + if (F_ISSET(flags, MDB_RESERVE)) + data->mv_data = ndata; + else + memcpy(ndata, data->mv_data, data->mv_size); + } + } + + return MDB_SUCCESS; + +full: + DPRINTF(("not enough room in page %"Z"u, got %u ptrs", + mdb_dbg_pgno(mp), NUMKEYS(mp))); + DPRINTF(("upper-lower = %u - %u = %"Z"d", mp->mp_upper,mp->mp_lower,room)); + DPRINTF(("node size = %"Z"u", node_size)); + mc->mc_txn->mt_flags |= MDB_TXN_ERROR; + return MDB_PAGE_FULL; +} + +/** Delete the specified node from a page. + * @param[in] mc Cursor pointing to the node to delete. + * @param[in] ksize The size of a node. Only used if the page is + * part of a #MDB_DUPFIXED database. + */ +static void +mdb_node_del(MDB_cursor *mc, int ksize) +{ + MDB_page *mp = mc->mc_pg[mc->mc_top]; + indx_t indx = mc->mc_ki[mc->mc_top]; + unsigned int sz; + indx_t i, j, numkeys, ptr; + MDB_node *node; + char *base; + + DPRINTF(("delete node %u on %s page %"Z"u", indx, + IS_LEAF(mp) ? "leaf" : "branch", mdb_dbg_pgno(mp))); + numkeys = NUMKEYS(mp); + mdb_cassert(mc, indx < numkeys); + + if (IS_LEAF2(mp)) { + int x = numkeys - 1 - indx; + base = LEAF2KEY(mp, indx, ksize); + if (x) + memmove(base, base + ksize, x * ksize); + mp->mp_lower -= sizeof(indx_t); + mp->mp_upper += ksize - sizeof(indx_t); + return; + } + + node = NODEPTR(mp, indx); + sz = NODESIZE + node->mn_ksize; + if (IS_LEAF(mp)) { + if (F_ISSET(node->mn_flags, F_BIGDATA)) + sz += sizeof(pgno_t); + else + sz += NODEDSZ(node); + } + sz = EVEN(sz); + + ptr = mp->mp_ptrs[indx]; + for (i = j = 0; i < numkeys; i++) { + if (i != indx) { + mp->mp_ptrs[j] = mp->mp_ptrs[i]; + if (mp->mp_ptrs[i] < ptr) + mp->mp_ptrs[j] += sz; + j++; + } + } + + base = (char *)mp + mp->mp_upper + PAGEBASE; + memmove(base + sz, base, ptr - mp->mp_upper); + + mp->mp_lower -= sizeof(indx_t); + mp->mp_upper += sz; +} + +/** Compact the main page after deleting a node on a subpage. + * @param[in] mp The main page to operate on. + * @param[in] indx The index of the subpage on the main page. + */ +static void +mdb_node_shrink(MDB_page *mp, indx_t indx) +{ + MDB_node *node; + MDB_page *sp, *xp; + char *base; + indx_t delta, nsize, len, ptr; + int i; + + node = NODEPTR(mp, indx); + sp = (MDB_page *)NODEDATA(node); + delta = SIZELEFT(sp); + nsize = NODEDSZ(node) - delta; + + /* Prepare to shift upward, set len = length(subpage part to shift) */ + if (IS_LEAF2(sp)) { + len = nsize; + if (nsize & 1) + return; /* do not make the node uneven-sized */ + } else { + xp = (MDB_page *)((char *)sp + delta); /* destination subpage */ + for (i = NUMKEYS(sp); --i >= 0; ) + xp->mp_ptrs[i] = sp->mp_ptrs[i] - delta; + len = PAGEHDRSZ; + } + sp->mp_upper = sp->mp_lower; + COPY_PGNO(sp->mp_pgno, mp->mp_pgno); + SETDSZ(node, nsize); + + /* Shift <lower nodes...initial part of subpage> upward */ + base = (char *)mp + mp->mp_upper + PAGEBASE; + memmove(base + delta, base, (char *)sp + len - base); + + ptr = mp->mp_ptrs[indx]; + for (i = NUMKEYS(mp); --i >= 0; ) { + if (mp->mp_ptrs[i] <= ptr) + mp->mp_ptrs[i] += delta; + } + mp->mp_upper += delta; +} + +/** Initial setup of a sorted-dups cursor. + * Sorted duplicates are implemented as a sub-database for the given key. + * The duplicate data items are actually keys of the sub-database. + * Operations on the duplicate data items are performed using a sub-cursor + * initialized when the sub-database is first accessed. This function does + * the preliminary setup of the sub-cursor, filling in the fields that + * depend only on the parent DB. + * @param[in] mc The main cursor whose sorted-dups cursor is to be initialized. + */ +static void +mdb_xcursor_init0(MDB_cursor *mc) +{ + MDB_xcursor *mx = mc->mc_xcursor; + + mx->mx_cursor.mc_xcursor = NULL; + mx->mx_cursor.mc_txn = mc->mc_txn; + mx->mx_cursor.mc_db = &mx->mx_db; + mx->mx_cursor.mc_dbx = &mx->mx_dbx; + mx->mx_cursor.mc_dbi = mc->mc_dbi; + mx->mx_cursor.mc_dbflag = &mx->mx_dbflag; + mx->mx_cursor.mc_snum = 0; + mx->mx_cursor.mc_top = 0; + mx->mx_cursor.mc_flags = C_SUB; + mx->mx_dbx.md_name.mv_size = 0; + mx->mx_dbx.md_name.mv_data = NULL; + mx->mx_dbx.md_cmp = mc->mc_dbx->md_dcmp; + mx->mx_dbx.md_dcmp = NULL; + mx->mx_dbx.md_rel = mc->mc_dbx->md_rel; +} + +/** Final setup of a sorted-dups cursor. + * Sets up the fields that depend on the data from the main cursor. + * @param[in] mc The main cursor whose sorted-dups cursor is to be initialized. + * @param[in] node The data containing the #MDB_db record for the + * sorted-dup database. + */ +static void +mdb_xcursor_init1(MDB_cursor *mc, MDB_node *node) +{ + MDB_xcursor *mx = mc->mc_xcursor; + + if (node->mn_flags & F_SUBDATA) { + memcpy(&mx->mx_db, NODEDATA(node), sizeof(MDB_db)); + mx->mx_cursor.mc_pg[0] = 0; + mx->mx_cursor.mc_snum = 0; + mx->mx_cursor.mc_top = 0; + mx->mx_cursor.mc_flags = C_SUB; + } else { + MDB_page *fp = NODEDATA(node); + mx->mx_db.md_pad = 0; + mx->mx_db.md_flags = 0; + mx->mx_db.md_depth = 1; + mx->mx_db.md_branch_pages = 0; + mx->mx_db.md_leaf_pages = 1; + mx->mx_db.md_overflow_pages = 0; + mx->mx_db.md_entries = NUMKEYS(fp); + COPY_PGNO(mx->mx_db.md_root, fp->mp_pgno); + mx->mx_cursor.mc_snum = 1; + mx->mx_cursor.mc_top = 0; + mx->mx_cursor.mc_flags = C_INITIALIZED|C_SUB; + mx->mx_cursor.mc_pg[0] = fp; + mx->mx_cursor.mc_ki[0] = 0; + if (mc->mc_db->md_flags & MDB_DUPFIXED) { + mx->mx_db.md_flags = MDB_DUPFIXED; + mx->mx_db.md_pad = fp->mp_pad; + if (mc->mc_db->md_flags & MDB_INTEGERDUP) + mx->mx_db.md_flags |= MDB_INTEGERKEY; + } + } + DPRINTF(("Sub-db -%u root page %"Z"u", mx->mx_cursor.mc_dbi, + mx->mx_db.md_root)); + mx->mx_dbflag = DB_VALID|DB_USRVALID|DB_DUPDATA; +#if UINT_MAX < SIZE_MAX + if (mx->mx_dbx.md_cmp == mdb_cmp_int && mx->mx_db.md_pad == sizeof(size_t)) + mx->mx_dbx.md_cmp = mdb_cmp_clong; +#endif +} + + +/** Fixup a sorted-dups cursor due to underlying update. + * Sets up some fields that depend on the data from the main cursor. + * Almost the same as init1, but skips initialization steps if the + * xcursor had already been used. + * @param[in] mc The main cursor whose sorted-dups cursor is to be fixed up. + * @param[in] src_mx The xcursor of an up-to-date cursor. + * @param[in] new_dupdata True if converting from a non-#F_DUPDATA item. + */ +static void +mdb_xcursor_init2(MDB_cursor *mc, MDB_xcursor *src_mx, int new_dupdata) +{ + MDB_xcursor *mx = mc->mc_xcursor; + + if (new_dupdata) { + mx->mx_cursor.mc_snum = 1; + mx->mx_cursor.mc_top = 0; + mx->mx_cursor.mc_flags |= C_INITIALIZED; + mx->mx_cursor.mc_ki[0] = 0; + mx->mx_dbflag = DB_VALID|DB_USRVALID|DB_DUPDATA; +#if UINT_MAX < SIZE_MAX + mx->mx_dbx.md_cmp = src_mx->mx_dbx.md_cmp; +#endif + } else if (!(mx->mx_cursor.mc_flags & C_INITIALIZED)) { + return; + } + mx->mx_db = src_mx->mx_db; + mx->mx_cursor.mc_pg[0] = src_mx->mx_cursor.mc_pg[0]; + DPRINTF(("Sub-db -%u root page %"Z"u", mx->mx_cursor.mc_dbi, + mx->mx_db.md_root)); +} + +/** Initialize a cursor for a given transaction and database. */ +static void +mdb_cursor_init(MDB_cursor *mc, MDB_txn *txn, MDB_dbi dbi, MDB_xcursor *mx) +{ + mc->mc_next = NULL; + mc->mc_backup = NULL; + mc->mc_dbi = dbi; + mc->mc_txn = txn; + mc->mc_db = &txn->mt_dbs[dbi]; + mc->mc_dbx = &txn->mt_dbxs[dbi]; + mc->mc_dbflag = &txn->mt_dbflags[dbi]; + mc->mc_snum = 0; + mc->mc_top = 0; + mc->mc_pg[0] = 0; + mc->mc_ki[0] = 0; + mc->mc_flags = 0; + if (txn->mt_dbs[dbi].md_flags & MDB_DUPSORT) { + mdb_tassert(txn, mx != NULL); + mc->mc_xcursor = mx; + mdb_xcursor_init0(mc); + } else { + mc->mc_xcursor = NULL; + } + if (*mc->mc_dbflag & DB_STALE) { + mdb_page_search(mc, NULL, MDB_PS_ROOTONLY); + } +} + +int +mdb_cursor_open(MDB_txn *txn, MDB_dbi dbi, MDB_cursor **ret) +{ + MDB_cursor *mc; + size_t size = sizeof(MDB_cursor); + + if (!ret || !TXN_DBI_EXIST(txn, dbi, DB_VALID)) + return EINVAL; + + if (txn->mt_flags & MDB_TXN_BLOCKED) + return MDB_BAD_TXN; + + if (dbi == FREE_DBI && !F_ISSET(txn->mt_flags, MDB_TXN_RDONLY)) + return EINVAL; + + if (txn->mt_dbs[dbi].md_flags & MDB_DUPSORT) + size += sizeof(MDB_xcursor); + + if ((mc = malloc(size)) != NULL) { + mdb_cursor_init(mc, txn, dbi, (MDB_xcursor *)(mc + 1)); + if (txn->mt_cursors) { + mc->mc_next = txn->mt_cursors[dbi]; + txn->mt_cursors[dbi] = mc; + mc->mc_flags |= C_UNTRACK; + } + } else { + return ENOMEM; + } + + *ret = mc; + + return MDB_SUCCESS; +} + +int +mdb_cursor_renew(MDB_txn *txn, MDB_cursor *mc) +{ + if (!mc || !TXN_DBI_EXIST(txn, mc->mc_dbi, DB_VALID)) + return EINVAL; + + if ((mc->mc_flags & C_UNTRACK) || txn->mt_cursors) + return EINVAL; + + if (txn->mt_flags & MDB_TXN_BLOCKED) + return MDB_BAD_TXN; + + mdb_cursor_init(mc, txn, mc->mc_dbi, mc->mc_xcursor); + return MDB_SUCCESS; +} + +/* Return the count of duplicate data items for the current key */ +int +mdb_cursor_count(MDB_cursor *mc, size_t *countp) +{ + MDB_node *leaf; + + if (mc == NULL || countp == NULL) + return EINVAL; + + if (mc->mc_xcursor == NULL) + return MDB_INCOMPATIBLE; + + if (mc->mc_txn->mt_flags & MDB_TXN_BLOCKED) + return MDB_BAD_TXN; + + if (!(mc->mc_flags & C_INITIALIZED)) + return EINVAL; + + if (!mc->mc_snum) + return MDB_NOTFOUND; + + if (mc->mc_flags & C_EOF) { + if (mc->mc_ki[mc->mc_top] >= NUMKEYS(mc->mc_pg[mc->mc_top])) + return MDB_NOTFOUND; + mc->mc_flags ^= C_EOF; + } + + leaf = NODEPTR(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]); + if (!F_ISSET(leaf->mn_flags, F_DUPDATA)) { + *countp = 1; + } else { + if (!(mc->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED)) + return EINVAL; + + *countp = mc->mc_xcursor->mx_db.md_entries; + } + return MDB_SUCCESS; +} + +void +mdb_cursor_close(MDB_cursor *mc) +{ + if (mc && !mc->mc_backup) { + /* remove from txn, if tracked */ + if ((mc->mc_flags & C_UNTRACK) && mc->mc_txn->mt_cursors) { + MDB_cursor **prev = &mc->mc_txn->mt_cursors[mc->mc_dbi]; + while (*prev && *prev != mc) prev = &(*prev)->mc_next; + if (*prev == mc) + *prev = mc->mc_next; + } + free(mc); + } +} + +MDB_txn * +mdb_cursor_txn(MDB_cursor *mc) +{ + if (!mc) return NULL; + return mc->mc_txn; +} + +MDB_dbi +mdb_cursor_dbi(MDB_cursor *mc) +{ + return mc->mc_dbi; +} + +/** Replace the key for a branch node with a new key. + * Set #MDB_TXN_ERROR on failure. + * @param[in] mc Cursor pointing to the node to operate on. + * @param[in] key The new key to use. + * @return 0 on success, non-zero on failure. + */ +static int +mdb_update_key(MDB_cursor *mc, MDB_val *key) +{ + MDB_page *mp; + MDB_node *node; + char *base; + size_t len; + int delta, ksize, oksize; + indx_t ptr, i, numkeys, indx; + DKBUF; + + indx = mc->mc_ki[mc->mc_top]; + mp = mc->mc_pg[mc->mc_top]; + node = NODEPTR(mp, indx); + ptr = mp->mp_ptrs[indx]; +#if MDB_DEBUG + { + MDB_val k2; + char kbuf2[DKBUF_MAXKEYSIZE*2+1]; + k2.mv_data = NODEKEY(node); + k2.mv_size = node->mn_ksize; + DPRINTF(("update key %u (ofs %u) [%s] to [%s] on page %"Z"u", + indx, ptr, + mdb_dkey(&k2, kbuf2), + DKEY(key), + mp->mp_pgno)); + } +#endif + + /* Sizes must be 2-byte aligned. */ + ksize = EVEN(key->mv_size); + oksize = EVEN(node->mn_ksize); + delta = ksize - oksize; + + /* Shift node contents if EVEN(key length) changed. */ + if (delta) { + if (delta > 0 && SIZELEFT(mp) < delta) { + pgno_t pgno; + /* not enough space left, do a delete and split */ + DPRINTF(("Not enough room, delta = %d, splitting...", delta)); + pgno = NODEPGNO(node); + mdb_node_del(mc, 0); + return mdb_page_split(mc, key, NULL, pgno, MDB_SPLIT_REPLACE); + } + + numkeys = NUMKEYS(mp); + for (i = 0; i < numkeys; i++) { + if (mp->mp_ptrs[i] <= ptr) + mp->mp_ptrs[i] -= delta; + } + + base = (char *)mp + mp->mp_upper + PAGEBASE; + len = ptr - mp->mp_upper + NODESIZE; + memmove(base - delta, base, len); + mp->mp_upper -= delta; + + node = NODEPTR(mp, indx); + } + + /* But even if no shift was needed, update ksize */ + if (node->mn_ksize != key->mv_size) + node->mn_ksize = key->mv_size; + + if (key->mv_size) + memcpy(NODEKEY(node), key->mv_data, key->mv_size); + + return MDB_SUCCESS; +} + +static void +mdb_cursor_copy(const MDB_cursor *csrc, MDB_cursor *cdst); + +/** Perform \b act while tracking temporary cursor \b mn */ +#define WITH_CURSOR_TRACKING(mn, act) do { \ + MDB_cursor dummy, *tracked, **tp = &(mn).mc_txn->mt_cursors[mn.mc_dbi]; \ + if ((mn).mc_flags & C_SUB) { \ + dummy.mc_flags = C_INITIALIZED; \ + dummy.mc_xcursor = (MDB_xcursor *)&(mn); \ + tracked = &dummy; \ + } else { \ + tracked = &(mn); \ + } \ + tracked->mc_next = *tp; \ + *tp = tracked; \ + { act; } \ + *tp = tracked->mc_next; \ +} while (0) + +/** Move a node from csrc to cdst. + */ +static int +mdb_node_move(MDB_cursor *csrc, MDB_cursor *cdst, int fromleft) +{ + MDB_node *srcnode; + MDB_val key, data; + pgno_t srcpg; + MDB_cursor mn; + int rc; + unsigned short flags; + + DKBUF; + + /* Mark src and dst as dirty. */ + if ((rc = mdb_page_touch(csrc)) || + (rc = mdb_page_touch(cdst))) + return rc; + + if (IS_LEAF2(csrc->mc_pg[csrc->mc_top])) { + key.mv_size = csrc->mc_db->md_pad; + key.mv_data = LEAF2KEY(csrc->mc_pg[csrc->mc_top], csrc->mc_ki[csrc->mc_top], key.mv_size); + data.mv_size = 0; + data.mv_data = NULL; + srcpg = 0; + flags = 0; + } else { + srcnode = NODEPTR(csrc->mc_pg[csrc->mc_top], csrc->mc_ki[csrc->mc_top]); + mdb_cassert(csrc, !((size_t)srcnode & 1)); + srcpg = NODEPGNO(srcnode); + flags = srcnode->mn_flags; + if (csrc->mc_ki[csrc->mc_top] == 0 && IS_BRANCH(csrc->mc_pg[csrc->mc_top])) { + unsigned int snum = csrc->mc_snum; + MDB_node *s2; + /* must find the lowest key below src */ + rc = mdb_page_search_lowest(csrc); + if (rc) + return rc; + if (IS_LEAF2(csrc->mc_pg[csrc->mc_top])) { + key.mv_size = csrc->mc_db->md_pad; + key.mv_data = LEAF2KEY(csrc->mc_pg[csrc->mc_top], 0, key.mv_size); + } else { + s2 = NODEPTR(csrc->mc_pg[csrc->mc_top], 0); + key.mv_size = NODEKSZ(s2); + key.mv_data = NODEKEY(s2); + } + csrc->mc_snum = snum--; + csrc->mc_top = snum; + } else { + key.mv_size = NODEKSZ(srcnode); + key.mv_data = NODEKEY(srcnode); + } + data.mv_size = NODEDSZ(srcnode); + data.mv_data = NODEDATA(srcnode); + } + mn.mc_xcursor = NULL; + if (IS_BRANCH(cdst->mc_pg[cdst->mc_top]) && cdst->mc_ki[cdst->mc_top] == 0) { + unsigned int snum = cdst->mc_snum; + MDB_node *s2; + MDB_val bkey; + /* must find the lowest key below dst */ + mdb_cursor_copy(cdst, &mn); + rc = mdb_page_search_lowest(&mn); + if (rc) + return rc; + if (IS_LEAF2(mn.mc_pg[mn.mc_top])) { + bkey.mv_size = mn.mc_db->md_pad; + bkey.mv_data = LEAF2KEY(mn.mc_pg[mn.mc_top], 0, bkey.mv_size); + } else { + s2 = NODEPTR(mn.mc_pg[mn.mc_top], 0); + bkey.mv_size = NODEKSZ(s2); + bkey.mv_data = NODEKEY(s2); + } + mn.mc_snum = snum--; + mn.mc_top = snum; + mn.mc_ki[snum] = 0; + rc = mdb_update_key(&mn, &bkey); + if (rc) + return rc; + } + + DPRINTF(("moving %s node %u [%s] on page %"Z"u to node %u on page %"Z"u", + IS_LEAF(csrc->mc_pg[csrc->mc_top]) ? "leaf" : "branch", + csrc->mc_ki[csrc->mc_top], + DKEY(&key), + csrc->mc_pg[csrc->mc_top]->mp_pgno, + cdst->mc_ki[cdst->mc_top], cdst->mc_pg[cdst->mc_top]->mp_pgno)); + + /* Add the node to the destination page. + */ + rc = mdb_node_add(cdst, cdst->mc_ki[cdst->mc_top], &key, &data, srcpg, flags); + if (rc != MDB_SUCCESS) + return rc; + + /* Delete the node from the source page. + */ + mdb_node_del(csrc, key.mv_size); + + { + /* Adjust other cursors pointing to mp */ + MDB_cursor *m2, *m3; + MDB_dbi dbi = csrc->mc_dbi; + MDB_page *mpd, *mps; + + mps = csrc->mc_pg[csrc->mc_top]; + /* If we're adding on the left, bump others up */ + if (fromleft) { + mpd = cdst->mc_pg[csrc->mc_top]; + for (m2 = csrc->mc_txn->mt_cursors[dbi]; m2; m2=m2->mc_next) { + if (csrc->mc_flags & C_SUB) + m3 = &m2->mc_xcursor->mx_cursor; + else + m3 = m2; + if (!(m3->mc_flags & C_INITIALIZED) || m3->mc_top < csrc->mc_top) + continue; + if (m3 != cdst && + m3->mc_pg[csrc->mc_top] == mpd && + m3->mc_ki[csrc->mc_top] >= cdst->mc_ki[csrc->mc_top]) { + m3->mc_ki[csrc->mc_top]++; + } + if (m3 !=csrc && + m3->mc_pg[csrc->mc_top] == mps && + m3->mc_ki[csrc->mc_top] == csrc->mc_ki[csrc->mc_top]) { + m3->mc_pg[csrc->mc_top] = cdst->mc_pg[cdst->mc_top]; + m3->mc_ki[csrc->mc_top] = cdst->mc_ki[cdst->mc_top]; + m3->mc_ki[csrc->mc_top-1]++; + } + if (IS_LEAF(mps)) + XCURSOR_REFRESH(m3, csrc->mc_top, m3->mc_pg[csrc->mc_top]); + } + } else + /* Adding on the right, bump others down */ + { + for (m2 = csrc->mc_txn->mt_cursors[dbi]; m2; m2=m2->mc_next) { + if (csrc->mc_flags & C_SUB) + m3 = &m2->mc_xcursor->mx_cursor; + else + m3 = m2; + if (m3 == csrc) continue; + if (!(m3->mc_flags & C_INITIALIZED) || m3->mc_top < csrc->mc_top) + continue; + if (m3->mc_pg[csrc->mc_top] == mps) { + if (!m3->mc_ki[csrc->mc_top]) { + m3->mc_pg[csrc->mc_top] = cdst->mc_pg[cdst->mc_top]; + m3->mc_ki[csrc->mc_top] = cdst->mc_ki[cdst->mc_top]; + m3->mc_ki[csrc->mc_top-1]--; + } else { + m3->mc_ki[csrc->mc_top]--; + } + if (IS_LEAF(mps)) + XCURSOR_REFRESH(m3, csrc->mc_top, m3->mc_pg[csrc->mc_top]); + } + } + } + } + + /* Update the parent separators. + */ + if (csrc->mc_ki[csrc->mc_top] == 0) { + if (csrc->mc_ki[csrc->mc_top-1] != 0) { + if (IS_LEAF2(csrc->mc_pg[csrc->mc_top])) { + key.mv_data = LEAF2KEY(csrc->mc_pg[csrc->mc_top], 0, key.mv_size); + } else { + srcnode = NODEPTR(csrc->mc_pg[csrc->mc_top], 0); + key.mv_size = NODEKSZ(srcnode); + key.mv_data = NODEKEY(srcnode); + } + DPRINTF(("update separator for source page %"Z"u to [%s]", + csrc->mc_pg[csrc->mc_top]->mp_pgno, DKEY(&key))); + mdb_cursor_copy(csrc, &mn); + mn.mc_snum--; + mn.mc_top--; + /* We want mdb_rebalance to find mn when doing fixups */ + WITH_CURSOR_TRACKING(mn, + rc = mdb_update_key(&mn, &key)); + if (rc) + return rc; + } + if (IS_BRANCH(csrc->mc_pg[csrc->mc_top])) { + MDB_val nullkey; + indx_t ix = csrc->mc_ki[csrc->mc_top]; + nullkey.mv_size = 0; + csrc->mc_ki[csrc->mc_top] = 0; + rc = mdb_update_key(csrc, &nullkey); + csrc->mc_ki[csrc->mc_top] = ix; + mdb_cassert(csrc, rc == MDB_SUCCESS); + } + } + + if (cdst->mc_ki[cdst->mc_top] == 0) { + if (cdst->mc_ki[cdst->mc_top-1] != 0) { + if (IS_LEAF2(csrc->mc_pg[csrc->mc_top])) { + key.mv_data = LEAF2KEY(cdst->mc_pg[cdst->mc_top], 0, key.mv_size); + } else { + srcnode = NODEPTR(cdst->mc_pg[cdst->mc_top], 0); + key.mv_size = NODEKSZ(srcnode); + key.mv_data = NODEKEY(srcnode); + } + DPRINTF(("update separator for destination page %"Z"u to [%s]", + cdst->mc_pg[cdst->mc_top]->mp_pgno, DKEY(&key))); + mdb_cursor_copy(cdst, &mn); + mn.mc_snum--; + mn.mc_top--; + /* We want mdb_rebalance to find mn when doing fixups */ + WITH_CURSOR_TRACKING(mn, + rc = mdb_update_key(&mn, &key)); + if (rc) + return rc; + } + if (IS_BRANCH(cdst->mc_pg[cdst->mc_top])) { + MDB_val nullkey; + indx_t ix = cdst->mc_ki[cdst->mc_top]; + nullkey.mv_size = 0; + cdst->mc_ki[cdst->mc_top] = 0; + rc = mdb_update_key(cdst, &nullkey); + cdst->mc_ki[cdst->mc_top] = ix; + mdb_cassert(cdst, rc == MDB_SUCCESS); + } + } + + return MDB_SUCCESS; +} + +/** Merge one page into another. + * The nodes from the page pointed to by \b csrc will + * be copied to the page pointed to by \b cdst and then + * the \b csrc page will be freed. + * @param[in] csrc Cursor pointing to the source page. + * @param[in] cdst Cursor pointing to the destination page. + * @return 0 on success, non-zero on failure. + */ +static int +mdb_page_merge(MDB_cursor *csrc, MDB_cursor *cdst) +{ + MDB_page *psrc, *pdst; + MDB_node *srcnode; + MDB_val key, data; + unsigned nkeys; + int rc; + indx_t i, j; + + psrc = csrc->mc_pg[csrc->mc_top]; + pdst = cdst->mc_pg[cdst->mc_top]; + + DPRINTF(("merging page %"Z"u into %"Z"u", psrc->mp_pgno, pdst->mp_pgno)); + + mdb_cassert(csrc, csrc->mc_snum > 1); /* can't merge root page */ + mdb_cassert(csrc, cdst->mc_snum > 1); + + /* Mark dst as dirty. */ + if ((rc = mdb_page_touch(cdst))) + return rc; + + /* get dst page again now that we've touched it. */ + pdst = cdst->mc_pg[cdst->mc_top]; + + /* Move all nodes from src to dst. + */ + j = nkeys = NUMKEYS(pdst); + if (IS_LEAF2(psrc)) { + key.mv_size = csrc->mc_db->md_pad; + key.mv_data = METADATA(psrc); + for (i = 0; i < NUMKEYS(psrc); i++, j++) { + rc = mdb_node_add(cdst, j, &key, NULL, 0, 0); + if (rc != MDB_SUCCESS) + return rc; + key.mv_data = (char *)key.mv_data + key.mv_size; + } + } else { + for (i = 0; i < NUMKEYS(psrc); i++, j++) { + srcnode = NODEPTR(psrc, i); + if (i == 0 && IS_BRANCH(psrc)) { + MDB_cursor mn; + MDB_node *s2; + mdb_cursor_copy(csrc, &mn); + mn.mc_xcursor = NULL; + /* must find the lowest key below src */ + rc = mdb_page_search_lowest(&mn); + if (rc) + return rc; + if (IS_LEAF2(mn.mc_pg[mn.mc_top])) { + key.mv_size = mn.mc_db->md_pad; + key.mv_data = LEAF2KEY(mn.mc_pg[mn.mc_top], 0, key.mv_size); + } else { + s2 = NODEPTR(mn.mc_pg[mn.mc_top], 0); + key.mv_size = NODEKSZ(s2); + key.mv_data = NODEKEY(s2); + } + } else { + key.mv_size = srcnode->mn_ksize; + key.mv_data = NODEKEY(srcnode); + } + + data.mv_size = NODEDSZ(srcnode); + data.mv_data = NODEDATA(srcnode); + rc = mdb_node_add(cdst, j, &key, &data, NODEPGNO(srcnode), srcnode->mn_flags); + if (rc != MDB_SUCCESS) + return rc; + } + } + + DPRINTF(("dst page %"Z"u now has %u keys (%.1f%% filled)", + pdst->mp_pgno, NUMKEYS(pdst), + (float)PAGEFILL(cdst->mc_txn->mt_env, pdst) / 10)); + + /* Unlink the src page from parent and add to free list. + */ + csrc->mc_top--; + mdb_node_del(csrc, 0); + if (csrc->mc_ki[csrc->mc_top] == 0) { + key.mv_size = 0; + rc = mdb_update_key(csrc, &key); + if (rc) { + csrc->mc_top++; + return rc; + } + } + csrc->mc_top++; + + psrc = csrc->mc_pg[csrc->mc_top]; + /* If not operating on FreeDB, allow this page to be reused + * in this txn. Otherwise just add to free list. + */ + rc = mdb_page_loose(csrc, psrc); + if (rc) + return rc; + if (IS_LEAF(psrc)) + csrc->mc_db->md_leaf_pages--; + else + csrc->mc_db->md_branch_pages--; + { + /* Adjust other cursors pointing to mp */ + MDB_cursor *m2, *m3; + MDB_dbi dbi = csrc->mc_dbi; + unsigned int top = csrc->mc_top; + + for (m2 = csrc->mc_txn->mt_cursors[dbi]; m2; m2=m2->mc_next) { + if (csrc->mc_flags & C_SUB) + m3 = &m2->mc_xcursor->mx_cursor; + else + m3 = m2; + if (m3 == csrc) continue; + if (m3->mc_snum < csrc->mc_snum) continue; + if (m3->mc_pg[top] == psrc) { + m3->mc_pg[top] = pdst; + m3->mc_ki[top] += nkeys; + m3->mc_ki[top-1] = cdst->mc_ki[top-1]; + } else if (m3->mc_pg[top-1] == csrc->mc_pg[top-1] && + m3->mc_ki[top-1] > csrc->mc_ki[top-1]) { + m3->mc_ki[top-1]--; + } + if (IS_LEAF(psrc)) + XCURSOR_REFRESH(m3, top, m3->mc_pg[top]); + } + } + { + unsigned int snum = cdst->mc_snum; + uint16_t depth = cdst->mc_db->md_depth; + mdb_cursor_pop(cdst); + rc = mdb_rebalance(cdst); + /* Did the tree height change? */ + if (depth != cdst->mc_db->md_depth) + snum += cdst->mc_db->md_depth - depth; + cdst->mc_snum = snum; + cdst->mc_top = snum-1; + } + return rc; +} + +/** Copy the contents of a cursor. + * @param[in] csrc The cursor to copy from. + * @param[out] cdst The cursor to copy to. + */ +static void +mdb_cursor_copy(const MDB_cursor *csrc, MDB_cursor *cdst) +{ + unsigned int i; + + cdst->mc_txn = csrc->mc_txn; + cdst->mc_dbi = csrc->mc_dbi; + cdst->mc_db = csrc->mc_db; + cdst->mc_dbx = csrc->mc_dbx; + cdst->mc_snum = csrc->mc_snum; + cdst->mc_top = csrc->mc_top; + cdst->mc_flags = csrc->mc_flags; + + for (i=0; i<csrc->mc_snum; i++) { + cdst->mc_pg[i] = csrc->mc_pg[i]; + cdst->mc_ki[i] = csrc->mc_ki[i]; + } +} + +/** Rebalance the tree after a delete operation. + * @param[in] mc Cursor pointing to the page where rebalancing + * should begin. + * @return 0 on success, non-zero on failure. + */ +static int +mdb_rebalance(MDB_cursor *mc) +{ + MDB_node *node; + int rc, fromleft; + unsigned int ptop, minkeys, thresh; + MDB_cursor mn; + indx_t oldki; + + if (IS_BRANCH(mc->mc_pg[mc->mc_top])) { + minkeys = 2; + thresh = 1; + } else { + minkeys = 1; + thresh = FILL_THRESHOLD; + } + DPRINTF(("rebalancing %s page %"Z"u (has %u keys, %.1f%% full)", + IS_LEAF(mc->mc_pg[mc->mc_top]) ? "leaf" : "branch", + mdb_dbg_pgno(mc->mc_pg[mc->mc_top]), NUMKEYS(mc->mc_pg[mc->mc_top]), + (float)PAGEFILL(mc->mc_txn->mt_env, mc->mc_pg[mc->mc_top]) / 10)); + + if (PAGEFILL(mc->mc_txn->mt_env, mc->mc_pg[mc->mc_top]) >= thresh && + NUMKEYS(mc->mc_pg[mc->mc_top]) >= minkeys) { + DPRINTF(("no need to rebalance page %"Z"u, above fill threshold", + mdb_dbg_pgno(mc->mc_pg[mc->mc_top]))); + return MDB_SUCCESS; + } + + if (mc->mc_snum < 2) { + MDB_page *mp = mc->mc_pg[0]; + if (IS_SUBP(mp)) { + DPUTS("Can't rebalance a subpage, ignoring"); + return MDB_SUCCESS; + } + if (NUMKEYS(mp) == 0) { + DPUTS("tree is completely empty"); + mc->mc_db->md_root = P_INVALID; + mc->mc_db->md_depth = 0; + mc->mc_db->md_leaf_pages = 0; + rc = mdb_midl_append(&mc->mc_txn->mt_free_pgs, mp->mp_pgno); + if (rc) + return rc; + /* Adjust cursors pointing to mp */ + mc->mc_snum = 0; + mc->mc_top = 0; + mc->mc_flags &= ~C_INITIALIZED; + { + MDB_cursor *m2, *m3; + MDB_dbi dbi = mc->mc_dbi; + + for (m2 = mc->mc_txn->mt_cursors[dbi]; m2; m2=m2->mc_next) { + if (mc->mc_flags & C_SUB) + m3 = &m2->mc_xcursor->mx_cursor; + else + m3 = m2; + if (!(m3->mc_flags & C_INITIALIZED) || (m3->mc_snum < mc->mc_snum)) + continue; + if (m3->mc_pg[0] == mp) { + m3->mc_snum = 0; + m3->mc_top = 0; + m3->mc_flags &= ~C_INITIALIZED; + } + } + } + } else if (IS_BRANCH(mp) && NUMKEYS(mp) == 1) { + int i; + DPUTS("collapsing root page!"); + rc = mdb_midl_append(&mc->mc_txn->mt_free_pgs, mp->mp_pgno); + if (rc) + return rc; + mc->mc_db->md_root = NODEPGNO(NODEPTR(mp, 0)); + rc = mdb_page_get(mc, mc->mc_db->md_root, &mc->mc_pg[0], NULL); + if (rc) + return rc; + mc->mc_db->md_depth--; + mc->mc_db->md_branch_pages--; + mc->mc_ki[0] = mc->mc_ki[1]; + for (i = 1; i<mc->mc_db->md_depth; i++) { + mc->mc_pg[i] = mc->mc_pg[i+1]; + mc->mc_ki[i] = mc->mc_ki[i+1]; + } + { + /* Adjust other cursors pointing to mp */ + MDB_cursor *m2, *m3; + MDB_dbi dbi = mc->mc_dbi; + + for (m2 = mc->mc_txn->mt_cursors[dbi]; m2; m2=m2->mc_next) { + if (mc->mc_flags & C_SUB) + m3 = &m2->mc_xcursor->mx_cursor; + else + m3 = m2; + if (m3 == mc) continue; + if (!(m3->mc_flags & C_INITIALIZED)) + continue; + if (m3->mc_pg[0] == mp) { + for (i=0; i<mc->mc_db->md_depth; i++) { + m3->mc_pg[i] = m3->mc_pg[i+1]; + m3->mc_ki[i] = m3->mc_ki[i+1]; + } + m3->mc_snum--; + m3->mc_top--; + } + } + } + } else + DPUTS("root page doesn't need rebalancing"); + return MDB_SUCCESS; + } + + /* The parent (branch page) must have at least 2 pointers, + * otherwise the tree is invalid. + */ + ptop = mc->mc_top-1; + mdb_cassert(mc, NUMKEYS(mc->mc_pg[ptop]) > 1); + + /* Leaf page fill factor is below the threshold. + * Try to move keys from left or right neighbor, or + * merge with a neighbor page. + */ + + /* Find neighbors. + */ + mdb_cursor_copy(mc, &mn); + mn.mc_xcursor = NULL; + + oldki = mc->mc_ki[mc->mc_top]; + if (mc->mc_ki[ptop] == 0) { + /* We're the leftmost leaf in our parent. + */ + DPUTS("reading right neighbor"); + mn.mc_ki[ptop]++; + node = NODEPTR(mc->mc_pg[ptop], mn.mc_ki[ptop]); + rc = mdb_page_get(mc, NODEPGNO(node), &mn.mc_pg[mn.mc_top], NULL); + if (rc) + return rc; + mn.mc_ki[mn.mc_top] = 0; + mc->mc_ki[mc->mc_top] = NUMKEYS(mc->mc_pg[mc->mc_top]); + fromleft = 0; + } else { + /* There is at least one neighbor to the left. + */ + DPUTS("reading left neighbor"); + mn.mc_ki[ptop]--; + node = NODEPTR(mc->mc_pg[ptop], mn.mc_ki[ptop]); + rc = mdb_page_get(mc, NODEPGNO(node), &mn.mc_pg[mn.mc_top], NULL); + if (rc) + return rc; + mn.mc_ki[mn.mc_top] = NUMKEYS(mn.mc_pg[mn.mc_top]) - 1; + mc->mc_ki[mc->mc_top] = 0; + fromleft = 1; + } + + DPRINTF(("found neighbor page %"Z"u (%u keys, %.1f%% full)", + mn.mc_pg[mn.mc_top]->mp_pgno, NUMKEYS(mn.mc_pg[mn.mc_top]), + (float)PAGEFILL(mc->mc_txn->mt_env, mn.mc_pg[mn.mc_top]) / 10)); + + /* If the neighbor page is above threshold and has enough keys, + * move one key from it. Otherwise we should try to merge them. + * (A branch page must never have less than 2 keys.) + */ + if (PAGEFILL(mc->mc_txn->mt_env, mn.mc_pg[mn.mc_top]) >= thresh && NUMKEYS(mn.mc_pg[mn.mc_top]) > minkeys) { + rc = mdb_node_move(&mn, mc, fromleft); + if (fromleft) { + /* if we inserted on left, bump position up */ + oldki++; + } + } else { + if (!fromleft) { + rc = mdb_page_merge(&mn, mc); + } else { + oldki += NUMKEYS(mn.mc_pg[mn.mc_top]); + mn.mc_ki[mn.mc_top] += mc->mc_ki[mn.mc_top] + 1; + /* We want mdb_rebalance to find mn when doing fixups */ + WITH_CURSOR_TRACKING(mn, + rc = mdb_page_merge(mc, &mn)); + mdb_cursor_copy(&mn, mc); + } + mc->mc_flags &= ~C_EOF; + } + mc->mc_ki[mc->mc_top] = oldki; + return rc; +} + +/** Complete a delete operation started by #mdb_cursor_del(). */ +static int +mdb_cursor_del0(MDB_cursor *mc) +{ + int rc; + MDB_page *mp; + indx_t ki; + unsigned int nkeys; + MDB_cursor *m2, *m3; + MDB_dbi dbi = mc->mc_dbi; + + ki = mc->mc_ki[mc->mc_top]; + mp = mc->mc_pg[mc->mc_top]; + mdb_node_del(mc, mc->mc_db->md_pad); + mc->mc_db->md_entries--; + { + /* Adjust other cursors pointing to mp */ + for (m2 = mc->mc_txn->mt_cursors[dbi]; m2; m2=m2->mc_next) { + m3 = (mc->mc_flags & C_SUB) ? &m2->mc_xcursor->mx_cursor : m2; + if (! (m2->mc_flags & m3->mc_flags & C_INITIALIZED)) + continue; + if (m3 == mc || m3->mc_snum < mc->mc_snum) + continue; + if (m3->mc_pg[mc->mc_top] == mp) { + if (m3->mc_ki[mc->mc_top] == ki) { + m3->mc_flags |= C_DEL; + if (mc->mc_db->md_flags & MDB_DUPSORT) { + /* Sub-cursor referred into dataset which is gone */ + m3->mc_xcursor->mx_cursor.mc_flags &= ~(C_INITIALIZED|C_EOF); + } + continue; + } else if (m3->mc_ki[mc->mc_top] > ki) { + m3->mc_ki[mc->mc_top]--; + } + XCURSOR_REFRESH(m3, mc->mc_top, mp); + } + } + } + rc = mdb_rebalance(mc); + + if (rc == MDB_SUCCESS) { + /* DB is totally empty now, just bail out. + * Other cursors adjustments were already done + * by mdb_rebalance and aren't needed here. + */ + if (!mc->mc_snum) + return rc; + + mp = mc->mc_pg[mc->mc_top]; + nkeys = NUMKEYS(mp); + + /* Adjust other cursors pointing to mp */ + for (m2 = mc->mc_txn->mt_cursors[dbi]; !rc && m2; m2=m2->mc_next) { + m3 = (mc->mc_flags & C_SUB) ? &m2->mc_xcursor->mx_cursor : m2; + if (! (m2->mc_flags & m3->mc_flags & C_INITIALIZED)) + continue; + if (m3->mc_snum < mc->mc_snum) + continue; + if (m3->mc_pg[mc->mc_top] == mp) { + /* if m3 points past last node in page, find next sibling */ + if (m3->mc_ki[mc->mc_top] >= mc->mc_ki[mc->mc_top]) { + if (m3->mc_ki[mc->mc_top] >= nkeys) { + rc = mdb_cursor_sibling(m3, 1); + if (rc == MDB_NOTFOUND) { + m3->mc_flags |= C_EOF; + rc = MDB_SUCCESS; + continue; + } + } + if (mc->mc_db->md_flags & MDB_DUPSORT) { + MDB_node *node = NODEPTR(m3->mc_pg[m3->mc_top], m3->mc_ki[m3->mc_top]); + /* If this node has dupdata, it may need to be reinited + * because its data has moved. + * If the xcursor was not initd it must be reinited. + * Else if node points to a subDB, nothing is needed. + * Else (xcursor was initd, not a subDB) needs mc_pg[0] reset. + */ + if (node->mn_flags & F_DUPDATA) { + if (m3->mc_xcursor->mx_cursor.mc_flags & C_INITIALIZED) { + if (!(node->mn_flags & F_SUBDATA)) + m3->mc_xcursor->mx_cursor.mc_pg[0] = NODEDATA(node); + } else { + mdb_xcursor_init1(m3, node); + m3->mc_xcursor->mx_cursor.mc_flags |= C_DEL; + } + } + } + } + } + } + mc->mc_flags |= C_DEL; + } + + if (rc) + mc->mc_txn->mt_flags |= MDB_TXN_ERROR; + return rc; +} + +int +mdb_del(MDB_txn *txn, MDB_dbi dbi, + MDB_val *key, MDB_val *data) +{ + if (!key || !TXN_DBI_EXIST(txn, dbi, DB_USRVALID)) + return EINVAL; + + if (txn->mt_flags & (MDB_TXN_RDONLY|MDB_TXN_BLOCKED)) + return (txn->mt_flags & MDB_TXN_RDONLY) ? EACCES : MDB_BAD_TXN; + + if (!F_ISSET(txn->mt_dbs[dbi].md_flags, MDB_DUPSORT)) { + /* must ignore any data */ + data = NULL; + } + + return mdb_del0(txn, dbi, key, data, 0); +} + +static int +mdb_del0(MDB_txn *txn, MDB_dbi dbi, + MDB_val *key, MDB_val *data, unsigned flags) +{ + MDB_cursor mc; + MDB_xcursor mx; + MDB_cursor_op op; + MDB_val rdata, *xdata; + int rc, exact = 0; + DKBUF; + + DPRINTF(("====> delete db %u key [%s]", dbi, DKEY(key))); + + mdb_cursor_init(&mc, txn, dbi, &mx); + + if (data) { + op = MDB_GET_BOTH; + rdata = *data; + xdata = &rdata; + } else { + op = MDB_SET; + xdata = NULL; + flags |= MDB_NODUPDATA; + } + rc = mdb_cursor_set(&mc, key, xdata, op, &exact); + if (rc == 0) { + /* let mdb_page_split know about this cursor if needed: + * delete will trigger a rebalance; if it needs to move + * a node from one page to another, it will have to + * update the parent's separator key(s). If the new sepkey + * is larger than the current one, the parent page may + * run out of space, triggering a split. We need this + * cursor to be consistent until the end of the rebalance. + */ + mc.mc_flags |= C_UNTRACK; + mc.mc_next = txn->mt_cursors[dbi]; + txn->mt_cursors[dbi] = &mc; + rc = mdb_cursor_del(&mc, flags); + txn->mt_cursors[dbi] = mc.mc_next; + } + return rc; +} + +/** Split a page and insert a new node. + * Set #MDB_TXN_ERROR on failure. + * @param[in,out] mc Cursor pointing to the page and desired insertion index. + * The cursor will be updated to point to the actual page and index where + * the node got inserted after the split. + * @param[in] newkey The key for the newly inserted node. + * @param[in] newdata The data for the newly inserted node. + * @param[in] newpgno The page number, if the new node is a branch node. + * @param[in] nflags The #NODE_ADD_FLAGS for the new node. + * @return 0 on success, non-zero on failure. + */ +static int +mdb_page_split(MDB_cursor *mc, MDB_val *newkey, MDB_val *newdata, pgno_t newpgno, + unsigned int nflags) +{ + unsigned int flags; + int rc = MDB_SUCCESS, new_root = 0, did_split = 0; + indx_t newindx; + pgno_t pgno = 0; + int i, j, split_indx, nkeys, pmax; + MDB_env *env = mc->mc_txn->mt_env; + MDB_node *node; + MDB_val sepkey, rkey, xdata, *rdata = &xdata; + MDB_page *copy = NULL; + MDB_page *mp, *rp, *pp; + int ptop; + MDB_cursor mn; + DKBUF; + + mp = mc->mc_pg[mc->mc_top]; + newindx = mc->mc_ki[mc->mc_top]; + nkeys = NUMKEYS(mp); + + DPRINTF(("-----> splitting %s page %"Z"u and adding [%s] at index %i/%i", + IS_LEAF(mp) ? "leaf" : "branch", mp->mp_pgno, + DKEY(newkey), mc->mc_ki[mc->mc_top], nkeys)); + + /* Create a right sibling. */ + if ((rc = mdb_page_new(mc, mp->mp_flags, 1, &rp))) + return rc; + rp->mp_pad = mp->mp_pad; + DPRINTF(("new right sibling: page %"Z"u", rp->mp_pgno)); + + /* Usually when splitting the root page, the cursor + * height is 1. But when called from mdb_update_key, + * the cursor height may be greater because it walks + * up the stack while finding the branch slot to update. + */ + if (mc->mc_top < 1) { + if ((rc = mdb_page_new(mc, P_BRANCH, 1, &pp))) + goto done; + /* shift current top to make room for new parent */ + for (i=mc->mc_snum; i>0; i--) { + mc->mc_pg[i] = mc->mc_pg[i-1]; + mc->mc_ki[i] = mc->mc_ki[i-1]; + } + mc->mc_pg[0] = pp; + mc->mc_ki[0] = 0; + mc->mc_db->md_root = pp->mp_pgno; + DPRINTF(("root split! new root = %"Z"u", pp->mp_pgno)); + new_root = mc->mc_db->md_depth++; + + /* Add left (implicit) pointer. */ + if ((rc = mdb_node_add(mc, 0, NULL, NULL, mp->mp_pgno, 0)) != MDB_SUCCESS) { + /* undo the pre-push */ + mc->mc_pg[0] = mc->mc_pg[1]; + mc->mc_ki[0] = mc->mc_ki[1]; + mc->mc_db->md_root = mp->mp_pgno; + mc->mc_db->md_depth--; + goto done; + } + mc->mc_snum++; + mc->mc_top++; + ptop = 0; + } else { + ptop = mc->mc_top-1; + DPRINTF(("parent branch page is %"Z"u", mc->mc_pg[ptop]->mp_pgno)); + } + + mdb_cursor_copy(mc, &mn); + mn.mc_xcursor = NULL; + mn.mc_pg[mn.mc_top] = rp; + mn.mc_ki[ptop] = mc->mc_ki[ptop]+1; + + if (nflags & MDB_APPEND) { + mn.mc_ki[mn.mc_top] = 0; + sepkey = *newkey; + split_indx = newindx; + nkeys = 0; + } else { + + split_indx = (nkeys+1) / 2; + + if (IS_LEAF2(rp)) { + char *split, *ins; + int x; + unsigned int lsize, rsize, ksize; + /* Move half of the keys to the right sibling */ + x = mc->mc_ki[mc->mc_top] - split_indx; + ksize = mc->mc_db->md_pad; + split = LEAF2KEY(mp, split_indx, ksize); + rsize = (nkeys - split_indx) * ksize; + lsize = (nkeys - split_indx) * sizeof(indx_t); + mp->mp_lower -= lsize; + rp->mp_lower += lsize; + mp->mp_upper += rsize - lsize; + rp->mp_upper -= rsize - lsize; + sepkey.mv_size = ksize; + if (newindx == split_indx) { + sepkey.mv_data = newkey->mv_data; + } else { + sepkey.mv_data = split; + } + if (x<0) { + ins = LEAF2KEY(mp, mc->mc_ki[mc->mc_top], ksize); + memcpy(rp->mp_ptrs, split, rsize); + sepkey.mv_data = rp->mp_ptrs; + memmove(ins+ksize, ins, (split_indx - mc->mc_ki[mc->mc_top]) * ksize); + memcpy(ins, newkey->mv_data, ksize); + mp->mp_lower += sizeof(indx_t); + mp->mp_upper -= ksize - sizeof(indx_t); + } else { + if (x) + memcpy(rp->mp_ptrs, split, x * ksize); + ins = LEAF2KEY(rp, x, ksize); + memcpy(ins, newkey->mv_data, ksize); + memcpy(ins+ksize, split + x * ksize, rsize - x * ksize); + rp->mp_lower += sizeof(indx_t); + rp->mp_upper -= ksize - sizeof(indx_t); + mc->mc_ki[mc->mc_top] = x; + } + } else { + int psize, nsize, k; + /* Maximum free space in an empty page */ + pmax = env->me_psize - PAGEHDRSZ; + if (IS_LEAF(mp)) + nsize = mdb_leaf_size(env, newkey, newdata); + else + nsize = mdb_branch_size(env, newkey); + nsize = EVEN(nsize); + + /* grab a page to hold a temporary copy */ + copy = mdb_page_malloc(mc->mc_txn, 1); + if (copy == NULL) { + rc = ENOMEM; + goto done; + } + copy->mp_pgno = mp->mp_pgno; + copy->mp_flags = mp->mp_flags; + copy->mp_lower = (PAGEHDRSZ-PAGEBASE); + copy->mp_upper = env->me_psize - PAGEBASE; + + /* prepare to insert */ + for (i=0, j=0; i<nkeys; i++) { + if (i == newindx) { + copy->mp_ptrs[j++] = 0; + } + copy->mp_ptrs[j++] = mp->mp_ptrs[i]; + } + + /* When items are relatively large the split point needs + * to be checked, because being off-by-one will make the + * difference between success or failure in mdb_node_add. + * + * It's also relevant if a page happens to be laid out + * such that one half of its nodes are all "small" and + * the other half of its nodes are "large." If the new + * item is also "large" and falls on the half with + * "large" nodes, it also may not fit. + * + * As a final tweak, if the new item goes on the last + * spot on the page (and thus, onto the new page), bias + * the split so the new page is emptier than the old page. + * This yields better packing during sequential inserts. + */ + if (nkeys < 20 || nsize > pmax/16 || newindx >= nkeys) { + /* Find split point */ + psize = 0; + if (newindx <= split_indx || newindx >= nkeys) { + i = 0; j = 1; + k = newindx >= nkeys ? nkeys : split_indx+1+IS_LEAF(mp); + } else { + i = nkeys; j = -1; + k = split_indx-1; + } + for (; i!=k; i+=j) { + if (i == newindx) { + psize += nsize; + node = NULL; + } else { + node = (MDB_node *)((char *)mp + copy->mp_ptrs[i] + PAGEBASE); + psize += NODESIZE + NODEKSZ(node) + sizeof(indx_t); + if (IS_LEAF(mp)) { + if (F_ISSET(node->mn_flags, F_BIGDATA)) + psize += sizeof(pgno_t); + else + psize += NODEDSZ(node); + } + psize = EVEN(psize); + } + if (psize > pmax || i == k-j) { + split_indx = i + (j<0); + break; + } + } + } + if (split_indx == newindx) { + sepkey.mv_size = newkey->mv_size; + sepkey.mv_data = newkey->mv_data; + } else { + node = (MDB_node *)((char *)mp + copy->mp_ptrs[split_indx] + PAGEBASE); + sepkey.mv_size = node->mn_ksize; + sepkey.mv_data = NODEKEY(node); + } + } + } + + DPRINTF(("separator is %d [%s]", split_indx, DKEY(&sepkey))); + + /* Copy separator key to the parent. + */ + if (SIZELEFT(mn.mc_pg[ptop]) < mdb_branch_size(env, &sepkey)) { + int snum = mc->mc_snum; + mn.mc_snum--; + mn.mc_top--; + did_split = 1; + /* We want other splits to find mn when doing fixups */ + WITH_CURSOR_TRACKING(mn, + rc = mdb_page_split(&mn, &sepkey, NULL, rp->mp_pgno, 0)); + if (rc) + goto done; + + /* root split? */ + if (mc->mc_snum > snum) { + ptop++; + } + /* Right page might now have changed parent. + * Check if left page also changed parent. + */ + if (mn.mc_pg[ptop] != mc->mc_pg[ptop] && + mc->mc_ki[ptop] >= NUMKEYS(mc->mc_pg[ptop])) { + for (i=0; i<ptop; i++) { + mc->mc_pg[i] = mn.mc_pg[i]; + mc->mc_ki[i] = mn.mc_ki[i]; + } + mc->mc_pg[ptop] = mn.mc_pg[ptop]; + if (mn.mc_ki[ptop]) { + mc->mc_ki[ptop] = mn.mc_ki[ptop] - 1; + } else { + /* find right page's left sibling */ + mc->mc_ki[ptop] = mn.mc_ki[ptop]; + mdb_cursor_sibling(mc, 0); + } + } + } else { + mn.mc_top--; + rc = mdb_node_add(&mn, mn.mc_ki[ptop], &sepkey, NULL, rp->mp_pgno, 0); + mn.mc_top++; + } + if (rc != MDB_SUCCESS) { + goto done; + } + if (nflags & MDB_APPEND) { + mc->mc_pg[mc->mc_top] = rp; + mc->mc_ki[mc->mc_top] = 0; + rc = mdb_node_add(mc, 0, newkey, newdata, newpgno, nflags); + if (rc) + goto done; + for (i=0; i<mc->mc_top; i++) + mc->mc_ki[i] = mn.mc_ki[i]; + } else if (!IS_LEAF2(mp)) { + /* Move nodes */ + mc->mc_pg[mc->mc_top] = rp; + i = split_indx; + j = 0; + do { + if (i == newindx) { + rkey.mv_data = newkey->mv_data; + rkey.mv_size = newkey->mv_size; + if (IS_LEAF(mp)) { + rdata = newdata; + } else + pgno = newpgno; + flags = nflags; + /* Update index for the new key. */ + mc->mc_ki[mc->mc_top] = j; + } else { + node = (MDB_node *)((char *)mp + copy->mp_ptrs[i] + PAGEBASE); + rkey.mv_data = NODEKEY(node); + rkey.mv_size = node->mn_ksize; + if (IS_LEAF(mp)) { + xdata.mv_data = NODEDATA(node); + xdata.mv_size = NODEDSZ(node); + rdata = &xdata; + } else + pgno = NODEPGNO(node); + flags = node->mn_flags; + } + + if (!IS_LEAF(mp) && j == 0) { + /* First branch index doesn't need key data. */ + rkey.mv_size = 0; + } + + rc = mdb_node_add(mc, j, &rkey, rdata, pgno, flags); + if (rc) + goto done; + if (i == nkeys) { + i = 0; + j = 0; + mc->mc_pg[mc->mc_top] = copy; + } else { + i++; + j++; + } + } while (i != split_indx); + + nkeys = NUMKEYS(copy); + for (i=0; i<nkeys; i++) + mp->mp_ptrs[i] = copy->mp_ptrs[i]; + mp->mp_lower = copy->mp_lower; + mp->mp_upper = copy->mp_upper; + memcpy(NODEPTR(mp, nkeys-1), NODEPTR(copy, nkeys-1), + env->me_psize - copy->mp_upper - PAGEBASE); + + /* reset back to original page */ + if (newindx < split_indx) { + mc->mc_pg[mc->mc_top] = mp; + } else { + mc->mc_pg[mc->mc_top] = rp; + mc->mc_ki[ptop]++; + /* Make sure mc_ki is still valid. + */ + if (mn.mc_pg[ptop] != mc->mc_pg[ptop] && + mc->mc_ki[ptop] >= NUMKEYS(mc->mc_pg[ptop])) { + for (i=0; i<=ptop; i++) { + mc->mc_pg[i] = mn.mc_pg[i]; + mc->mc_ki[i] = mn.mc_ki[i]; + } + } + } + if (nflags & MDB_RESERVE) { + node = NODEPTR(mc->mc_pg[mc->mc_top], mc->mc_ki[mc->mc_top]); + if (!(node->mn_flags & F_BIGDATA)) + newdata->mv_data = NODEDATA(node); + } + } else { + if (newindx >= split_indx) { + mc->mc_pg[mc->mc_top] = rp; + mc->mc_ki[ptop]++; + /* Make sure mc_ki is still valid. + */ + if (mn.mc_pg[ptop] != mc->mc_pg[ptop] && + mc->mc_ki[ptop] >= NUMKEYS(mc->mc_pg[ptop])) { + for (i=0; i<=ptop; i++) { + mc->mc_pg[i] = mn.mc_pg[i]; + mc->mc_ki[i] = mn.mc_ki[i]; + } + } + } + } + + { + /* Adjust other cursors pointing to mp */ + MDB_cursor *m2, *m3; + MDB_dbi dbi = mc->mc_dbi; + nkeys = NUMKEYS(mp); + + for (m2 = mc->mc_txn->mt_cursors[dbi]; m2; m2=m2->mc_next) { + if (mc->mc_flags & C_SUB) + m3 = &m2->mc_xcursor->mx_cursor; + else + m3 = m2; + if (m3 == mc) + continue; + if (!(m2->mc_flags & m3->mc_flags & C_INITIALIZED)) + continue; + if (new_root) { + int k; + /* sub cursors may be on different DB */ + if (m3->mc_pg[0] != mp) + continue; + /* root split */ + for (k=new_root; k>=0; k--) { + m3->mc_ki[k+1] = m3->mc_ki[k]; + m3->mc_pg[k+1] = m3->mc_pg[k]; + } + if (m3->mc_ki[0] >= nkeys) { + m3->mc_ki[0] = 1; + } else { + m3->mc_ki[0] = 0; + } + m3->mc_pg[0] = mc->mc_pg[0]; + m3->mc_snum++; + m3->mc_top++; + } + if (m3->mc_top >= mc->mc_top && m3->mc_pg[mc->mc_top] == mp) { + if (m3->mc_ki[mc->mc_top] >= newindx && !(nflags & MDB_SPLIT_REPLACE)) + m3->mc_ki[mc->mc_top]++; + if (m3->mc_ki[mc->mc_top] >= nkeys) { + m3->mc_pg[mc->mc_top] = rp; + m3->mc_ki[mc->mc_top] -= nkeys; + for (i=0; i<mc->mc_top; i++) { + m3->mc_ki[i] = mn.mc_ki[i]; + m3->mc_pg[i] = mn.mc_pg[i]; + } + } + } else if (!did_split && m3->mc_top >= ptop && m3->mc_pg[ptop] == mc->mc_pg[ptop] && + m3->mc_ki[ptop] >= mc->mc_ki[ptop]) { + m3->mc_ki[ptop]++; + } + if (IS_LEAF(mp)) + XCURSOR_REFRESH(m3, mc->mc_top, m3->mc_pg[mc->mc_top]); + } + } + DPRINTF(("mp left: %d, rp left: %d", SIZELEFT(mp), SIZELEFT(rp))); + +done: + if (copy) /* tmp page */ + mdb_page_free(env, copy); + if (rc) + mc->mc_txn->mt_flags |= MDB_TXN_ERROR; + return rc; +} + +int +mdb_put(MDB_txn *txn, MDB_dbi dbi, + MDB_val *key, MDB_val *data, unsigned int flags) +{ + MDB_cursor mc; + MDB_xcursor mx; + int rc; + + if (!key || !data || !TXN_DBI_EXIST(txn, dbi, DB_USRVALID)) + return EINVAL; + + if (flags & ~(MDB_NOOVERWRITE|MDB_NODUPDATA|MDB_RESERVE|MDB_APPEND|MDB_APPENDDUP)) + return EINVAL; + + if (txn->mt_flags & (MDB_TXN_RDONLY|MDB_TXN_BLOCKED)) + return (txn->mt_flags & MDB_TXN_RDONLY) ? EACCES : MDB_BAD_TXN; + + mdb_cursor_init(&mc, txn, dbi, &mx); + mc.mc_next = txn->mt_cursors[dbi]; + txn->mt_cursors[dbi] = &mc; + rc = mdb_cursor_put(&mc, key, data, flags); + txn->mt_cursors[dbi] = mc.mc_next; + return rc; +} + +#ifndef MDB_WBUF +#define MDB_WBUF (1024*1024) +#endif +#define MDB_EOF 0x10 /**< #mdb_env_copyfd1() is done reading */ + + /** State needed for a double-buffering compacting copy. */ +typedef struct mdb_copy { + MDB_env *mc_env; + MDB_txn *mc_txn; + pthread_mutex_t mc_mutex; + pthread_cond_t mc_cond; /**< Condition variable for #mc_new */ + char *mc_wbuf[2]; + char *mc_over[2]; + int mc_wlen[2]; + int mc_olen[2]; + pgno_t mc_next_pgno; + HANDLE mc_fd; + int mc_toggle; /**< Buffer number in provider */ + int mc_new; /**< (0-2 buffers to write) | (#MDB_EOF at end) */ + /** Error code. Never cleared if set. Both threads can set nonzero + * to fail the copy. Not mutex-protected, LMDB expects atomic int. + */ + volatile int mc_error; +} mdb_copy; + + /** Dedicated writer thread for compacting copy. */ +static THREAD_RET ESECT CALL_CONV +mdb_env_copythr(void *arg) +{ + mdb_copy *my = arg; + char *ptr; + int toggle = 0, wsize, rc; +#ifdef _WIN32 + DWORD len; +#define DO_WRITE(rc, fd, ptr, w2, len) rc = WriteFile(fd, ptr, w2, &len, NULL) +#else + int len; +#define DO_WRITE(rc, fd, ptr, w2, len) len = write(fd, ptr, w2); rc = (len >= 0) +#ifdef SIGPIPE + sigset_t set; + sigemptyset(&set); + sigaddset(&set, SIGPIPE); + if ((rc = pthread_sigmask(SIG_BLOCK, &set, NULL)) != 0) + my->mc_error = rc; +#endif +#endif + + pthread_mutex_lock(&my->mc_mutex); + for(;;) { + while (!my->mc_new) + pthread_cond_wait(&my->mc_cond, &my->mc_mutex); + if (my->mc_new == 0 + MDB_EOF) /* 0 buffers, just EOF */ + break; + wsize = my->mc_wlen[toggle]; + ptr = my->mc_wbuf[toggle]; +again: + rc = MDB_SUCCESS; + while (wsize > 0 && !my->mc_error) { + DO_WRITE(rc, my->mc_fd, ptr, wsize, len); + if (!rc) { + rc = ErrCode(); +#if defined(SIGPIPE) && !defined(_WIN32) + if (rc == EPIPE) { + /* Collect the pending SIGPIPE, otherwise at least OS X + * gives it to the process on thread-exit (ITS#8504). + */ + int tmp; + sigwait(&set, &tmp); + } +#endif + break; + } else if (len > 0) { + rc = MDB_SUCCESS; + ptr += len; + wsize -= len; + continue; + } else { + rc = EIO; + break; + } + } + if (rc) { + my->mc_error = rc; + } + /* If there's an overflow page tail, write it too */ + if (my->mc_olen[toggle]) { + wsize = my->mc_olen[toggle]; + ptr = my->mc_over[toggle]; + my->mc_olen[toggle] = 0; + goto again; + } + my->mc_wlen[toggle] = 0; + toggle ^= 1; + /* Return the empty buffer to provider */ + my->mc_new--; + pthread_cond_signal(&my->mc_cond); + } + pthread_mutex_unlock(&my->mc_mutex); + return (THREAD_RET)0; +#undef DO_WRITE +} + + /** Give buffer and/or #MDB_EOF to writer thread, await unused buffer. + * + * @param[in] my control structure. + * @param[in] adjust (1 to hand off 1 buffer) | (MDB_EOF when ending). + */ +static int ESECT +mdb_env_cthr_toggle(mdb_copy *my, int adjust) +{ + pthread_mutex_lock(&my->mc_mutex); + my->mc_new += adjust; + pthread_cond_signal(&my->mc_cond); + while (my->mc_new & 2) /* both buffers in use */ + pthread_cond_wait(&my->mc_cond, &my->mc_mutex); + pthread_mutex_unlock(&my->mc_mutex); + + my->mc_toggle ^= (adjust & 1); + /* Both threads reset mc_wlen, to be safe from threading errors */ + my->mc_wlen[my->mc_toggle] = 0; + return my->mc_error; +} + + /** Depth-first tree traversal for compacting copy. + * @param[in] my control structure. + * @param[in,out] pg database root. + * @param[in] flags includes #F_DUPDATA if it is a sorted-duplicate sub-DB. + */ +static int ESECT +mdb_env_cwalk(mdb_copy *my, pgno_t *pg, int flags) +{ + MDB_cursor mc = {0}; + MDB_node *ni; + MDB_page *mo, *mp, *leaf; + char *buf, *ptr; + int rc, toggle; + unsigned int i; + + /* Empty DB, nothing to do */ + if (*pg == P_INVALID) + return MDB_SUCCESS; + + mc.mc_snum = 1; + mc.mc_txn = my->mc_txn; + + rc = mdb_page_get(&mc, *pg, &mc.mc_pg[0], NULL); + if (rc) + return rc; + rc = mdb_page_search_root(&mc, NULL, MDB_PS_FIRST); + if (rc) + return rc; + + /* Make cursor pages writable */ + buf = ptr = malloc(my->mc_env->me_psize * mc.mc_snum); + if (buf == NULL) + return ENOMEM; + + for (i=0; i<mc.mc_top; i++) { + mdb_page_copy((MDB_page *)ptr, mc.mc_pg[i], my->mc_env->me_psize); + mc.mc_pg[i] = (MDB_page *)ptr; + ptr += my->mc_env->me_psize; + } + + /* This is writable space for a leaf page. Usually not needed. */ + leaf = (MDB_page *)ptr; + + toggle = my->mc_toggle; + while (mc.mc_snum > 0) { + unsigned n; + mp = mc.mc_pg[mc.mc_top]; + n = NUMKEYS(mp); + + if (IS_LEAF(mp)) { + if (!IS_LEAF2(mp) && !(flags & F_DUPDATA)) { + for (i=0; i<n; i++) { + ni = NODEPTR(mp, i); + if (ni->mn_flags & F_BIGDATA) { + MDB_page *omp; + pgno_t pg; + + /* Need writable leaf */ + if (mp != leaf) { + mc.mc_pg[mc.mc_top] = leaf; + mdb_page_copy(leaf, mp, my->mc_env->me_psize); + mp = leaf; + ni = NODEPTR(mp, i); + } + + memcpy(&pg, NODEDATA(ni), sizeof(pg)); + memcpy(NODEDATA(ni), &my->mc_next_pgno, sizeof(pgno_t)); + rc = mdb_page_get(&mc, pg, &omp, NULL); + if (rc) + goto done; + if (my->mc_wlen[toggle] >= MDB_WBUF) { + rc = mdb_env_cthr_toggle(my, 1); + if (rc) + goto done; + toggle = my->mc_toggle; + } + mo = (MDB_page *)(my->mc_wbuf[toggle] + my->mc_wlen[toggle]); + memcpy(mo, omp, my->mc_env->me_psize); + mo->mp_pgno = my->mc_next_pgno; + my->mc_next_pgno += omp->mp_pages; + my->mc_wlen[toggle] += my->mc_env->me_psize; + if (omp->mp_pages > 1) { + my->mc_olen[toggle] = my->mc_env->me_psize * (omp->mp_pages - 1); + my->mc_over[toggle] = (char *)omp + my->mc_env->me_psize; + rc = mdb_env_cthr_toggle(my, 1); + if (rc) + goto done; + toggle = my->mc_toggle; + } + } else if (ni->mn_flags & F_SUBDATA) { + MDB_db db; + + /* Need writable leaf */ + if (mp != leaf) { + mc.mc_pg[mc.mc_top] = leaf; + mdb_page_copy(leaf, mp, my->mc_env->me_psize); + mp = leaf; + ni = NODEPTR(mp, i); + } + + memcpy(&db, NODEDATA(ni), sizeof(db)); + my->mc_toggle = toggle; + rc = mdb_env_cwalk(my, &db.md_root, ni->mn_flags & F_DUPDATA); + if (rc) + goto done; + toggle = my->mc_toggle; + memcpy(NODEDATA(ni), &db, sizeof(db)); + } + } + } + } else { + mc.mc_ki[mc.mc_top]++; + if (mc.mc_ki[mc.mc_top] < n) { + pgno_t pg; +again: + ni = NODEPTR(mp, mc.mc_ki[mc.mc_top]); + pg = NODEPGNO(ni); + rc = mdb_page_get(&mc, pg, &mp, NULL); + if (rc) + goto done; + mc.mc_top++; + mc.mc_snum++; + mc.mc_ki[mc.mc_top] = 0; + if (IS_BRANCH(mp)) { + /* Whenever we advance to a sibling branch page, + * we must proceed all the way down to its first leaf. + */ + mdb_page_copy(mc.mc_pg[mc.mc_top], mp, my->mc_env->me_psize); + goto again; + } else + mc.mc_pg[mc.mc_top] = mp; + continue; + } + } + if (my->mc_wlen[toggle] >= MDB_WBUF) { + rc = mdb_env_cthr_toggle(my, 1); + if (rc) + goto done; + toggle = my->mc_toggle; + } + mo = (MDB_page *)(my->mc_wbuf[toggle] + my->mc_wlen[toggle]); + mdb_page_copy(mo, mp, my->mc_env->me_psize); + mo->mp_pgno = my->mc_next_pgno++; + my->mc_wlen[toggle] += my->mc_env->me_psize; + if (mc.mc_top) { + /* Update parent if there is one */ + ni = NODEPTR(mc.mc_pg[mc.mc_top-1], mc.mc_ki[mc.mc_top-1]); + SETPGNO(ni, mo->mp_pgno); + mdb_cursor_pop(&mc); + } else { + /* Otherwise we're done */ + *pg = mo->mp_pgno; + break; + } + } +done: + free(buf); + return rc; +} + + /** Copy environment with compaction. */ +static int ESECT +mdb_env_copyfd1(MDB_env *env, HANDLE fd) +{ + MDB_meta *mm; + MDB_page *mp; + mdb_copy my = {0}; + MDB_txn *txn = NULL; + pthread_t thr; + pgno_t root, new_root; + int rc = MDB_SUCCESS; + +#ifdef _WIN32 + if (!(my.mc_mutex = CreateMutex(NULL, FALSE, NULL)) || + !(my.mc_cond = CreateEvent(NULL, FALSE, FALSE, NULL))) { + rc = ErrCode(); + goto done; + } + my.mc_wbuf[0] = _aligned_malloc(MDB_WBUF*2, env->me_os_psize); + if (my.mc_wbuf[0] == NULL) { + /* _aligned_malloc() sets errno, but we use Windows error codes */ + rc = ERROR_NOT_ENOUGH_MEMORY; + goto done; + } +#else + if ((rc = pthread_mutex_init(&my.mc_mutex, NULL)) != 0) + return rc; + if ((rc = pthread_cond_init(&my.mc_cond, NULL)) != 0) + goto done2; +#ifdef HAVE_MEMALIGN + my.mc_wbuf[0] = memalign(env->me_os_psize, MDB_WBUF*2); + if (my.mc_wbuf[0] == NULL) { + rc = errno; + goto done; + } +#else + { + void *p; + if ((rc = posix_memalign(&p, env->me_os_psize, MDB_WBUF*2)) != 0) + goto done; + my.mc_wbuf[0] = p; + } +#endif +#endif + memset(my.mc_wbuf[0], 0, MDB_WBUF*2); + my.mc_wbuf[1] = my.mc_wbuf[0] + MDB_WBUF; + my.mc_next_pgno = NUM_METAS; + my.mc_env = env; + my.mc_fd = fd; + rc = THREAD_CREATE(thr, mdb_env_copythr, &my); + if (rc) + goto done; + + rc = mdb_txn_begin(env, NULL, MDB_RDONLY, &txn); + if (rc) + goto finish; + + mp = (MDB_page *)my.mc_wbuf[0]; + memset(mp, 0, NUM_METAS * env->me_psize); + mp->mp_pgno = 0; + mp->mp_flags = P_META; + mm = (MDB_meta *)METADATA(mp); + mdb_env_init_meta0(env, mm); + mm->mm_address = env->me_metas[0]->mm_address; + + mp = (MDB_page *)(my.mc_wbuf[0] + env->me_psize); + mp->mp_pgno = 1; + mp->mp_flags = P_META; + *(MDB_meta *)METADATA(mp) = *mm; + mm = (MDB_meta *)METADATA(mp); + + /* Set metapage 1 with current main DB */ + root = new_root = txn->mt_dbs[MAIN_DBI].md_root; + if (root != P_INVALID) { + /* Count free pages + freeDB pages. Subtract from last_pg + * to find the new last_pg, which also becomes the new root. + */ + MDB_ID freecount = 0; + MDB_cursor mc; + MDB_val key, data; + mdb_cursor_init(&mc, txn, FREE_DBI, NULL); + while ((rc = mdb_cursor_get(&mc, &key, &data, MDB_NEXT)) == 0) + freecount += *(MDB_ID *)data.mv_data; + if (rc != MDB_NOTFOUND) + goto finish; + freecount += txn->mt_dbs[FREE_DBI].md_branch_pages + + txn->mt_dbs[FREE_DBI].md_leaf_pages + + txn->mt_dbs[FREE_DBI].md_overflow_pages; + + new_root = txn->mt_next_pgno - 1 - freecount; + mm->mm_last_pg = new_root; + mm->mm_dbs[MAIN_DBI] = txn->mt_dbs[MAIN_DBI]; + mm->mm_dbs[MAIN_DBI].md_root = new_root; + } else { + /* When the DB is empty, handle it specially to + * fix any breakage like page leaks from ITS#8174. + */ + mm->mm_dbs[MAIN_DBI].md_flags = txn->mt_dbs[MAIN_DBI].md_flags; + } + if (root != P_INVALID || mm->mm_dbs[MAIN_DBI].md_flags) { + mm->mm_txnid = 1; /* use metapage 1 */ + } + + my.mc_wlen[0] = env->me_psize * NUM_METAS; + my.mc_txn = txn; + rc = mdb_env_cwalk(&my, &root, 0); + if (rc == MDB_SUCCESS && root != new_root) { + rc = MDB_INCOMPATIBLE; /* page leak or corrupt DB */ + } + +finish: + if (rc) + my.mc_error = rc; + mdb_env_cthr_toggle(&my, 1 | MDB_EOF); + rc = THREAD_FINISH(thr); + mdb_txn_abort(txn); + +done: +#ifdef _WIN32 + if (my.mc_wbuf[0]) _aligned_free(my.mc_wbuf[0]); + if (my.mc_cond) CloseHandle(my.mc_cond); + if (my.mc_mutex) CloseHandle(my.mc_mutex); +#else + free(my.mc_wbuf[0]); + pthread_cond_destroy(&my.mc_cond); +done2: + pthread_mutex_destroy(&my.mc_mutex); +#endif + return rc ? rc : my.mc_error; +} + + /** Copy environment as-is. */ +static int ESECT +mdb_env_copyfd0(MDB_env *env, HANDLE fd) +{ + MDB_txn *txn = NULL; + mdb_mutexref_t wmutex = NULL; + int rc; + size_t wsize, w3; + char *ptr; +#ifdef _WIN32 + DWORD len, w2; +#define DO_WRITE(rc, fd, ptr, w2, len) rc = WriteFile(fd, ptr, w2, &len, NULL) +#else + ssize_t len; + size_t w2; +#define DO_WRITE(rc, fd, ptr, w2, len) len = write(fd, ptr, w2); rc = (len >= 0) +#endif + + /* Do the lock/unlock of the reader mutex before starting the + * write txn. Otherwise other read txns could block writers. + */ + rc = mdb_txn_begin(env, NULL, MDB_RDONLY, &txn); + if (rc) + return rc; + + if (env->me_txns) { + /* We must start the actual read txn after blocking writers */ + mdb_txn_end(txn, MDB_END_RESET_TMP); + + /* Temporarily block writers until we snapshot the meta pages */ + wmutex = env->me_wmutex; + if (LOCK_MUTEX(rc, env, wmutex)) + goto leave; + + rc = mdb_txn_renew0(txn); + if (rc) { + UNLOCK_MUTEX(wmutex); + goto leave; + } + } + + wsize = env->me_psize * NUM_METAS; + ptr = env->me_map; + w2 = wsize; + while (w2 > 0) { + DO_WRITE(rc, fd, ptr, w2, len); + if (!rc) { + rc = ErrCode(); + break; + } else if (len > 0) { + rc = MDB_SUCCESS; + ptr += len; + w2 -= len; + continue; + } else { + /* Non-blocking or async handles are not supported */ + rc = EIO; + break; + } + } + if (wmutex) + UNLOCK_MUTEX(wmutex); + + if (rc) + goto leave; + + w3 = txn->mt_next_pgno * env->me_psize; + { + size_t fsize = 0; + if ((rc = mdb_fsize(env->me_fd, &fsize))) + goto leave; + if (w3 > fsize) + w3 = fsize; + } + wsize = w3 - wsize; + while (wsize > 0) { + if (wsize > MAX_WRITE) + w2 = MAX_WRITE; + else + w2 = wsize; + DO_WRITE(rc, fd, ptr, w2, len); + if (!rc) { + rc = ErrCode(); + break; + } else if (len > 0) { + rc = MDB_SUCCESS; + ptr += len; + wsize -= len; + continue; + } else { + rc = EIO; + break; + } + } + +leave: + mdb_txn_abort(txn); + return rc; +} + +int ESECT +mdb_env_copyfd2(MDB_env *env, HANDLE fd, unsigned int flags) +{ + if (flags & MDB_CP_COMPACT) + return mdb_env_copyfd1(env, fd); + else + return mdb_env_copyfd0(env, fd); +} + +int ESECT +mdb_env_copyfd(MDB_env *env, HANDLE fd) +{ + return mdb_env_copyfd2(env, fd, 0); +} + +int ESECT +mdb_env_copy2(MDB_env *env, const char *path, unsigned int flags) +{ + int rc; + MDB_name fname; + HANDLE newfd = INVALID_HANDLE_VALUE; + + rc = mdb_fname_init(path, env->me_flags | MDB_NOLOCK, &fname); + if (rc == MDB_SUCCESS) { + rc = mdb_fopen(env, &fname, MDB_O_COPY, 0666, &newfd); + mdb_fname_destroy(fname); + } + if (rc == MDB_SUCCESS) { + rc = mdb_env_copyfd2(env, newfd, flags); + if (close(newfd) < 0 && rc == MDB_SUCCESS) + rc = ErrCode(); + } + return rc; +} + +int ESECT +mdb_env_copy(MDB_env *env, const char *path) +{ + return mdb_env_copy2(env, path, 0); +} + +int ESECT +mdb_env_set_flags(MDB_env *env, unsigned int flag, int onoff) +{ + if (flag & ~CHANGEABLE) + return EINVAL; + if (onoff) + env->me_flags |= flag; + else + env->me_flags &= ~flag; + return MDB_SUCCESS; +} + +int ESECT +mdb_env_get_flags(MDB_env *env, unsigned int *arg) +{ + if (!env || !arg) + return EINVAL; + + *arg = env->me_flags & (CHANGEABLE|CHANGELESS); + return MDB_SUCCESS; +} + +int ESECT +mdb_env_set_userctx(MDB_env *env, void *ctx) +{ + if (!env) + return EINVAL; + env->me_userctx = ctx; + return MDB_SUCCESS; +} + +void * ESECT +mdb_env_get_userctx(MDB_env *env) +{ + return env ? env->me_userctx : NULL; +} + +int ESECT +mdb_env_set_assert(MDB_env *env, MDB_assert_func *func) +{ + if (!env) + return EINVAL; +#ifndef NDEBUG + env->me_assert_func = func; +#endif + return MDB_SUCCESS; +} + +int ESECT +mdb_env_get_path(MDB_env *env, const char **arg) +{ + if (!env || !arg) + return EINVAL; + + *arg = env->me_path; + return MDB_SUCCESS; +} + +int ESECT +mdb_env_get_fd(MDB_env *env, mdb_filehandle_t *arg) +{ + if (!env || !arg) + return EINVAL; + + *arg = env->me_fd; + return MDB_SUCCESS; +} + +/** Common code for #mdb_stat() and #mdb_env_stat(). + * @param[in] env the environment to operate in. + * @param[in] db the #MDB_db record containing the stats to return. + * @param[out] arg the address of an #MDB_stat structure to receive the stats. + * @return 0, this function always succeeds. + */ +static int ESECT +mdb_stat0(MDB_env *env, MDB_db *db, MDB_stat *arg) +{ + arg->ms_psize = env->me_psize; + arg->ms_depth = db->md_depth; + arg->ms_branch_pages = db->md_branch_pages; + arg->ms_leaf_pages = db->md_leaf_pages; + arg->ms_overflow_pages = db->md_overflow_pages; + arg->ms_entries = db->md_entries; + + return MDB_SUCCESS; +} + +int ESECT +mdb_env_stat(MDB_env *env, MDB_stat *arg) +{ + MDB_meta *meta; + + if (env == NULL || arg == NULL) + return EINVAL; + + meta = mdb_env_pick_meta(env); + + return mdb_stat0(env, &meta->mm_dbs[MAIN_DBI], arg); +} + +int ESECT +mdb_env_info(MDB_env *env, MDB_envinfo *arg) +{ + MDB_meta *meta; + + if (env == NULL || arg == NULL) + return EINVAL; + + meta = mdb_env_pick_meta(env); + arg->me_mapaddr = meta->mm_address; + arg->me_last_pgno = meta->mm_last_pg; + arg->me_last_txnid = meta->mm_txnid; + + arg->me_mapsize = env->me_mapsize; + arg->me_maxreaders = env->me_maxreaders; + arg->me_numreaders = env->me_txns ? env->me_txns->mti_numreaders : 0; + return MDB_SUCCESS; +} + +/** Set the default comparison functions for a database. + * Called immediately after a database is opened to set the defaults. + * The user can then override them with #mdb_set_compare() or + * #mdb_set_dupsort(). + * @param[in] txn A transaction handle returned by #mdb_txn_begin() + * @param[in] dbi A database handle returned by #mdb_dbi_open() + */ +static void +mdb_default_cmp(MDB_txn *txn, MDB_dbi dbi) +{ + uint16_t f = txn->mt_dbs[dbi].md_flags; + + txn->mt_dbxs[dbi].md_cmp = + (f & MDB_REVERSEKEY) ? mdb_cmp_memnr : + (f & MDB_INTEGERKEY) ? mdb_cmp_cint : mdb_cmp_memn; + + txn->mt_dbxs[dbi].md_dcmp = + !(f & MDB_DUPSORT) ? 0 : + ((f & MDB_INTEGERDUP) + ? ((f & MDB_DUPFIXED) ? mdb_cmp_int : mdb_cmp_cint) + : ((f & MDB_REVERSEDUP) ? mdb_cmp_memnr : mdb_cmp_memn)); +} + +int mdb_dbi_open(MDB_txn *txn, const char *name, unsigned int flags, MDB_dbi *dbi) +{ + MDB_val key, data; + MDB_dbi i; + MDB_cursor mc; + MDB_db dummy; + int rc, dbflag, exact; + unsigned int unused = 0, seq; + char *namedup; + size_t len; + + if (flags & ~VALID_FLAGS) + return EINVAL; + if (txn->mt_flags & MDB_TXN_BLOCKED) + return MDB_BAD_TXN; + + /* main DB? */ + if (!name) { + *dbi = MAIN_DBI; + if (flags & PERSISTENT_FLAGS) { + uint16_t f2 = flags & PERSISTENT_FLAGS; + /* make sure flag changes get committed */ + if ((txn->mt_dbs[MAIN_DBI].md_flags | f2) != txn->mt_dbs[MAIN_DBI].md_flags) { + txn->mt_dbs[MAIN_DBI].md_flags |= f2; + txn->mt_flags |= MDB_TXN_DIRTY; + } + } + mdb_default_cmp(txn, MAIN_DBI); + return MDB_SUCCESS; + } + + if (txn->mt_dbxs[MAIN_DBI].md_cmp == NULL) { + mdb_default_cmp(txn, MAIN_DBI); + } + + /* Is the DB already open? */ + len = strlen(name); + for (i=CORE_DBS; i<txn->mt_numdbs; i++) { + if (!txn->mt_dbxs[i].md_name.mv_size) { + /* Remember this free slot */ + if (!unused) unused = i; + continue; + } + if (len == txn->mt_dbxs[i].md_name.mv_size && + !strncmp(name, txn->mt_dbxs[i].md_name.mv_data, len)) { + *dbi = i; + return MDB_SUCCESS; + } + } + + /* If no free slot and max hit, fail */ + if (!unused && txn->mt_numdbs >= txn->mt_env->me_maxdbs) + return MDB_DBS_FULL; + + /* Cannot mix named databases with some mainDB flags */ + if (txn->mt_dbs[MAIN_DBI].md_flags & (MDB_DUPSORT|MDB_INTEGERKEY)) + return (flags & MDB_CREATE) ? MDB_INCOMPATIBLE : MDB_NOTFOUND; + + /* Find the DB info */ + dbflag = DB_NEW|DB_VALID|DB_USRVALID; + exact = 0; + key.mv_size = len; + key.mv_data = (void *)name; + mdb_cursor_init(&mc, txn, MAIN_DBI, NULL); + rc = mdb_cursor_set(&mc, &key, &data, MDB_SET, &exact); + if (rc == MDB_SUCCESS) { + /* make sure this is actually a DB */ + MDB_node *node = NODEPTR(mc.mc_pg[mc.mc_top], mc.mc_ki[mc.mc_top]); + if ((node->mn_flags & (F_DUPDATA|F_SUBDATA)) != F_SUBDATA) + return MDB_INCOMPATIBLE; + } else { + if (rc != MDB_NOTFOUND || !(flags & MDB_CREATE)) + return rc; + if (F_ISSET(txn->mt_flags, MDB_TXN_RDONLY)) + return EACCES; + } + + /* Done here so we cannot fail after creating a new DB */ + if ((namedup = strdup(name)) == NULL) + return ENOMEM; + + if (rc) { + /* MDB_NOTFOUND and MDB_CREATE: Create new DB */ + data.mv_size = sizeof(MDB_db); + data.mv_data = &dummy; + memset(&dummy, 0, sizeof(dummy)); + dummy.md_root = P_INVALID; + dummy.md_flags = flags & PERSISTENT_FLAGS; + WITH_CURSOR_TRACKING(mc, + rc = mdb_cursor_put(&mc, &key, &data, F_SUBDATA)); + dbflag |= DB_DIRTY; + } + + if (rc) { + free(namedup); + } else { + /* Got info, register DBI in this txn */ + unsigned int slot = unused ? unused : txn->mt_numdbs; + txn->mt_dbxs[slot].md_name.mv_data = namedup; + txn->mt_dbxs[slot].md_name.mv_size = len; + txn->mt_dbxs[slot].md_rel = NULL; + txn->mt_dbflags[slot] = dbflag; + /* txn-> and env-> are the same in read txns, use + * tmp variable to avoid undefined assignment + */ + seq = ++txn->mt_env->me_dbiseqs[slot]; + txn->mt_dbiseqs[slot] = seq; + + memcpy(&txn->mt_dbs[slot], data.mv_data, sizeof(MDB_db)); + *dbi = slot; + mdb_default_cmp(txn, slot); + if (!unused) { + txn->mt_numdbs++; + } + } + + return rc; +} + +int ESECT +mdb_stat(MDB_txn *txn, MDB_dbi dbi, MDB_stat *arg) +{ + if (!arg || !TXN_DBI_EXIST(txn, dbi, DB_VALID)) + return EINVAL; + + if (txn->mt_flags & MDB_TXN_BLOCKED) + return MDB_BAD_TXN; + + if (txn->mt_dbflags[dbi] & DB_STALE) { + MDB_cursor mc; + MDB_xcursor mx; + /* Stale, must read the DB's root. cursor_init does it for us. */ + mdb_cursor_init(&mc, txn, dbi, &mx); + } + return mdb_stat0(txn->mt_env, &txn->mt_dbs[dbi], arg); +} + +void mdb_dbi_close(MDB_env *env, MDB_dbi dbi) +{ + char *ptr; + if (dbi < CORE_DBS || dbi >= env->me_maxdbs) + return; + ptr = env->me_dbxs[dbi].md_name.mv_data; + /* If there was no name, this was already closed */ + if (ptr) { + env->me_dbxs[dbi].md_name.mv_data = NULL; + env->me_dbxs[dbi].md_name.mv_size = 0; + env->me_dbflags[dbi] = 0; + env->me_dbiseqs[dbi]++; + free(ptr); + } +} + +int mdb_dbi_flags(MDB_txn *txn, MDB_dbi dbi, unsigned int *flags) +{ + /* We could return the flags for the FREE_DBI too but what's the point? */ + if (!TXN_DBI_EXIST(txn, dbi, DB_USRVALID)) + return EINVAL; + *flags = txn->mt_dbs[dbi].md_flags & PERSISTENT_FLAGS; + return MDB_SUCCESS; +} + +/** Add all the DB's pages to the free list. + * @param[in] mc Cursor on the DB to free. + * @param[in] subs non-Zero to check for sub-DBs in this DB. + * @return 0 on success, non-zero on failure. + */ +static int +mdb_drop0(MDB_cursor *mc, int subs) +{ + int rc; + + rc = mdb_page_search(mc, NULL, MDB_PS_FIRST); + if (rc == MDB_SUCCESS) { + MDB_txn *txn = mc->mc_txn; + MDB_node *ni; + MDB_cursor mx; + unsigned int i; + + /* DUPSORT sub-DBs have no ovpages/DBs. Omit scanning leaves. + * This also avoids any P_LEAF2 pages, which have no nodes. + * Also if the DB doesn't have sub-DBs and has no overflow + * pages, omit scanning leaves. + */ + if ((mc->mc_flags & C_SUB) || + (!subs && !mc->mc_db->md_overflow_pages)) + mdb_cursor_pop(mc); + + mdb_cursor_copy(mc, &mx); + while (mc->mc_snum > 0) { + MDB_page *mp = mc->mc_pg[mc->mc_top]; + unsigned n = NUMKEYS(mp); + if (IS_LEAF(mp)) { + for (i=0; i<n; i++) { + ni = NODEPTR(mp, i); + if (ni->mn_flags & F_BIGDATA) { + MDB_page *omp; + pgno_t pg; + memcpy(&pg, NODEDATA(ni), sizeof(pg)); + rc = mdb_page_get(mc, pg, &omp, NULL); + if (rc != 0) + goto done; + mdb_cassert(mc, IS_OVERFLOW(omp)); + rc = mdb_midl_append_range(&txn->mt_free_pgs, + pg, omp->mp_pages); + if (rc) + goto done; + mc->mc_db->md_overflow_pages -= omp->mp_pages; + if (!mc->mc_db->md_overflow_pages && !subs) + break; + } else if (subs && (ni->mn_flags & F_SUBDATA)) { + mdb_xcursor_init1(mc, ni); + rc = mdb_drop0(&mc->mc_xcursor->mx_cursor, 0); + if (rc) + goto done; + } + } + if (!subs && !mc->mc_db->md_overflow_pages) + goto pop; + } else { + if ((rc = mdb_midl_need(&txn->mt_free_pgs, n)) != 0) + goto done; + for (i=0; i<n; i++) { + pgno_t pg; + ni = NODEPTR(mp, i); + pg = NODEPGNO(ni); + /* free it */ + mdb_midl_xappend(txn->mt_free_pgs, pg); + } + } + if (!mc->mc_top) + break; + mc->mc_ki[mc->mc_top] = i; + rc = mdb_cursor_sibling(mc, 1); + if (rc) { + if (rc != MDB_NOTFOUND) + goto done; + /* no more siblings, go back to beginning + * of previous level. + */ +pop: + mdb_cursor_pop(mc); + mc->mc_ki[0] = 0; + for (i=1; i<mc->mc_snum; i++) { + mc->mc_ki[i] = 0; + mc->mc_pg[i] = mx.mc_pg[i]; + } + } + } + /* free it */ + rc = mdb_midl_append(&txn->mt_free_pgs, mc->mc_db->md_root); +done: + if (rc) + txn->mt_flags |= MDB_TXN_ERROR; + } else if (rc == MDB_NOTFOUND) { + rc = MDB_SUCCESS; + } + mc->mc_flags &= ~C_INITIALIZED; + return rc; +} + +int mdb_drop(MDB_txn *txn, MDB_dbi dbi, int del) +{ + MDB_cursor *mc, *m2; + int rc; + + if ((unsigned)del > 1 || !TXN_DBI_EXIST(txn, dbi, DB_USRVALID)) + return EINVAL; + + if (F_ISSET(txn->mt_flags, MDB_TXN_RDONLY)) + return EACCES; + + if (TXN_DBI_CHANGED(txn, dbi)) + return MDB_BAD_DBI; + + rc = mdb_cursor_open(txn, dbi, &mc); + if (rc) + return rc; + + rc = mdb_drop0(mc, mc->mc_db->md_flags & MDB_DUPSORT); + /* Invalidate the dropped DB's cursors */ + for (m2 = txn->mt_cursors[dbi]; m2; m2 = m2->mc_next) + m2->mc_flags &= ~(C_INITIALIZED|C_EOF); + if (rc) + goto leave; + + /* Can't delete the main DB */ + if (del && dbi >= CORE_DBS) { + rc = mdb_del0(txn, MAIN_DBI, &mc->mc_dbx->md_name, NULL, F_SUBDATA); + if (!rc) { + txn->mt_dbflags[dbi] = DB_STALE; + mdb_dbi_close(txn->mt_env, dbi); + } else { + txn->mt_flags |= MDB_TXN_ERROR; + } + } else { + /* reset the DB record, mark it dirty */ + txn->mt_dbflags[dbi] |= DB_DIRTY; + txn->mt_dbs[dbi].md_depth = 0; + txn->mt_dbs[dbi].md_branch_pages = 0; + txn->mt_dbs[dbi].md_leaf_pages = 0; + txn->mt_dbs[dbi].md_overflow_pages = 0; + txn->mt_dbs[dbi].md_entries = 0; + txn->mt_dbs[dbi].md_root = P_INVALID; + + txn->mt_flags |= MDB_TXN_DIRTY; + } +leave: + mdb_cursor_close(mc); + return rc; +} + +int mdb_set_compare(MDB_txn *txn, MDB_dbi dbi, MDB_cmp_func *cmp) +{ + if (!TXN_DBI_EXIST(txn, dbi, DB_USRVALID)) + return EINVAL; + + txn->mt_dbxs[dbi].md_cmp = cmp; + return MDB_SUCCESS; +} + +int mdb_set_dupsort(MDB_txn *txn, MDB_dbi dbi, MDB_cmp_func *cmp) +{ + if (!TXN_DBI_EXIST(txn, dbi, DB_USRVALID)) + return EINVAL; + + txn->mt_dbxs[dbi].md_dcmp = cmp; + return MDB_SUCCESS; +} + +int mdb_set_relfunc(MDB_txn *txn, MDB_dbi dbi, MDB_rel_func *rel) +{ + if (!TXN_DBI_EXIST(txn, dbi, DB_USRVALID)) + return EINVAL; + + txn->mt_dbxs[dbi].md_rel = rel; + return MDB_SUCCESS; +} + +int mdb_set_relctx(MDB_txn *txn, MDB_dbi dbi, void *ctx) +{ + if (!TXN_DBI_EXIST(txn, dbi, DB_USRVALID)) + return EINVAL; + + txn->mt_dbxs[dbi].md_relctx = ctx; + return MDB_SUCCESS; +} + +int ESECT +mdb_env_get_maxkeysize(MDB_env *env) +{ + return ENV_MAXKEY(env); +} + +int ESECT +mdb_reader_list(MDB_env *env, MDB_msg_func *func, void *ctx) +{ + unsigned int i, rdrs; + MDB_reader *mr; + char buf[64]; + int rc = 0, first = 1; + + if (!env || !func) + return -1; + if (!env->me_txns) { + return func("(no reader locks)\n", ctx); + } + rdrs = env->me_txns->mti_numreaders; + mr = env->me_txns->mti_readers; + for (i=0; i<rdrs; i++) { + if (mr[i].mr_pid) { + txnid_t txnid = mr[i].mr_txnid; + sprintf(buf, txnid == (txnid_t)-1 ? + "%10d %"Z"x -\n" : "%10d %"Z"x %"Z"u\n", + (int)mr[i].mr_pid, (size_t)mr[i].mr_tid, txnid); + if (first) { + first = 0; + rc = func(" pid thread txnid\n", ctx); + if (rc < 0) + break; + } + rc = func(buf, ctx); + if (rc < 0) + break; + } + } + if (first) { + rc = func("(no active readers)\n", ctx); + } + return rc; +} + +/** Insert pid into list if not already present. + * return -1 if already present. + */ +static int ESECT +mdb_pid_insert(MDB_PID_T *ids, MDB_PID_T pid) +{ + /* binary search of pid in list */ + unsigned base = 0; + unsigned cursor = 1; + int val = 0; + unsigned n = ids[0]; + + while( 0 < n ) { + unsigned pivot = n >> 1; + cursor = base + pivot + 1; + val = pid - ids[cursor]; + + if( val < 0 ) { + n = pivot; + + } else if ( val > 0 ) { + base = cursor; + n -= pivot + 1; + + } else { + /* found, so it's a duplicate */ + return -1; + } + } + + if( val > 0 ) { + ++cursor; + } + ids[0]++; + for (n = ids[0]; n > cursor; n--) + ids[n] = ids[n-1]; + ids[n] = pid; + return 0; +} + +int ESECT +mdb_reader_check(MDB_env *env, int *dead) +{ + if (!env) + return EINVAL; + if (dead) + *dead = 0; + return env->me_txns ? mdb_reader_check0(env, 0, dead) : MDB_SUCCESS; +} + +/** As #mdb_reader_check(). \b rlocked is set if caller locked #me_rmutex. */ +static int ESECT +mdb_reader_check0(MDB_env *env, int rlocked, int *dead) +{ + mdb_mutexref_t rmutex = rlocked ? NULL : env->me_rmutex; + unsigned int i, j, rdrs; + MDB_reader *mr; + MDB_PID_T *pids, pid; + int rc = MDB_SUCCESS, count = 0; + + rdrs = env->me_txns->mti_numreaders; + pids = malloc((rdrs+1) * sizeof(MDB_PID_T)); + if (!pids) + return ENOMEM; + pids[0] = 0; + mr = env->me_txns->mti_readers; + for (i=0; i<rdrs; i++) { + pid = mr[i].mr_pid; + if (pid && pid != env->me_pid) { + if (mdb_pid_insert(pids, pid) == 0) { + if (!mdb_reader_pid(env, Pidcheck, pid)) { + /* Stale reader found */ + j = i; + if (rmutex) { + if ((rc = LOCK_MUTEX0(rmutex)) != 0) { + if ((rc = mdb_mutex_failed(env, rmutex, rc))) + break; + rdrs = 0; /* the above checked all readers */ + } else { + /* Recheck, a new process may have reused pid */ + if (mdb_reader_pid(env, Pidcheck, pid)) + j = rdrs; + } + } + for (; j<rdrs; j++) + if (mr[j].mr_pid == pid) { + DPRINTF(("clear stale reader pid %u txn %"Z"d", + (unsigned) pid, mr[j].mr_txnid)); + mr[j].mr_pid = 0; + count++; + } + if (rmutex) + UNLOCK_MUTEX(rmutex); + } + } + } + } + free(pids); + if (dead) + *dead = count; + return rc; +} + +#ifdef MDB_ROBUST_SUPPORTED +/** Handle #LOCK_MUTEX0() failure. + * Try to repair the lock file if the mutex owner died. + * @param[in] env the environment handle + * @param[in] mutex LOCK_MUTEX0() mutex + * @param[in] rc LOCK_MUTEX0() error (nonzero) + * @return 0 on success with the mutex locked, or an error code on failure. + */ +static int ESECT +mdb_mutex_failed(MDB_env *env, mdb_mutexref_t mutex, int rc) +{ + int rlocked, rc2; + MDB_meta *meta; + + if (rc == MDB_OWNERDEAD) { + /* We own the mutex. Clean up after dead previous owner. */ + rc = MDB_SUCCESS; + rlocked = (mutex == env->me_rmutex); + if (!rlocked) { + /* Keep mti_txnid updated, otherwise next writer can + * overwrite data which latest meta page refers to. + */ + meta = mdb_env_pick_meta(env); + env->me_txns->mti_txnid = meta->mm_txnid; + /* env is hosed if the dead thread was ours */ + if (env->me_txn) { + env->me_flags |= MDB_FATAL_ERROR; + env->me_txn = NULL; + rc = MDB_PANIC; + } + } + DPRINTF(("%cmutex owner died, %s", (rlocked ? 'r' : 'w'), + (rc ? "this process' env is hosed" : "recovering"))); + rc2 = mdb_reader_check0(env, rlocked, NULL); + if (rc2 == 0) + rc2 = mdb_mutex_consistent(mutex); + if (rc || (rc = rc2)) { + DPRINTF(("LOCK_MUTEX recovery failed, %s", mdb_strerror(rc))); + UNLOCK_MUTEX(mutex); + } + } else { +#ifdef _WIN32 + rc = ErrCode(); +#endif + DPRINTF(("LOCK_MUTEX failed, %s", mdb_strerror(rc))); + } + + return rc; +} +#endif /* MDB_ROBUST_SUPPORTED */ + +#if defined(_WIN32) +/** Convert \b src to new wchar_t[] string with room for \b xtra extra chars */ +static int ESECT +utf8_to_utf16(const char *src, MDB_name *dst, int xtra) +{ + int rc, need = 0; + wchar_t *result = NULL; + for (;;) { /* malloc result, then fill it in */ + need = MultiByteToWideChar(CP_UTF8, 0, src, -1, result, need); + if (!need) { + rc = ErrCode(); + free(result); + return rc; + } + if (!result) { + result = malloc(sizeof(wchar_t) * (need + xtra)); + if (!result) + return ENOMEM; + continue; + } + dst->mn_alloced = 1; + dst->mn_len = need - 1; + dst->mn_val = result; + return MDB_SUCCESS; + } +} +#endif /* defined(_WIN32) */ +/** @} */ diff --git a/src/contrib/lmdb/midl.c b/src/contrib/lmdb/midl.c new file mode 100644 index 0000000..7b2b77e --- /dev/null +++ b/src/contrib/lmdb/midl.c @@ -0,0 +1,359 @@ +/** @file midl.c + * @brief ldap bdb back-end ID List functions */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2000-2018 The OpenLDAP Foundation. + * Portions Copyright 2001-2018 Howard Chu, Symas Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#include <limits.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <sys/types.h> +#include "midl.h" + +/** @defgroup internal LMDB Internals + * @{ + */ +/** @defgroup idls ID List Management + * @{ + */ +#define CMP(x,y) ( (x) < (y) ? -1 : (x) > (y) ) + +unsigned mdb_midl_search( MDB_IDL ids, MDB_ID id ) +{ + /* + * binary search of id in ids + * if found, returns position of id + * if not found, returns first position greater than id + */ + unsigned base = 0; + unsigned cursor = 1; + int val = 0; + unsigned n = ids[0]; + + while( 0 < n ) { + unsigned pivot = n >> 1; + cursor = base + pivot + 1; + val = CMP( ids[cursor], id ); + + if( val < 0 ) { + n = pivot; + + } else if ( val > 0 ) { + base = cursor; + n -= pivot + 1; + + } else { + return cursor; + } + } + + if( val > 0 ) { + ++cursor; + } + return cursor; +} + +#if 0 /* superseded by append/sort */ +int mdb_midl_insert( MDB_IDL ids, MDB_ID id ) +{ + unsigned x, i; + + x = mdb_midl_search( ids, id ); + assert( x > 0 ); + + if( x < 1 ) { + /* internal error */ + return -2; + } + + if ( x <= ids[0] && ids[x] == id ) { + /* duplicate */ + assert(0); + return -1; + } + + if ( ++ids[0] >= MDB_IDL_DB_MAX ) { + /* no room */ + --ids[0]; + return -2; + + } else { + /* insert id */ + for (i=ids[0]; i>x; i--) + ids[i] = ids[i-1]; + ids[x] = id; + } + + return 0; +} +#endif + +MDB_IDL mdb_midl_alloc(int num) +{ + MDB_IDL ids = malloc((num+2) * sizeof(MDB_ID)); + if (ids) { + *ids++ = num; + *ids = 0; + } + return ids; +} + +void mdb_midl_free(MDB_IDL ids) +{ + if (ids) + free(ids-1); +} + +void mdb_midl_shrink( MDB_IDL *idp ) +{ + MDB_IDL ids = *idp; + if (*(--ids) > MDB_IDL_UM_MAX && + (ids = realloc(ids, (MDB_IDL_UM_MAX+2) * sizeof(MDB_ID)))) + { + *ids++ = MDB_IDL_UM_MAX; + *idp = ids; + } +} + +static int mdb_midl_grow( MDB_IDL *idp, int num ) +{ + MDB_IDL idn = *idp-1; + /* grow it */ + idn = realloc(idn, (*idn + num + 2) * sizeof(MDB_ID)); + if (!idn) + return ENOMEM; + *idn++ += num; + *idp = idn; + return 0; +} + +int mdb_midl_need( MDB_IDL *idp, unsigned num ) +{ + MDB_IDL ids = *idp; + num += ids[0]; + if (num > ids[-1]) { + num = (num + num/4 + (256 + 2)) & -256; + if (!(ids = realloc(ids-1, num * sizeof(MDB_ID)))) + return ENOMEM; + *ids++ = num - 2; + *idp = ids; + } + return 0; +} + +int mdb_midl_append( MDB_IDL *idp, MDB_ID id ) +{ + MDB_IDL ids = *idp; + /* Too big? */ + if (ids[0] >= ids[-1]) { + if (mdb_midl_grow(idp, MDB_IDL_UM_MAX)) + return ENOMEM; + ids = *idp; + } + ids[0]++; + ids[ids[0]] = id; + return 0; +} + +int mdb_midl_append_list( MDB_IDL *idp, MDB_IDL app ) +{ + MDB_IDL ids = *idp; + /* Too big? */ + if (ids[0] + app[0] >= ids[-1]) { + if (mdb_midl_grow(idp, app[0])) + return ENOMEM; + ids = *idp; + } + memcpy(&ids[ids[0]+1], &app[1], app[0] * sizeof(MDB_ID)); + ids[0] += app[0]; + return 0; +} + +int mdb_midl_append_range( MDB_IDL *idp, MDB_ID id, unsigned n ) +{ + MDB_ID *ids = *idp, len = ids[0]; + /* Too big? */ + if (len + n > ids[-1]) { + if (mdb_midl_grow(idp, n | MDB_IDL_UM_MAX)) + return ENOMEM; + ids = *idp; + } + ids[0] = len + n; + ids += len; + while (n) + ids[n--] = id++; + return 0; +} + +void mdb_midl_xmerge( MDB_IDL idl, MDB_IDL merge ) +{ + MDB_ID old_id, merge_id, i = merge[0], j = idl[0], k = i+j, total = k; + idl[0] = (MDB_ID)-1; /* delimiter for idl scan below */ + old_id = idl[j]; + while (i) { + merge_id = merge[i--]; + for (; old_id < merge_id; old_id = idl[--j]) + idl[k--] = old_id; + idl[k--] = merge_id; + } + idl[0] = total; +} + +/* Quicksort + Insertion sort for small arrays */ + +#define SMALL 8 +#define MIDL_SWAP(a,b) { itmp=(a); (a)=(b); (b)=itmp; } + +void +mdb_midl_sort( MDB_IDL ids ) +{ + /* Max possible depth of int-indexed tree * 2 items/level */ + int istack[sizeof(int)*CHAR_BIT * 2]; + int i,j,k,l,ir,jstack; + MDB_ID a, itmp; + + ir = (int)ids[0]; + l = 1; + jstack = 0; + for(;;) { + if (ir - l < SMALL) { /* Insertion sort */ + for (j=l+1;j<=ir;j++) { + a = ids[j]; + for (i=j-1;i>=1;i--) { + if (ids[i] >= a) break; + ids[i+1] = ids[i]; + } + ids[i+1] = a; + } + if (jstack == 0) break; + ir = istack[jstack--]; + l = istack[jstack--]; + } else { + k = (l + ir) >> 1; /* Choose median of left, center, right */ + MIDL_SWAP(ids[k], ids[l+1]); + if (ids[l] < ids[ir]) { + MIDL_SWAP(ids[l], ids[ir]); + } + if (ids[l+1] < ids[ir]) { + MIDL_SWAP(ids[l+1], ids[ir]); + } + if (ids[l] < ids[l+1]) { + MIDL_SWAP(ids[l], ids[l+1]); + } + i = l+1; + j = ir; + a = ids[l+1]; + for(;;) { + do i++; while(ids[i] > a); + do j--; while(ids[j] < a); + if (j < i) break; + MIDL_SWAP(ids[i],ids[j]); + } + ids[l+1] = ids[j]; + ids[j] = a; + jstack += 2; + if (ir-i+1 >= j-l) { + istack[jstack] = ir; + istack[jstack-1] = i; + ir = j-1; + } else { + istack[jstack] = j-1; + istack[jstack-1] = l; + l = i; + } + } + } +} + +unsigned mdb_mid2l_search( MDB_ID2L ids, MDB_ID id ) +{ + /* + * binary search of id in ids + * if found, returns position of id + * if not found, returns first position greater than id + */ + unsigned base = 0; + unsigned cursor = 1; + int val = 0; + unsigned n = (unsigned)ids[0].mid; + + while( 0 < n ) { + unsigned pivot = n >> 1; + cursor = base + pivot + 1; + val = CMP( id, ids[cursor].mid ); + + if( val < 0 ) { + n = pivot; + + } else if ( val > 0 ) { + base = cursor; + n -= pivot + 1; + + } else { + return cursor; + } + } + + if( val > 0 ) { + ++cursor; + } + return cursor; +} + +int mdb_mid2l_insert( MDB_ID2L ids, MDB_ID2 *id ) +{ + unsigned x, i; + + x = mdb_mid2l_search( ids, id->mid ); + + if( x < 1 ) { + /* internal error */ + return -2; + } + + if ( x <= ids[0].mid && ids[x].mid == id->mid ) { + /* duplicate */ + return -1; + } + + if ( ids[0].mid >= MDB_IDL_UM_MAX ) { + /* too big */ + return -2; + + } else { + /* insert id */ + ids[0].mid++; + for (i=(unsigned)ids[0].mid; i>x; i--) + ids[i] = ids[i-1]; + ids[x] = *id; + } + + return 0; +} + +int mdb_mid2l_append( MDB_ID2L ids, MDB_ID2 *id ) +{ + /* Too big? */ + if (ids[0].mid >= MDB_IDL_UM_MAX) { + return -2; + } + ids[0].mid++; + ids[ids[0].mid] = *id; + return 0; +} + +/** @} */ +/** @} */ diff --git a/src/contrib/lmdb/midl.h b/src/contrib/lmdb/midl.h new file mode 100644 index 0000000..1aa374c --- /dev/null +++ b/src/contrib/lmdb/midl.h @@ -0,0 +1,186 @@ +/** @file midl.h + * @brief LMDB ID List header file. + * + * This file was originally part of back-bdb but has been + * modified for use in libmdb. Most of the macros defined + * in this file are unused, just left over from the original. + * + * This file is only used internally in libmdb and its definitions + * are not exposed publicly. + */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software <http://www.openldap.org/>. + * + * Copyright 2000-2018 The OpenLDAP Foundation. + * Portions Copyright 2001-2018 Howard Chu, Symas Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * <http://www.OpenLDAP.org/license.html>. + */ + +#ifndef _MDB_MIDL_H_ +#define _MDB_MIDL_H_ + +#include <stddef.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** @defgroup internal LMDB Internals + * @{ + */ + +/** @defgroup idls ID List Management + * @{ + */ + /** A generic unsigned ID number. These were entryIDs in back-bdb. + * Preferably it should have the same size as a pointer. + */ +typedef size_t MDB_ID; + + /** An IDL is an ID List, a sorted array of IDs. The first + * element of the array is a counter for how many actual + * IDs are in the list. In the original back-bdb code, IDLs are + * sorted in ascending order. For libmdb IDLs are sorted in + * descending order. + */ +typedef MDB_ID *MDB_IDL; + +/* IDL sizes - likely should be even bigger + * limiting factors: sizeof(ID), thread stack size + */ +#define MDB_IDL_LOGN 16 /* DB_SIZE is 2^16, UM_SIZE is 2^17 */ +#define MDB_IDL_DB_SIZE (1<<MDB_IDL_LOGN) +#define MDB_IDL_UM_SIZE (1<<(MDB_IDL_LOGN+1)) + +#define MDB_IDL_DB_MAX (MDB_IDL_DB_SIZE-1) +#define MDB_IDL_UM_MAX (MDB_IDL_UM_SIZE-1) + +#define MDB_IDL_SIZEOF(ids) (((ids)[0]+1) * sizeof(MDB_ID)) +#define MDB_IDL_IS_ZERO(ids) ( (ids)[0] == 0 ) +#define MDB_IDL_CPY( dst, src ) (memcpy( dst, src, MDB_IDL_SIZEOF( src ) )) +#define MDB_IDL_FIRST( ids ) ( (ids)[1] ) +#define MDB_IDL_LAST( ids ) ( (ids)[(ids)[0]] ) + + /** Current max length of an #mdb_midl_alloc()ed IDL */ +#define MDB_IDL_ALLOCLEN( ids ) ( (ids)[-1] ) + + /** Append ID to IDL. The IDL must be big enough. */ +#define mdb_midl_xappend(idl, id) do { \ + MDB_ID *xidl = (idl), xlen = ++(xidl[0]); \ + xidl[xlen] = (id); \ + } while (0) + + /** Search for an ID in an IDL. + * @param[in] ids The IDL to search. + * @param[in] id The ID to search for. + * @return The index of the first ID greater than or equal to \b id. + */ +unsigned mdb_midl_search( MDB_IDL ids, MDB_ID id ); + + /** Allocate an IDL. + * Allocates memory for an IDL of the given size. + * @return IDL on success, NULL on failure. + */ +MDB_IDL mdb_midl_alloc(int num); + + /** Free an IDL. + * @param[in] ids The IDL to free. + */ +void mdb_midl_free(MDB_IDL ids); + + /** Shrink an IDL. + * Return the IDL to the default size if it has grown larger. + * @param[in,out] idp Address of the IDL to shrink. + */ +void mdb_midl_shrink(MDB_IDL *idp); + + /** Make room for num additional elements in an IDL. + * @param[in,out] idp Address of the IDL. + * @param[in] num Number of elements to make room for. + * @return 0 on success, ENOMEM on failure. + */ +int mdb_midl_need(MDB_IDL *idp, unsigned num); + + /** Append an ID onto an IDL. + * @param[in,out] idp Address of the IDL to append to. + * @param[in] id The ID to append. + * @return 0 on success, ENOMEM if the IDL is too large. + */ +int mdb_midl_append( MDB_IDL *idp, MDB_ID id ); + + /** Append an IDL onto an IDL. + * @param[in,out] idp Address of the IDL to append to. + * @param[in] app The IDL to append. + * @return 0 on success, ENOMEM if the IDL is too large. + */ +int mdb_midl_append_list( MDB_IDL *idp, MDB_IDL app ); + + /** Append an ID range onto an IDL. + * @param[in,out] idp Address of the IDL to append to. + * @param[in] id The lowest ID to append. + * @param[in] n Number of IDs to append. + * @return 0 on success, ENOMEM if the IDL is too large. + */ +int mdb_midl_append_range( MDB_IDL *idp, MDB_ID id, unsigned n ); + + /** Merge an IDL onto an IDL. The destination IDL must be big enough. + * @param[in] idl The IDL to merge into. + * @param[in] merge The IDL to merge. + */ +void mdb_midl_xmerge( MDB_IDL idl, MDB_IDL merge ); + + /** Sort an IDL. + * @param[in,out] ids The IDL to sort. + */ +void mdb_midl_sort( MDB_IDL ids ); + + /** An ID2 is an ID/pointer pair. + */ +typedef struct MDB_ID2 { + MDB_ID mid; /**< The ID */ + void *mptr; /**< The pointer */ +} MDB_ID2; + + /** An ID2L is an ID2 List, a sorted array of ID2s. + * The first element's \b mid member is a count of how many actual + * elements are in the array. The \b mptr member of the first element is unused. + * The array is sorted in ascending order by \b mid. + */ +typedef MDB_ID2 *MDB_ID2L; + + /** Search for an ID in an ID2L. + * @param[in] ids The ID2L to search. + * @param[in] id The ID to search for. + * @return The index of the first ID2 whose \b mid member is greater than or equal to \b id. + */ +unsigned mdb_mid2l_search( MDB_ID2L ids, MDB_ID id ); + + + /** Insert an ID2 into a ID2L. + * @param[in,out] ids The ID2L to insert into. + * @param[in] id The ID2 to insert. + * @return 0 on success, -1 if the ID was already present in the ID2L. + */ +int mdb_mid2l_insert( MDB_ID2L ids, MDB_ID2 *id ); + + /** Append an ID2 into a ID2L. + * @param[in,out] ids The ID2L to append into. + * @param[in] id The ID2 to append. + * @return 0 on success, -2 if the ID2L is too big. + */ +int mdb_mid2l_append( MDB_ID2L ids, MDB_ID2 *id ); + +/** @} */ +/** @} */ +#ifdef __cplusplus +} +#endif +#endif /* _MDB_MIDL_H_ */ diff --git a/src/contrib/macros.h b/src/contrib/macros.h new file mode 100644 index 0000000..1db2241 --- /dev/null +++ b/src/contrib/macros.h @@ -0,0 +1,43 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \brief Common macros. + */ + +#pragma once + +/*! \brief Eliminate compiler warning with unused parameters. */ +#define UNUSED(param) (void)(param) + +#ifndef MIN +/*! \brief Type-safe minimum macro. */ +#define MIN(a, b) \ + ({ __typeof__ (a) _a = (a); __typeof__ (b) _b = (b); _a < _b ? _a : _b; }) + +/*! \brief Type-safe maximum macro. */ +#define MAX(a, b) \ + ({ __typeof__ (a) _a = (a); __typeof__ (b) _b = (b); _a > _b ? _a : _b; }) +#endif + +#ifndef likely +/*! \brief Optimize for x to be true value. */ +#define likely(x) __builtin_expect((x), 1) +#endif + +#ifndef unlikely +/*! \brief Optimize for x to be false value. */ +#define unlikely(x) __builtin_expect((x), 0) +#endif diff --git a/src/contrib/mempattern.c b/src/contrib/mempattern.c new file mode 100644 index 0000000..74add91 --- /dev/null +++ b/src/contrib/mempattern.c @@ -0,0 +1,122 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdlib.h> + +#include "contrib/mempattern.h" +#include "contrib/string.h" +#include "contrib/ucw/mempool.h" + +static void mm_nofree(void *p) +{ + /* nop */ +} + +static void *mm_malloc(void *ctx, size_t n) +{ + (void)ctx; + return malloc(n); +} + +void *mm_alloc(knot_mm_t *mm, size_t size) +{ + if (mm) { + return mm->alloc(mm->ctx, size); + } else { + return malloc(size); + } +} + +void *mm_calloc(knot_mm_t *mm, size_t nmemb, size_t size) +{ + if (nmemb == 0 || size == 0) { + return NULL; + } + if (mm) { + size_t total_size = nmemb * size; + if (total_size / nmemb != size) { // Overflow check + return NULL; + } + void *mem = mm_alloc(mm, total_size); + if (mem == NULL) { + return NULL; + } + return memzero(mem, total_size); + } else { + return calloc(nmemb, size); + } +} + +void *mm_realloc(knot_mm_t *mm, void *what, size_t size, size_t prev_size) +{ + if (mm) { + void *p = mm->alloc(mm->ctx, size); + if (p == NULL) { + return NULL; + } else { + if (what) { + memcpy(p, what, + prev_size < size ? prev_size : size); + } + mm_free(mm, what); + return p; + } + } else { + return realloc(what, size); + } +} + +char *mm_strdup(knot_mm_t *mm, const char *s) +{ + if (s == NULL) { + return NULL; + } + if (mm) { + size_t len = strlen(s) + 1; + void *mem = mm_alloc(mm, len); + if (mem == NULL) { + return NULL; + } + return memcpy(mem, s, len); + } else { + return strdup(s); + } +} + +void mm_free(knot_mm_t *mm, void *what) +{ + if (mm) { + if (mm->free) { + mm->free(what); + } + } else { + free(what); + } +} + +void mm_ctx_init(knot_mm_t *mm) +{ + mm->ctx = NULL; + mm->alloc = mm_malloc; + mm->free = free; +} + +void mm_ctx_mempool(knot_mm_t *mm, size_t chunk_size) +{ + mm->ctx = mp_new(chunk_size); + mm->alloc = (knot_mm_alloc_t)mp_alloc; + mm->free = mm_nofree; +} diff --git a/src/contrib/mempattern.h b/src/contrib/mempattern.h new file mode 100644 index 0000000..1c5d9a2 --- /dev/null +++ b/src/contrib/mempattern.h @@ -0,0 +1,46 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \brief Memory allocation related functions. + */ + +#pragma once + +#include "libknot/mm_ctx.h" + +/*! \brief Default memory block size. */ +#define MM_DEFAULT_BLKSIZE 4096 + +/*! \brief Allocs using 'mm' if any, uses system malloc() otherwise. */ +void *mm_alloc(knot_mm_t *mm, size_t size); + +/*! \brief Callocs using 'mm' if any, uses system calloc() otherwise. */ +void *mm_calloc(knot_mm_t *mm, size_t nmemb, size_t size); + +/*! \brief Reallocs using 'mm' if any, uses system realloc() otherwise. */ +void *mm_realloc(knot_mm_t *mm, void *what, size_t size, size_t prev_size); + +/*! \brief Strdups using 'mm' if any, uses system strdup() otherwise. */ +char *mm_strdup(knot_mm_t *mm, const char *s); + +/*! \brief Free using 'mm' if any, uses system free() otherwise. */ +void mm_free(knot_mm_t *mm, void *what); + +/*! \brief Initialize default memory allocation context. */ +void mm_ctx_init(knot_mm_t *mm); + +/*! \brief Memory pool context. */ +void mm_ctx_mempool(knot_mm_t *mm, size_t chunk_size); diff --git a/src/contrib/net.c b/src/contrib/net.c new file mode 100644 index 0000000..81fc8ef --- /dev/null +++ b/src/contrib/net.c @@ -0,0 +1,598 @@ +/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> +#include <errno.h> +#include <fcntl.h> +#include <netinet/in.h> +#include <poll.h> +#include <stdbool.h> +#include <sys/socket.h> +#include <sys/uio.h> +#include <unistd.h> + +#include "libknot/errcode.h" +#include "contrib/net.h" +#include "contrib/sockaddr.h" + +/* + * OS X doesn't support MSG_NOSIGNAL. Use SO_NOSIGPIPE socket option instead. + */ +#if defined(__APPLE__) && !defined(MSG_NOSIGNAL) +# define MSG_NOSIGNAL 0 +# define osx_block_sigpipe(sock) sockopt_enable(sock, SOL_SOCKET, SO_NOSIGPIPE) +#else +# define osx_block_sigpipe(sock) KNOT_EOK +#endif + +/*! + * \brief Enable socket option. + */ +static int sockopt_enable(int sock, int level, int optname) +{ + const int enable = 1; + if (setsockopt(sock, level, optname, &enable, sizeof(enable)) != 0) { + return knot_map_errno(); + } + + return KNOT_EOK; +} + +/*! + * \brief Create a non-blocking socket. + * + * Prefer SOCK_NONBLOCK if available to save one fcntl() syscall. + * + */ +static int socket_create(int family, int type, int proto) +{ +#ifdef SOCK_NONBLOCK + type |= SOCK_NONBLOCK; +#endif + int sock = socket(family, type, proto); + if (sock < 0) { + return knot_map_errno(); + } + +#ifndef SOCK_NONBLOCK + if (fcntl(sock, F_SETFL, O_NONBLOCK) != 0) { + int ret = knot_map_errno(); + close(sock); + return ret; + } +#endif + + int ret = osx_block_sigpipe(sock); + if (ret != KNOT_EOK) { + return ret; + } + + return sock; +} + +int net_unbound_socket(int type, const struct sockaddr *sa) +{ + if (sa == NULL) { + return KNOT_EINVAL; + } + + /* Create socket. */ + return socket_create(sa->sa_family, type, 0); +} + +struct option { + int level; + int name; +}; + +/*! + * \brief Get setsock option for binding non-local address. + */ +static const struct option *nonlocal_option(int family) +{ + static const struct option ipv4 = { + #if defined(IP_FREEBIND) + IPPROTO_IP, IP_FREEBIND + #elif defined(IP_BINDANY) + IPPROTO_IP, IP_BINDANY + #else + 0, 0 + #endif + }; + + static const struct option ipv6 = { + #if defined(IP_FREEBIND) + IPPROTO_IP, IP_FREEBIND + #elif defined(IPV6_BINDANY) + IPPROTO_IPV6, IPV6_BINDANY + #else + 0, 0 + #endif + + }; + + switch (family) { + case AF_INET: return &ipv4; + case AF_INET6: return &ipv6; + default: + return NULL; + } +} + +static int enable_nonlocal(int sock, int family) +{ + const struct option *opt = nonlocal_option(family); + if (opt == NULL || opt->name == 0) { + return KNOT_ENOTSUP; + } + + return sockopt_enable(sock, opt->level, opt->name); +} + +static int enable_reuseport(int sock) +{ +#ifdef ENABLE_REUSEPORT + return sockopt_enable(sock, SOL_SOCKET, SO_REUSEPORT); +#else + return KNOT_ENOTSUP; +#endif +} + +static void unlink_unix_socket(const struct sockaddr *addr) +{ + char path[SOCKADDR_STRLEN] = { 0 }; + sockaddr_tostr(path, sizeof(path), addr); + unlink(path); +} + +int net_bound_socket(int type, const struct sockaddr *sa, enum net_flags flags) +{ + /* Create socket. */ + int sock = net_unbound_socket(type, sa); + if (sock < 0) { + return sock; + } + + /* Unlink UNIX sock if exists. */ + if (sa->sa_family == AF_UNIX) { + unlink_unix_socket(sa); + } + + /* Reuse old address if taken. */ + int ret = sockopt_enable(sock, SOL_SOCKET, SO_REUSEADDR); + if (ret != KNOT_EOK) { + close(sock); + return ret; + } + + /* Don't bind IPv4 for IPv6 any address. */ + if (sa->sa_family == AF_INET6) { + ret = sockopt_enable(sock, IPPROTO_IPV6, IPV6_V6ONLY); + if (ret != KNOT_EOK) { + close(sock); + return ret; + } + } + + /* Allow bind to non-local address. */ + if (flags & NET_BIND_NONLOCAL) { + ret = enable_nonlocal(sock, sa->sa_family); + if (ret != KNOT_EOK) { + close(sock); + return ret; + } + } + + /* Allow to bind the same address by multiple threads. */ + if (flags & NET_BIND_MULTIPLE) { + ret = enable_reuseport(sock); + if (ret != KNOT_EOK) { + close(sock); + return ret; + } + } + + /* Bind to specified address. */ + ret = bind(sock, sa, sockaddr_len(sa)); + if (ret < 0) { + ret = knot_map_errno(); + close(sock); + return ret; + } + + return sock; +} + +int net_connected_socket(int type, const struct sockaddr *dst_addr, + const struct sockaddr *src_addr) +{ + if (dst_addr == NULL) { + return KNOT_EINVAL; + } + + /* Check port. */ + if (sockaddr_port(dst_addr) == 0) { + return KNOT_NET_EADDR; + } + + /* Bind to specific source address - if set. */ + int sock = -1; + if (src_addr && src_addr->sa_family != AF_UNSPEC) { + sock = net_bound_socket(type, src_addr, 0); + } else { + sock = net_unbound_socket(type, dst_addr); + } + if (sock < 0) { + return sock; + } + + /* Connect to destination. */ + const struct sockaddr *sa = (const struct sockaddr *)dst_addr; + int ret = connect(sock, sa, sockaddr_len(sa)); + if (ret != 0 && errno != EINPROGRESS) { + ret = knot_map_errno(); + close(sock); + return ret; + } + + return sock; +} + +bool net_is_connected(int sock) +{ + struct sockaddr_storage ss; + socklen_t len = sizeof(ss); + return (getpeername(sock, (struct sockaddr *)&ss, &len) == 0); +} + +int net_socktype(int sock) +{ + int type; + socklen_t size = sizeof(type); + + if (getsockopt(sock, SOL_SOCKET, SO_TYPE, &type, &size) == 0) { + return type; + } else { + return AF_UNSPEC; + } +} + +bool net_is_stream(int sock) +{ + return net_socktype(sock) == SOCK_STREAM; +} + +int net_accept(int sock, struct sockaddr_storage *addr) +{ + socklen_t sa_len = sizeof(*addr); + + int remote = -1; + +#if defined(HAVE_ACCEPT4) && defined(SOCK_NONBLOCK) + remote = accept4(sock, (struct sockaddr *)addr, &sa_len, SOCK_NONBLOCK); + if (remote < 0) { + return knot_map_errno(); + } +#else + remote = accept(sock, (struct sockaddr *)addr, &sa_len); + if (fcntl(remote, F_SETFL, O_NONBLOCK) != 0) { + int error = knot_map_errno(); + close(remote); + return error; + } +#endif + + return remote; +} + +/* -- I/O interface handling partial -------------------------------------- */ + +/*! + * \brief Perform \a poll() on one socket. + */ +static int poll_one(int fd, int events, int timeout_ms) +{ + struct pollfd pfd = { + .fd = fd, + .events = events + }; + + return poll(&pfd, 1, timeout_ms); +} + +/*! + * \brief Check if we should wait for I/O readiness. + * + * \param error \a errno set by the failed I/O operation. + */ +static bool io_should_wait(int error) +{ + /* socket data not ready */ + if (error == EAGAIN || error == EWOULDBLOCK) { + return true; + } + +#ifndef __linux__ + /* FreeBSD: connection in progress */ + if (error == ENOTCONN) { + return true; + } +#endif + + return false; +} + +/*! + * \brief I/O operation callbacks. + */ +struct io { + ssize_t (*process)(int sockfd, struct msghdr *msg); + int (*wait)(int sockfd, int timeout_ms); +}; + +/*! + * \brief Get total size of I/O vector in a message. + */ +static size_t msg_iov_len(const struct msghdr *msg) +{ + size_t total = 0; + + for (int i = 0; i < msg->msg_iovlen; i++) { + total += msg->msg_iov[i].iov_len; + } + + return total; +} + +/*! + * \brief Shift processed data out of message IO vectors. + */ +static void msg_iov_shift(struct msghdr *msg, size_t done) +{ + struct iovec *iov = msg->msg_iov; + int iovlen = msg->msg_iovlen; + + for (int i = 0; i < iovlen && done > 0; i++) { + if (iov[i].iov_len > done) { + iov[i].iov_base += done; + iov[i].iov_len -= done; + done = 0; + } else { + done -= iov[i].iov_len; + msg->msg_iov += 1; + msg->msg_iovlen -= 1; + } + } + + assert(done == 0); +} + +/*! + * \brief Perform an I/O operation with a socket with waiting. + * + * \param oneshot If set, doesn't wait until the buffer is fully processed. + * + */ +static ssize_t io_exec(const struct io *io, int fd, struct msghdr *msg, + bool oneshot, int timeout_ms) +{ + size_t done = 0; + size_t total = msg_iov_len(msg); + + for (;;) { + /* Perform I/O. */ + ssize_t ret = io->process(fd, msg); + if (ret == -1 && errno == EINTR) { + continue; + } + if (ret > 0) { + done += ret; + if (oneshot || done == total) { + break; + } + msg_iov_shift(msg, ret); + } + + /* Wait for data readiness. */ + if (ret > 0 || (ret == -1 && io_should_wait(errno))) { + do { + ret = io->wait(fd, timeout_ms); + } while (ret == -1 && errno == EINTR); + if (ret == 1) { + continue; + } else if (ret == 0) { + return KNOT_ETIMEOUT; + } + } + + /* Disconnected or error. */ + return KNOT_ECONN; + } + + return done; +} + +static ssize_t recv_process(int fd, struct msghdr *msg) +{ + return recvmsg(fd, msg, MSG_DONTWAIT | MSG_NOSIGNAL); +} + +static int recv_wait(int fd, int timeout_ms) +{ + return poll_one(fd, POLLIN, timeout_ms); +} + +static ssize_t recv_data(int sock, struct msghdr *msg, bool oneshot, int timeout_ms) +{ + static const struct io RECV_IO = { + .process = recv_process, + .wait = recv_wait + }; + + return io_exec(&RECV_IO, sock, msg, oneshot, timeout_ms); +} + +static ssize_t send_process(int fd, struct msghdr *msg) +{ + return sendmsg(fd, msg, MSG_NOSIGNAL); +} + +static int send_wait(int fd, int timeout_ms) +{ + return poll_one(fd, POLLOUT, timeout_ms); +} + +static ssize_t send_data(int sock, struct msghdr *msg, int timeout_ms) +{ + static const struct io SEND_IO = { + .process = send_process, + .wait = send_wait + }; + + return io_exec(&SEND_IO, sock, msg, false, timeout_ms); +} + +/* -- generic stream and datagram I/O -------------------------------------- */ + +ssize_t net_send(int sock, const uint8_t *buffer, size_t size, + const struct sockaddr *addr, int timeout_ms) +{ + if (sock < 0 || buffer == NULL) { + return KNOT_EINVAL; + } + + struct iovec iov = { 0 }; + iov.iov_base = (void *)buffer; + iov.iov_len = size; + + struct msghdr msg = { 0 }; + msg.msg_name = (void *)addr; + msg.msg_namelen = sockaddr_len(addr); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + int ret = send_data(sock, &msg, timeout_ms); + if (ret < 0) { + return ret; + } else if (ret != size) { + return KNOT_ECONN; + } + + return ret; +} + +ssize_t net_recv(int sock, uint8_t *buffer, size_t size, + struct sockaddr_storage *addr, int timeout_ms) +{ + if (sock < 0 || buffer == NULL) { + return KNOT_EINVAL; + } + + struct iovec iov = { 0 }; + iov.iov_base = buffer; + iov.iov_len = size; + + struct msghdr msg = { 0 }; + msg.msg_name = (void *)addr; + msg.msg_namelen = addr ? sizeof(*addr) : 0; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + return recv_data(sock, &msg, true, timeout_ms); +} + +ssize_t net_dgram_send(int sock, const uint8_t *buffer, size_t size, + const struct sockaddr *addr) +{ + return net_send(sock, buffer, size, addr, 0); +} + +ssize_t net_dgram_recv(int sock, uint8_t *buffer, size_t size, int timeout_ms) +{ + return net_recv(sock, buffer, size, NULL, timeout_ms); +} + +ssize_t net_stream_send(int sock, const uint8_t *buffer, size_t size, int timeout_ms) +{ + return net_send(sock, buffer, size, NULL, timeout_ms); +} + +ssize_t net_stream_recv(int sock, uint8_t *buffer, size_t size, int timeout_ms) +{ + return net_recv(sock, buffer, size, NULL, timeout_ms); +} + +/* -- DNS specific I/O ----------------------------------------------------- */ + +ssize_t net_dns_tcp_send(int sock, const uint8_t *buffer, size_t size, int timeout_ms) +{ + if (sock < 0 || buffer == NULL || size > UINT16_MAX) { + return KNOT_EINVAL; + } + + struct iovec iov[2]; + uint16_t pktsize = htons(size); + iov[0].iov_base = &pktsize; + iov[0].iov_len = sizeof(uint16_t); + iov[1].iov_base = (void *)buffer; + iov[1].iov_len = size; + + struct msghdr msg = { 0 }; + msg.msg_iov = iov; + msg.msg_iovlen = 2; + + ssize_t ret = send_data(sock, &msg, timeout_ms); + if (ret < 0) { + return ret; + } + + return size; /* Do not count the size prefix. */ +} + +ssize_t net_dns_tcp_recv(int sock, uint8_t *buffer, size_t size, int timeout_ms) +{ + if (sock < 0 || buffer == NULL) { + return KNOT_EINVAL; + } + + struct iovec iov = { 0 }; + struct msghdr msg = { 0 }; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + /* Receive size. */ + uint16_t pktsize = 0; + iov.iov_base = &pktsize; + iov.iov_len = sizeof(pktsize); + int ret = recv_data(sock, &msg, false, timeout_ms); + if (ret != sizeof(pktsize)) { + return ret; + } + + pktsize = ntohs(pktsize); + + /* Check packet size */ + if (size < pktsize) { + return KNOT_ESPACE; + } + + /* Receive payload. */ + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + iov.iov_base = buffer; + iov.iov_len = pktsize; + return recv_data(sock, &msg, false, timeout_ms); +} diff --git a/src/contrib/net.h b/src/contrib/net.h new file mode 100644 index 0000000..1b6b527 --- /dev/null +++ b/src/contrib/net.h @@ -0,0 +1,185 @@ +/* Copyright (C) 2015 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include <stdbool.h> +#include <stdint.h> +#include <sys/socket.h> + +/*! + * \brief Network interface flags. + */ +enum net_flags { + NET_BIND_NONLOCAL = (1 << 0), //!< Allow to bind unavailable address. + NET_BIND_MULTIPLE = (1 << 1), //!< Allow to bind address multiple times. +}; + +/*! + * \brief Create unbound socket of given family and type. + * + * \note The socket is set to non-blocking mode. + * + * \param type Socket transport type (SOCK_STREAM, SOCK_DGRAM). + * \param sa Socket address. + * + * \return socket or error code + */ +int net_unbound_socket(int type, const struct sockaddr *sa); + +/*! + * \brief Create socket bound to given address. + * + * The socket is set to non-blocking mode. + * + * \param type Socket transport type (SOCK_STREAM, SOCK_DGRAM). + * \param sa Socket address. + * \param flags Socket binding options. + * + * \return socket or error code + */ +int net_bound_socket(int type, const struct sockaddr *sa, enum net_flags flags); + +/*! + * \brief Create socket connected (asynchronously) to destination address. + * + * \note The socket is set to non-blocking mode. + * + * \param type Socket transport type (SOCK_STREAM, SOCK_DGRAM). + * \param dst_addr Destination address. + * \param src_addr Source address (can be NULL). + * + * \return socket or error code + */ +int net_connected_socket(int type, const struct sockaddr *dst_addr, + const struct sockaddr *src_addr); + +/*! + * \brief Return true if the socket is fully connected. + * + * \param sock Socket. + * + * \return true if connected + */ +bool net_is_connected(int sock); + +/*! + * \brief Get socket type (e.g. \a SOCK_STREAM). + * + * \param sock Socket. + */ +int net_socktype(int sock); + +/*! + * \brief Check if socket is a SOCK_STREAM socket. + */ +bool net_is_stream(int sock); + +/*! + * \brief Accept a connection on a listening socket. + * + * \brief The socket is set to non-blocking mode. + * + * \param sock Socket + * \param addr Remote address (can be NULL). + * + * \return socket or error code + */ +int net_accept(int sock, struct sockaddr_storage *addr); + +/*! + * \brief Send a message on a socket. + * + * The socket can be SOCK_STREAM or SOCK_DGRAM. + * + * The implementation handles partial-writes automatically. + * + * \param[in] sock Socket. + * \param[in] buffer Message buffer. + * \param[in] size Size of the message. + * \param[in] addr Remote address (ignored for SOCK_STREAM). + * \param[in] timeout_ms Write timeout in miliseconds (-1 for infinity, + * not valid for SOCK_DGRAM). + * + * \return Number of bytes sent or negative error code. + */ +ssize_t net_send(int sock, const uint8_t *buffer, size_t size, + const struct sockaddr *addr, int timeout_ms); + +/*! + * \brief Receive a message from a socket. + * + * \param[in] sock Socket. + * \param[out] buffer Receiving buffer. + * \param[in] size Capacity of the receiving buffer. + * \param[out] addr Remote address (can be NULL). + * \param[in] timeout_ms Read timeout in miliseconds (-1 for infinity). + * + * \return Number of bytes read or negative error code. + */ +ssize_t net_recv(int sock, uint8_t *buffer, size_t size, + struct sockaddr_storage *addr, int timeout_ms); + +/*! + * \brief Send a message on a SOCK_DGRAM socket. + * + * \see net_send + */ +ssize_t net_dgram_send(int sock, const uint8_t *buffer, size_t size, + const struct sockaddr *addr); + +/*! + * \brief Receive a message from a SOCK_DGRAM socket. + * + * \see net_recv + */ +ssize_t net_dgram_recv(int sock, uint8_t *buffer, size_t size, int timeout_ms); + +/*! + * \brief Send a message on a SOCK_STREAM socket. + * + * \see net_send + */ +ssize_t net_stream_send(int sock, const uint8_t *buffer, size_t size, int timeout_ms); + +/*! + * \brief Receive a message from a SOCK_STREAM socket. + * + * \see net_recv + */ +ssize_t net_stream_recv(int sock, uint8_t *buffer, size_t size, int timeout_ms); + +/*! + * \brief Send a DNS message on a TCP socket. + * + * The outgoing message is prefixed with a two-byte value carrying the DNS + * message size according to the specification. These two bytes are not + * reflected in the return value. + * + * \see net_send + */ +ssize_t net_dns_tcp_send(int sock, const uint8_t *buffer, size_t size, int timeout_ms); + +/*! + * \brief Receive a DNS message from a TCP socket. + * + * The first two bytes of the incoming message are interpreted as a DNS message + * size according to the specification. These two bytes are not included in + * the returned size. Only a complete DNS message is retrieved. + * + * \see net_recv + */ +ssize_t net_dns_tcp_recv(int sock, uint8_t *buffer, size_t size, int timeout_ms); diff --git a/src/contrib/openbsd/LICENSE b/src/contrib/openbsd/LICENSE new file mode 100644 index 0000000..e9a1aaa --- /dev/null +++ b/src/contrib/openbsd/LICENSE @@ -0,0 +1,2 @@ +../licenses/0BSD +../licenses/BSD-3-Clause
\ No newline at end of file diff --git a/src/contrib/openbsd/siphash.c b/src/contrib/openbsd/siphash.c new file mode 100644 index 0000000..26b8cfc --- /dev/null +++ b/src/contrib/openbsd/siphash.c @@ -0,0 +1,176 @@ +/* $OpenBSD: siphash.c,v 1.6 2017/04/12 17:41:49 deraadt Exp $ */ + +/*- + * Copyright (c) 2013 Andre Oppermann <andre@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * SipHash is a family of PRFs SipHash-c-d where the integer parameters c and d + * are the number of compression rounds and the number of finalization rounds. + * A compression round is identical to a finalization round and this round + * function is called SipRound. Given a 128-bit key k and a (possibly empty) + * byte string m, SipHash-c-d returns a 64-bit value SipHash-c-d(k; m). + * + * Implemented from the paper "SipHash: a fast short-input PRF", 2012.09.18, + * by Jean-Philippe Aumasson and Daniel J. Bernstein, + * Permanent Document ID b9a943a805fbfc6fde808af9fc0ecdfa + * https://131002.net/siphash/siphash.pdf + * https://131002.net/siphash/ + */ + +#include <string.h> + +#include "libknot/endian.h" +#include "contrib/string.h" +#include "contrib/openbsd/siphash.h" + +static void SipHash_CRounds(SIPHASH_CTX *, int); +static void SipHash_Rounds(SIPHASH_CTX *, int); + +void +SipHash_Init(SIPHASH_CTX *ctx, const SIPHASH_KEY *key) +{ + uint64_t k0, k1; + + k0 = le64toh(key->k0); + k1 = le64toh(key->k1); + + ctx->v[0] = 0x736f6d6570736575ULL ^ k0; + ctx->v[1] = 0x646f72616e646f6dULL ^ k1; + ctx->v[2] = 0x6c7967656e657261ULL ^ k0; + ctx->v[3] = 0x7465646279746573ULL ^ k1; + + memset(ctx->buf, 0, sizeof(ctx->buf)); + ctx->bytes = 0; +} + +void +SipHash_Update(SIPHASH_CTX *ctx, int rc, int rf, const void *src, size_t len) +{ + const uint8_t *ptr = src; + size_t left, used; + + if (len == 0) + return; + + used = ctx->bytes % sizeof(ctx->buf); + ctx->bytes += len; + + if (used > 0) { + left = sizeof(ctx->buf) - used; + + if (len >= left) { + memcpy(&ctx->buf[used], ptr, left); + SipHash_CRounds(ctx, rc); + len -= left; + ptr += left; + } else { + memcpy(&ctx->buf[used], ptr, len); + return; + } + } + + while (len >= sizeof(ctx->buf)) { + memcpy(ctx->buf, ptr, sizeof(ctx->buf)); + SipHash_CRounds(ctx, rc); + len -= sizeof(ctx->buf); + ptr += sizeof(ctx->buf); + } + + if (len > 0) + memcpy(&ctx->buf, ptr, len); +} + +uint64_t +SipHash_End(SIPHASH_CTX *ctx, int rc, int rf) +{ + uint64_t r; + size_t left, used; + + used = ctx->bytes % sizeof(ctx->buf); + left = sizeof(ctx->buf) - used; + memset(&ctx->buf[used], 0, left - 1); + ctx->buf[7] = ctx->bytes; + + SipHash_CRounds(ctx, rc); + ctx->v[2] ^= 0xff; + SipHash_Rounds(ctx, rf); + + r = (ctx->v[0] ^ ctx->v[1]) ^ (ctx->v[2] ^ ctx->v[3]); + memzero(ctx, sizeof(*ctx)); + return htole64(r); +} + +uint64_t +SipHash(const SIPHASH_KEY *key, int rc, int rf, const void *src, size_t len) +{ + SIPHASH_CTX ctx; + + SipHash_Init(&ctx, key); + SipHash_Update(&ctx, rc, rf, src, len); + return (SipHash_End(&ctx, rc, rf)); +} + +#define SIP_ROTL(x, b) ((x) << (b)) | ( (x) >> (64 - (b))) + +static void +SipHash_Rounds(SIPHASH_CTX *ctx, int rounds) +{ + while (rounds--) { + ctx->v[0] += ctx->v[1]; + ctx->v[2] += ctx->v[3]; + ctx->v[1] = SIP_ROTL(ctx->v[1], 13); + ctx->v[3] = SIP_ROTL(ctx->v[3], 16); + + ctx->v[1] ^= ctx->v[0]; + ctx->v[3] ^= ctx->v[2]; + ctx->v[0] = SIP_ROTL(ctx->v[0], 32); + + ctx->v[2] += ctx->v[1]; + ctx->v[0] += ctx->v[3]; + ctx->v[1] = SIP_ROTL(ctx->v[1], 17); + ctx->v[3] = SIP_ROTL(ctx->v[3], 21); + + ctx->v[1] ^= ctx->v[2]; + ctx->v[3] ^= ctx->v[0]; + ctx->v[2] = SIP_ROTL(ctx->v[2], 32); + } +} + +static void +SipHash_CRounds(SIPHASH_CTX *ctx, int rounds) +{ + uint64_t tmp; + + memcpy(&tmp, ctx->buf, sizeof(tmp)); + uint64_t m = le64toh(tmp); + + ctx->v[3] ^= m; + SipHash_Rounds(ctx, rounds); + ctx->v[0] ^= m; +} diff --git a/src/contrib/openbsd/siphash.h b/src/contrib/openbsd/siphash.h new file mode 100644 index 0000000..d551fe8 --- /dev/null +++ b/src/contrib/openbsd/siphash.h @@ -0,0 +1,83 @@ +/*- + * Copyright (c) 2013 Andre Oppermann <andre@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $OpenBSD: siphash.h,v 1.3 2015/02/20 11:51:03 tedu Exp $ + */ + +/* + * SipHash is a family of pseudorandom functions (a.k.a. keyed hash functions) + * optimized for speed on short messages returning a 64bit hash/digest value. + * + * The number of rounds is defined during the initialization: + * SipHash24_Init() for the fast and resonable strong version + * SipHash48_Init() for the strong version (half as fast) + * + * struct SIPHASH_CTX ctx; + * SipHash24_Init(&ctx); + * SipHash_SetKey(&ctx, "16bytes long key"); + * SipHash_Update(&ctx, pointer_to_string, length_of_string); + * SipHash_End(&ctx); + */ + +#ifndef _SIPHASH_H_ +#define _SIPHASH_H_ + +#include <stddef.h> +#include <stdint.h> + +#define SIPHASH_BLOCK_LENGTH 8 +#define SIPHASH_KEY_LENGTH 16 +#define SIPHASH_DIGEST_LENGTH 8 + +typedef struct _SIPHASH_CTX { + uint64_t v[4]; + uint8_t buf[SIPHASH_BLOCK_LENGTH]; + uint32_t bytes; +} SIPHASH_CTX; + +typedef struct { + uint64_t k0; + uint64_t k1; +} SIPHASH_KEY; + +void SipHash_Init(SIPHASH_CTX *, const SIPHASH_KEY *); +void SipHash_Update(SIPHASH_CTX *, int, int, const void *, size_t); +uint64_t SipHash_End(SIPHASH_CTX *, int, int); +uint64_t SipHash(const SIPHASH_KEY *, int, int, const void *, size_t); + +#define SipHash24_Init(_c, _k) SipHash_Init((_c), (_k)) +#define SipHash24_Update(_c, _p, _l) SipHash_Update((_c), 2, 4, (_p), (_l)) +#define SipHash24_End(_d) SipHash_End((_d), 2, 4) +#define SipHash24(_k, _p, _l) SipHash((_k), 2, 4, (_p), (_l)) + +#define SipHash48_Init(_c, _k) SipHash_Init((_c), (_k)) +#define SipHash48_Update(_c, _p, _l) SipHash_Update((_c), 4, 8, (_p), (_l)) +#define SipHash48_End(_d) SipHash_End((_d), 4, 8) +#define SipHash48(_k, _p, _l) SipHash((_k), 4, 8, (_p), (_l)) + +#endif /* _SIPHASH_H_ */ diff --git a/src/contrib/openbsd/strlcat.c b/src/contrib/openbsd/strlcat.c new file mode 100644 index 0000000..1409062 --- /dev/null +++ b/src/contrib/openbsd/strlcat.c @@ -0,0 +1,48 @@ +/* + * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <string.h> + +#include "contrib/openbsd/strlcat.h" + +size_t +knot_strlcat(char *dst, const char *src, size_t siz) +{ + char *d = dst; + const char *s = src; + size_t n = siz; + size_t dlen; + + /* Find the end of dst and adjust bytes left but don't go past end */ + while (n-- != 0 && *d != '\0') + d++; + dlen = d - dst; + n = siz - dlen; + + if (n == 0) + return(dlen + strlen(s)); + while (*s != '\0') { + if (n != 1) { + *d++ = *s; + n--; + } + s++; + } + *d = '\0'; + + return(dlen + (s - src)); /* count does not include NUL */ +} diff --git a/src/contrib/openbsd/strlcat.h b/src/contrib/openbsd/strlcat.h new file mode 100644 index 0000000..7016069 --- /dev/null +++ b/src/contrib/openbsd/strlcat.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#pragma once + +#ifndef HAVE_STRLCAT +#define strlcat(dst, src, size) knot_strlcat(dst, src, size) +#endif + +/* + * Appends src to string dst of size siz (unlike strncat, siz is the + * full size of dst, not space left). At most siz-1 characters + * will be copied. Always NUL terminates (unless siz <= strlen(dst)). + * Returns strlen(src) + MIN(siz, strlen(initial dst)). + * If retval >= siz, truncation occurred. + */ +size_t +knot_strlcat(char *dst, const char *src, size_t siz); diff --git a/src/contrib/openbsd/strlcpy.c b/src/contrib/openbsd/strlcpy.c new file mode 100644 index 0000000..eafc0e4 --- /dev/null +++ b/src/contrib/openbsd/strlcpy.c @@ -0,0 +1,46 @@ +/* + * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <string.h> + +#include "contrib/openbsd/strlcpy.h" + +size_t +knot_strlcpy(char *dst, const char *src, size_t siz) +{ + char *d = dst; + const char *s = src; + size_t n = siz; + + /* Copy as many bytes as will fit */ + if (n != 0) { + while (--n != 0) { + if ((*d++ = *s++) == '\0') + break; + } + } + + /* Not enough room in dst, add NUL and traverse rest of src */ + if (n == 0) { + if (siz != 0) + *d = '\0'; /* NUL-terminate dst */ + while (*s++) + ; + } + + return(s - src - 1); /* count does not include NUL */ +} diff --git a/src/contrib/openbsd/strlcpy.h b/src/contrib/openbsd/strlcpy.h new file mode 100644 index 0000000..6421068 --- /dev/null +++ b/src/contrib/openbsd/strlcpy.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#pragma once + +#ifndef HAVE_STRLCPY +#define strlcpy(dst, src, size) knot_strlcpy(dst, src, size) +#endif + +/* + * Copy src to string dst of size siz. At most siz-1 characters + * will be copied. Always NUL terminates (unless siz == 0). + * Returns strlen(src); if retval >= siz, truncation occurred. + */ +size_t +knot_strlcpy(char *dst, const char *src, size_t siz); diff --git a/src/contrib/qp-trie/trie.c b/src/contrib/qp-trie/trie.c new file mode 100644 index 0000000..10f9919 --- /dev/null +++ b/src/contrib/qp-trie/trie.c @@ -0,0 +1,835 @@ +/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + + The code originated from https://github.com/fanf2/qp/blob/master/qp.c + at revision 5f6d93753. + */ + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +#include "contrib/qp-trie/trie.h" +#include "contrib/macros.h" +#include "contrib/mempattern.h" +#include "libknot/errcode.h" + +#if defined(__i386) || defined(__x86_64) || defined(_M_IX86) \ + || (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN) \ + && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) + + /*! + * \brief Use a pointer alignment hack to save memory. + * + * When on, isbranch() relies on the fact that in leaf_t the first pointer + * is aligned on multiple of 4 bytes and that the flags bitfield is + * overlaid over the lowest two bits of that pointer. + * Neither is really guaranteed by the C standards; the second part should + * be OK with x86_64 ABI and most likely any other little-endian platform. + * It would be possible to manipulate the right bits portably, but it would + * complicate the code nontrivially. C++ doesn't even guarantee type-punning. + * In debug mode we check this works OK when creating a new trie instance. + */ + #define FLAGS_HACK 1 +#else + #define FLAGS_HACK 0 +#endif + +typedef unsigned char byte; +typedef unsigned int uint; +typedef uint bitmap_t; /*! Bit-maps, using the range of 1<<0 to 1<<16 (inclusive). */ + +typedef struct { + uint32_t len; // 32 bits are enough for key lengths; probably even 16 bits would be. + char chars[]; +} tkey_t; + +/*! \brief Leaf of trie. */ +typedef struct { + #if !FLAGS_HACK + byte flags; + #endif + tkey_t *key; /*!< The pointer must be aligned to 4-byte multiples! */ + trie_val_t val; +} leaf_t; + +/*! \brief A trie node is either leaf_t or branch_t. */ +typedef union node node_t; + +/*! + * \brief Branch node of trie. + * + * - The flags distinguish whether the node is a leaf_t (0), or a branch + * testing the more-important nibble (1) or the less-important one (2). + * - It stores the index of the byte that the node tests. The combined + * value (index*4 + flags) increases in branch nodes as you go deeper + * into the trie. All the keys below a branch are identical up to the + * nibble identified by the branch. Indices have to be stored because + * we skip any branch nodes that would have a single child. + * (Consequently, the skipped parts of key have to be validated in a leaf.) + * - The bitmap indicates which subtries are present. The present child nodes + * are stored in the twigs array (with no holes between them). + * - To simplify storing keys that are prefixes of each other, the end-of-string + * position is treated as another nibble value, ordered before all others. + * That affects the bitmap and twigs fields. + * + * \note The branch nodes are never allocated individually, but they are + * always part of either the root node or the twigs array of the parent. + */ +typedef struct { + #if FLAGS_HACK + uint32_t flags : 2, + bitmap : 17; /*!< The first bitmap bit is for end-of-string child. */ + #else + byte flags; + uint32_t bitmap; + #endif + uint32_t index; + node_t *twigs; +} branch_t; + +union node { + leaf_t leaf; + branch_t branch; +}; + +struct trie { + node_t root; // undefined when weight == 0, see empty_root() + size_t weight; + knot_mm_t mm; +}; + +/*! \brief Make the root node empty (debug-only). */ +static inline void empty_root(node_t *root) { +#ifndef NDEBUG + *root = (node_t){ .branch = { + .flags = 3, // invalid value that fits + .bitmap = 0, + .index = -1, + .twigs = NULL + } }; +#endif +} + +/*! \brief Check that unportable code works OK (debug-only). */ +static void assert_portability(void) { +#if FLAGS_HACK + assert(((union node){ .leaf = { + .key = ((void *)NULL) + 1, + .val = NULL + } }).branch.flags == 1); +#endif +} + +/*! \brief Propagate error codes. */ +#define ERR_RETURN(x) \ + do { \ + int err_code_ = x; \ + if (unlikely(err_code_ != KNOT_EOK)) \ + return err_code_; \ + } while (false) + +/*! + * \brief Count the number of set bits. + * + * \TODO This implementation may be relatively slow on some HW. + */ +static uint bitmap_weight(bitmap_t w) +{ + assert((w & ~((1 << 17) - 1)) == 0); // using the least-important 17 bits + return __builtin_popcount(w); +} + +/*! \brief Test flags to determine type of this node. */ +static bool isbranch(const node_t *t) +{ + uint f = t->branch.flags; + assert(f <= 2); + return f != 0; +} + +/*! \brief Make a bitmask for testing a branch bitmap. */ +static bitmap_t nibbit(byte k, uint flags) +{ + uint shift = (2 - flags) << 2; + uint nibble = (k >> shift) & 0xf; + return 1 << (nibble + 1/*because of prefix keys*/); +} + +/*! \brief Extract a nibble from a key and turn it into a bitmask. */ +static bitmap_t twigbit(node_t *t, const char *key, uint32_t len) +{ + assert(isbranch(t)); + uint i = t->branch.index; + + if (i >= len) + return 1 << 0; // leaf position + + return nibbit((byte)key[i], t->branch.flags); +} + +/*! \brief Test if a branch node has a child indicated by a bitmask. */ +static bool hastwig(node_t *t, bitmap_t bit) +{ + assert(isbranch(t)); + return t->branch.bitmap & bit; +} + +/*! \brief Compute offset of an existing child in a branch node. */ +static uint twigoff(node_t *t, bitmap_t b) +{ + assert(isbranch(t)); + return bitmap_weight(t->branch.bitmap & (b - 1)); +} + +/*! \brief Get pointer to a particular child of a branch node. */ +static node_t* twig(node_t *t, uint i) +{ + assert(isbranch(t)); + return &t->branch.twigs[i]; +} + +/*! + * \brief For a branch nod, compute offset of a child and child count. + * + * Having this separate might be meaningful for performance optimization. + */ +#define TWIGOFFMAX(off, max, t, b) do { \ + off = twigoff(t, b); \ + max = bitmap_weight(t->branch.bitmap); \ + } while(0) + +/*! \brief Simple string comparator. */ +static int key_cmp(const char *k1, uint32_t k1_len, const char *k2, uint32_t k2_len) +{ + int ret = memcmp(k1, k2, MIN(k1_len, k2_len)); + if (ret != 0) { + return ret; + } + + /* Key string is equal, compare lengths. */ + if (k1_len == k2_len) { + return 0; + } else if (k1_len < k2_len) { + return -1; + } else { + return 1; + } +} + +trie_t* trie_create(knot_mm_t *mm) +{ + assert_portability(); + trie_t *trie = mm_alloc(mm, sizeof(trie_t)); + if (trie != NULL) { + empty_root(&trie->root); + trie->weight = 0; + if (mm != NULL) + trie->mm = *mm; + else + mm_ctx_init(&trie->mm); + } + return trie; +} + +/*! \brief Free anything under the trie node, except for the passed pointer itself. */ +static void clear_trie(node_t *trie, knot_mm_t *mm) +{ + if (!isbranch(trie)) { + mm_free(mm, trie->leaf.key); + } else { + branch_t *b = &trie->branch; + int len = bitmap_weight(b->bitmap); + for (int i = 0; i < len; ++i) + clear_trie(b->twigs + i, mm); + mm_free(mm, b->twigs); + } +} + +void trie_free(trie_t *tbl) +{ + if (tbl == NULL) + return; + if (tbl->weight) + clear_trie(&tbl->root, &tbl->mm); + mm_free(&tbl->mm, tbl); +} + +void trie_clear(trie_t *tbl) +{ + assert(tbl); + if (!tbl->weight) + return; + clear_trie(&tbl->root, &tbl->mm); + empty_root(&tbl->root); + tbl->weight = 0; +} + +size_t trie_weight(const trie_t *tbl) +{ + assert(tbl); + return tbl->weight; +} + +trie_val_t* trie_get_try(trie_t *tbl, const char *key, uint32_t len) +{ + assert(tbl); + if (!tbl->weight) + return NULL; + node_t *t = &tbl->root; + while (isbranch(t)) { + __builtin_prefetch(t->branch.twigs); + bitmap_t b = twigbit(t, key, len); + if (!hastwig(t, b)) + return NULL; + t = twig(t, twigoff(t, b)); + } + if (key_cmp(key, len, t->leaf.key->chars, t->leaf.key->len) != 0) + return NULL; + return &t->leaf.val; +} + +int trie_del(trie_t *tbl, const char *key, uint32_t len, trie_val_t *val) +{ + assert(tbl); + if (!tbl->weight) + return KNOT_ENOENT; + node_t *t = &tbl->root; // current and parent node + branch_t *p = NULL; + bitmap_t b = 0; + while (isbranch(t)) { + __builtin_prefetch(t->branch.twigs); + b = twigbit(t, key, len); + if (!hastwig(t, b)) + return KNOT_ENOENT; + p = &t->branch; + t = twig(t, twigoff(t, b)); + } + if (key_cmp(key, len, t->leaf.key->chars, t->leaf.key->len) != 0) + return KNOT_ENOENT; + mm_free(&tbl->mm, t->leaf.key); + if (val != NULL) + *val = t->leaf.val; // we return trie_val_t directly when deleting + --tbl->weight; + if (unlikely(!p)) { // whole trie was a single leaf + assert(tbl->weight == 0); + empty_root(&tbl->root); + return KNOT_EOK; + } + // remove leaf t as child of p + int ci = t - p->twigs, // child index via pointer arithmetic + cc = bitmap_weight(p->bitmap); // child count + assert(ci >= 0 && ci < cc); + + if (cc == 2) { // collapse binary node p: move the other child to this node + node_t *twigs = p->twigs; + (*(node_t *)p) = twigs[1 - ci]; // it might be a leaf or branch + mm_free(&tbl->mm, twigs); + return KNOT_EOK; + } + memmove(p->twigs + ci, p->twigs + ci + 1, sizeof(node_t) * (cc - ci - 1)); + p->bitmap &= ~b; + node_t *twigs = mm_realloc(&tbl->mm, p->twigs, sizeof(node_t) * (cc - 1), + sizeof(node_t) * cc); + if (likely(twigs != NULL)) + p->twigs = twigs; + /* We can ignore mm_realloc failure, only beware that next time + * the prev_size passed to it wouldn't be correct; TODO? */ + return KNOT_EOK; +} + +/*! + * \brief Stack of nodes, storing a path down a trie. + * + * The structure also serves directly as the public trie_it_t type, + * in which case it always points to the current leaf, unless we've finished + * (i.e. it->len == 0). + */ +typedef struct trie_it { + node_t* *stack; /*!< The stack; malloc is used directly instead of mm. */ + uint32_t len; /*!< Current length of the stack. */ + uint32_t alen; /*!< Allocated/available length of the stack. */ + /*! \brief Initial storage for \a stack; it should fit in most use cases. */ + node_t* stack_init[2000 / sizeof(node_t *)]; +} nstack_t; + +/*! \brief Create a node stack containing just the root (or empty). */ +static void ns_init(nstack_t *ns, trie_t *tbl) +{ + assert(tbl); + ns->stack = ns->stack_init; + ns->alen = sizeof(ns->stack_init) / sizeof(ns->stack_init[0]); + if (tbl->weight) { + ns->len = 1; + ns->stack[0] = &tbl->root; + } else { + ns->len = 0; + } +} + +/*! \brief Free inside of the stack, i.e. not the passed pointer itself. */ +static void ns_cleanup(nstack_t *ns) +{ + assert(ns && ns->stack); + if (likely(ns->stack == ns->stack_init)) + return; + free(ns->stack); + #ifndef NDEBUG + ns->stack = NULL; + ns->alen = 0; + #endif +} + +/*! \brief Allocate more space for the stack. */ +static int ns_longer_alloc(nstack_t *ns) +{ + ns->alen *= 2; + size_t new_size = sizeof(nstack_t) + ns->alen * sizeof(node_t *); + node_t **st; + if (ns->stack == ns->stack_init) { + st = malloc(new_size); + if (st != NULL) + memcpy(st, ns->stack, ns->len * sizeof(node_t *)); + } else { + st = realloc(ns->stack, new_size); + } + if (st == NULL) + return KNOT_ENOMEM; + ns->stack = st; + return KNOT_EOK; +} + +/*! \brief Ensure the node stack can be extended by one. */ +static inline int ns_longer(nstack_t *ns) +{ + // get a longer stack if needed + if (likely(ns->len < ns->alen)) + return KNOT_EOK; + return ns_longer_alloc(ns); // hand-split the part suitable for inlining +} + +/*! + * \brief Find the "branching point" as if searching for a key. + * + * The whole path to the point is kept on the passed stack; + * always at least the root will remain on the top of it. + * Beware: the precise semantics of this function is rather tricky. + * The top of the stack will contain: the corresponding leaf if exact match is found; + * or the immediate node below a branching-point-on-edge or the branching-point itself. + * + * \param info Set position of the point of first mismatch (in index and flags). + * \param first Set the value of the first non-matching character (from trie), + * optionally; end-of-string character has value -256 (that's why it's int). + * Note: the character is converted to *unsigned* char (i.e. 0..255), + * as that's the ordering used in the trie. + * + * \return KNOT_EOK or KNOT_ENOMEM. + */ +static int ns_find_branch(nstack_t *ns, const char *key, uint32_t len, + branch_t *info, int *first) +{ + assert(ns && ns->len && info); + // First find some leaf with longest matching prefix. + while (isbranch(ns->stack[ns->len - 1])) { + ERR_RETURN(ns_longer(ns)); + node_t *t = ns->stack[ns->len - 1]; + __builtin_prefetch(t->branch.twigs); + bitmap_t b = twigbit(t, key, len); + // Even if our key is missing from this branch we need to + // keep iterating down to a leaf. It doesn't matter which + // twig we choose since the keys are all the same up to this + // index. Note that blindly using twigoff(t, b) can cause + // an out-of-bounds index if it equals twigmax(t). + uint i = hastwig(t, b) ? twigoff(t, b) : 0; + ns->stack[ns->len++] = twig(t, i); + } + tkey_t *lkey = ns->stack[ns->len-1]->leaf.key; + // Find index of the first char that differs. + uint32_t index = 0; + while (index < MIN(len,lkey->len)) { + if (key[index] != lkey->chars[index]) + break; + else + ++index; + } + info->index = index; + if (first) + *first = lkey->len > index ? (unsigned char)lkey->chars[index] : -256; + // Find flags: which half-byte has matched. + uint flags; + if (index == len && len == lkey->len) { // found equivalent key + info->flags = flags = 0; + goto success; + } + if (likely(index < MIN(len,lkey->len))) { + byte k2 = (byte)lkey->chars[index]; + byte k1 = (byte)key[index]; + flags = ((k1 ^ k2) & 0xf0) ? 1 : 2; + } else { // one is prefix of another + flags = 1; + } + info->flags = flags; + // now go up the trie from the current leaf + branch_t *t; + do { + if (unlikely(ns->len == 1)) + goto success; // only the root stays on the stack + t = (branch_t*)ns->stack[ns->len - 2]; + if (t->index < index || (t->index == index && t->flags < flags)) + goto success; + --ns->len; + } while (true); +success: + #ifndef NDEBUG // invariants on successful return + assert(ns->len); + if (isbranch(ns->stack[ns->len - 1])) { + t = &ns->stack[ns->len - 1]->branch; + assert(t->index > index || (t->index == index && t->flags >= flags)); + } + if (ns->len > 1) { + t = &ns->stack[ns->len - 2]->branch; + assert(t->index < index || (t->index == index + && (t->flags < flags || (t->flags == 1 && flags == 0)))); + } + #endif + return KNOT_EOK; +} + +/*! + * \brief Advance the node stack to the last leaf in the subtree. + * + * \return KNOT_EOK or KNOT_ENOMEM. + */ +static int ns_last_leaf(nstack_t *ns) +{ + assert(ns); + do { + ERR_RETURN(ns_longer(ns)); + node_t *t = ns->stack[ns->len - 1]; + if (!isbranch(t)) + return KNOT_EOK; + int lasti = bitmap_weight(t->branch.bitmap) - 1; + assert(lasti >= 0); + ns->stack[ns->len++] = twig(t, lasti); + } while (true); +} + +/*! + * \brief Advance the node stack to the first leaf in the subtree. + * + * \return KNOT_EOK or KNOT_ENOMEM. + */ +static int ns_first_leaf(nstack_t *ns) +{ + assert(ns && ns->len); + do { + ERR_RETURN(ns_longer(ns)); + node_t *t = ns->stack[ns->len - 1]; + if (!isbranch(t)) + return KNOT_EOK; + ns->stack[ns->len++] = twig(t, 0); + } while (true); +} + +/*! + * \brief Advance the node stack to the leaf that is previous to the current node. + * + * \note Prefix leaf under the current node DOES count (if present; perhaps questionable). + * \return KNOT_EOK on success, KNOT_ENOENT on not-found, or possibly KNOT_ENOMEM. + */ +static int ns_prev_leaf(nstack_t *ns) +{ + assert(ns && ns->len > 0); + + node_t *t = ns->stack[ns->len - 1]; + if (hastwig(t, 1 << 0)) { // the prefix leaf + t = twig(t, 0); + ERR_RETURN(ns_longer(ns)); + ns->stack[ns->len++] = t; + return KNOT_EOK; + } + + do { + if (ns->len < 2) + return KNOT_ENOENT; // root without empty key has no previous leaf + t = ns->stack[ns->len - 1]; + node_t *p = ns->stack[ns->len - 2]; + int pindex = t - p->branch.twigs; // index in parent via pointer arithmetic + assert(pindex >= 0 && pindex <= 16); + if (pindex > 0) { // t isn't the first child -> go down the previous one + ns->stack[ns->len - 1] = twig(p, pindex - 1); + return ns_last_leaf(ns); + } + // we've got to go up again + --ns->len; + } while (true); +} + +/*! + * \brief Advance the node stack to the leaf that is successor to the current node. + * + * \note Prefix leaf or anything else under the current node DOES count. + * \return KNOT_EOK on success, KNOT_ENOENT on not-found, or possibly KNOT_ENOMEM. + */ +static int ns_next_leaf(nstack_t *ns) +{ + assert(ns && ns->len > 0); + + node_t *t = ns->stack[ns->len - 1]; + if (isbranch(t)) + return ns_first_leaf(ns); + do { + if (ns->len < 2) + return KNOT_ENOENT; // not found, as no more parent is available + t = ns->stack[ns->len - 1]; + node_t *p = ns->stack[ns->len - 2]; + int pindex = t - p->branch.twigs; // index in parent via pointer arithmetic + assert(pindex >= 0 && pindex <= 16); + int pcount = bitmap_weight(p->branch.bitmap); + if (pindex + 1 < pcount) { // t isn't the last child -> go down the next one + ns->stack[ns->len - 1] = twig(p, pindex + 1); + return ns_first_leaf(ns); + } + // we've got to go up again + --ns->len; + } while (true); +} + +int trie_get_leq(trie_t *tbl, const char *key, uint32_t len, trie_val_t **val) +{ + assert(tbl && val); + *val = NULL; // so on failure we can just return; + if (tbl->weight == 0) + return KNOT_ENOENT; + { // Intentionally un-indented; until end of function, to bound cleanup attr. + // First find a key with longest-matching prefix + __attribute__((cleanup(ns_cleanup))) + nstack_t ns_local; + ns_init(&ns_local, tbl); + nstack_t *ns = &ns_local; + branch_t bp; + int un_leaf; // first unmatched character in the leaf + ERR_RETURN(ns_find_branch(ns, key, len, &bp, &un_leaf)); + int un_key = bp.index < len ? (unsigned char)key[bp.index] : -256; + node_t *t = ns->stack[ns->len - 1]; + if (bp.flags == 0) { // found exact match + *val = &t->leaf.val; + return KNOT_EOK; + } + // Get t: the last node on matching path + if (isbranch(t) && t->branch.index == bp.index && t->branch.flags == bp.flags) { + // t is OK + } else { + // the top of the stack was the first unmatched node -> step up + if (ns->len == 1) { + // root was unmatched already + if (un_key < un_leaf) + return KNOT_ENOENT; + ERR_RETURN(ns_last_leaf(ns)); + goto success; + } + --ns->len; + t = ns->stack[ns->len - 1]; + } + // Now we re-do the first "non-matching" step in the trie + // but try the previous child if key was less (it may not exist) + bitmap_t b = twigbit(t, key, len); + int i = hastwig(t, b) + ? twigoff(t, b) - (un_key < un_leaf) + : twigoff(t, b) - 1 /*twigoff returns successor when !hastwig*/; + if (i >= 0) { + ERR_RETURN(ns_longer(ns)); + ns->stack[ns->len++] = twig(t, i); + ERR_RETURN(ns_last_leaf(ns)); + } else { + ERR_RETURN(ns_prev_leaf(ns)); + } +success: + assert(!isbranch(ns->stack[ns->len - 1])); + *val = &ns->stack[ns->len - 1]->leaf.val; + return 1; + } +} + +/*! \brief Initialize a new leaf, copying the key, and returning failure code. */ +static int mk_leaf(node_t *leaf, const char *key, uint32_t len, knot_mm_t *mm) +{ + tkey_t *k = mm_alloc(mm, sizeof(tkey_t) + len); + #if FLAGS_HACK + assert(((uintptr_t)k) % 4 == 0); // we need an aligned pointer + #endif + if (unlikely(!k)) + return KNOT_ENOMEM; + k->len = len; + memcpy(k->chars, key, len); + leaf->leaf = (leaf_t){ + #if !FLAGS_HACK + .flags = 0, + #endif + .val = NULL, + .key = k + }; + return KNOT_EOK; +} + +trie_val_t* trie_get_ins(trie_t *tbl, const char *key, uint32_t len) +{ + assert(tbl); + // First leaf in an empty tbl? + if (unlikely(!tbl->weight)) { + if (unlikely(mk_leaf(&tbl->root, key, len, &tbl->mm))) + return NULL; + ++tbl->weight; + return &tbl->root.leaf.val; + } + { // Intentionally un-indented; until end of function, to bound cleanup attr. + // Find the branching-point + __attribute__((cleanup(ns_cleanup))) + nstack_t ns_local; + ns_init(&ns_local, tbl); + nstack_t *ns = &ns_local; + branch_t bp; // branch-point: index and flags signifying the longest common prefix + int k2; // the first unmatched character in the leaf + if (unlikely(ns_find_branch(ns, key, len, &bp, &k2))) + return NULL; + node_t *t = ns->stack[ns->len - 1]; + if (bp.flags == 0) // the same key was already present + return &t->leaf.val; + node_t leaf; + if (unlikely(mk_leaf(&leaf, key, len, &tbl->mm))) + return NULL; + + if (isbranch(t) && bp.index == t->branch.index && bp.flags == t->branch.flags) { + // The node t needs a new leaf child. + bitmap_t b1 = twigbit(t, key, len); + assert(!hastwig(t, b1)); + uint s, m; TWIGOFFMAX(s, m, t, b1); // new child position and original child count + node_t *twigs = mm_realloc(&tbl->mm, t->branch.twigs, + sizeof(node_t) * (m + 1), sizeof(node_t) * m); + if (unlikely(!twigs)) + goto err_leaf; + memmove(twigs + s + 1, twigs + s, sizeof(node_t) * (m - s)); + twigs[s] = leaf; + t->branch.twigs = twigs; + t->branch.bitmap |= b1; + ++tbl->weight; + return &twigs[s].leaf.val; + } else { + // We need to insert a new binary branch with leaf at *t. + // Note: it works the same for the case where we insert above root t. + #ifndef NDEBUG + if (ns->len > 1) { + node_t *pt = ns->stack[ns->len - 2]; + assert(hastwig(pt, twigbit(pt, key, len))); + } + #endif + node_t *twigs = mm_alloc(&tbl->mm, sizeof(node_t) * 2); + if (unlikely(!twigs)) + goto err_leaf; + node_t t2 = *t; // Save before overwriting t. + t->branch.flags = bp.flags; + t->branch.index = bp.index; + t->branch.twigs = twigs; + bitmap_t b1 = twigbit(t, key, len); + bitmap_t b2 = unlikely(k2 == -256) ? (1 << 0) : nibbit(k2, bp.flags); + t->branch.bitmap = b1 | b2; + *twig(t, twigoff(t, b1)) = leaf; + *twig(t, twigoff(t, b2)) = t2; + ++tbl->weight; + return &twig(t, twigoff(t, b1))->leaf.val; + }; +err_leaf: + mm_free(&tbl->mm, leaf.leaf.key); + return NULL; + } +} + +/*! \brief Apply a function to every trie_val_t*, in order; a recursive solution. */ +static int apply_trie(node_t *t, int (*f)(trie_val_t *, void *), void *d) +{ + assert(t); + if (!isbranch(t)) + return f(&t->leaf.val, d); + int child_count = bitmap_weight(t->branch.bitmap); + for (int i = 0; i < child_count; ++i) + ERR_RETURN(apply_trie(twig(t, i), f, d)); + return KNOT_EOK; +} + +int trie_apply(trie_t *tbl, int (*f)(trie_val_t *, void *), void *d) +{ + assert(tbl && f); + if (!tbl->weight) + return KNOT_EOK; + return apply_trie(&tbl->root, f, d); +} + +/* These are all thin wrappers around static Tns* functions. */ +trie_it_t* trie_it_begin(trie_t *tbl) +{ + assert(tbl); + trie_it_t *it = malloc(sizeof(nstack_t)); + if (!it) + return NULL; + ns_init(it, tbl); + if (it->len == 0) // empty tbl + return it; + if (ns_first_leaf(it)) { + ns_cleanup(it); + free(it); + return NULL; + } + return it; +} + +void trie_it_next(trie_it_t *it) +{ + assert(it && it->len); + if (ns_next_leaf(it) != KNOT_EOK) + it->len = 0; +} + +bool trie_it_finished(trie_it_t *it) +{ + assert(it); + return it->len == 0; +} + +void trie_it_free(trie_it_t *it) +{ + if (!it) + return; + ns_cleanup(it); + free(it); +} + +const char* trie_it_key(trie_it_t *it, size_t *len) +{ + assert(it && it->len); + node_t *t = it->stack[it->len - 1]; + assert(!isbranch(t)); + tkey_t *key = t->leaf.key; + if (len) + *len = key->len; + return key->chars; +} + +trie_val_t* trie_it_val(trie_it_t *it) +{ + assert(it && it->len); + node_t *t = it->stack[it->len - 1]; + assert(!isbranch(t)); + return &t->leaf.val; +} diff --git a/src/contrib/qp-trie/trie.h b/src/contrib/qp-trie/trie.h new file mode 100644 index 0000000..28ea571 --- /dev/null +++ b/src/contrib/qp-trie/trie.h @@ -0,0 +1,112 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <stdbool.h> +#include <stdint.h> + +#include "libknot/mm_ctx.h" + +/*! + * \brief Native API of QP-tries: + * + * - keys are char strings, not necessarily zero-terminated, + * the structure copies the contents of the passed keys + * - values are void* pointers, typically you get an ephemeral pointer to it + * - key lengths are limited by 2^32-1 ATM + */ + +/*! \brief Element value. */ +typedef void* trie_val_t; + +/*! \brief Opaque structure holding a QP-trie. */ +typedef struct trie trie_t; + +/*! \brief Opaque type for holding a QP-trie iterator. */ +typedef struct trie_it trie_it_t; + +/*! \brief Create a trie instance. */ +trie_t* trie_create(knot_mm_t *mm); + +/*! \brief Free a trie instance. */ +void trie_free(trie_t *tbl); + +/*! \brief Clear a trie instance (make it empty). */ +void trie_clear(trie_t *tbl); + +/*! \brief Return the number of keys in the trie. */ +size_t trie_weight(const trie_t *tbl); + +/*! \brief Search the trie, returning NULL on failure. */ +trie_val_t* trie_get_try(trie_t *tbl, const char *key, uint32_t len); + +/*! \brief Search the trie, inserting NULL trie_val_t on failure. */ +trie_val_t* trie_get_ins(trie_t *tbl, const char *key, uint32_t len); + +/*! + * \brief Search for less-or-equal element. + * + * \param tbl Trie. + * \param key Searched key. + * \param len Key length. + * \param val Must be valid; it will be set to NULL if not found or errored. + * \return KNOT_EOK for exact match, 1 for previous, KNOT_ENOENT for not-found, + * or KNOT_E*. + */ +int trie_get_leq(trie_t *tbl, const char *key, uint32_t len, trie_val_t **val); + +/*! + * \brief Apply a function to every trie_val_t, in order. + * + * \return KNOT_EOK if success or KNOT_E* if error. + */ +int trie_apply(trie_t *tbl, int (*f)(trie_val_t *, void *), void *d); + +/*! + * \brief Remove an item, returning KNOT_EOK if succeeded or KNOT_ENOENT if not found. + * + * If val!=NULL and deletion succeeded, the deleted value is set. + */ +int trie_del(trie_t *tbl, const char *key, uint32_t len, trie_val_t *val); + +/*! \brief Create a new iterator pointing to the first element (if any). */ +trie_it_t* trie_it_begin(trie_t *tbl); + +/*! + * \brief Advance the iterator to the next element. + * + * Iteration is in ascending lexicographical order. + * In particular, the empty string would be considered as the very first. + */ +void trie_it_next(trie_it_t *it); + +/*! \brief Test if the iterator has gone past the last element. */ +bool trie_it_finished(trie_it_t *it); + +/*! \brief Free any resources of the iterator. It's OK to call it on NULL. */ +void trie_it_free(trie_it_t *it); + +/*! + * \brief Return pointer to the key of the current element. + * + * \note The len is uint32_t internally but size_t is better for our usage + * as it is without an additional type conversion. + */ +const char* trie_it_key(trie_it_t *it, size_t *len); + +/*! \brief Return pointer to the value of the current element (writable). */ +trie_val_t* trie_it_val(trie_it_t *it); diff --git a/src/contrib/sockaddr.c b/src/contrib/sockaddr.c new file mode 100644 index 0000000..d569292 --- /dev/null +++ b/src/contrib/sockaddr.c @@ -0,0 +1,351 @@ +/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <netdb.h> + +#include "libknot/consts.h" +#include "libknot/errcode.h" +#include "contrib/sockaddr.h" +#include "contrib/openbsd/strlcpy.h" +#include "contrib/macros.h" + +int sockaddr_len(const struct sockaddr *sa) +{ + if (sa == NULL) { + return 0; + } + + switch(sa->sa_family) { + case AF_INET: + return sizeof(struct sockaddr_in); + case AF_INET6: + return sizeof(struct sockaddr_in6); + case AF_UNIX: + return sizeof(struct sockaddr_un); + default: + return 0; + } +} + +static int cmp_ipv4(const struct sockaddr_in *a, const struct sockaddr_in *b) +{ + if (a->sin_addr.s_addr < b->sin_addr.s_addr) { + return -1; + } else if (a->sin_addr.s_addr > b->sin_addr.s_addr) { + return 1; + } else { + return a->sin_port - b->sin_port; + } +} + +static int cmp_ipv6(const struct sockaddr_in6 *a, const struct sockaddr_in6 *b) +{ + int ret = memcmp(&a->sin6_addr, &b->sin6_addr, sizeof(struct in6_addr)); + if (ret == 0) { + ret = a->sin6_port - b->sin6_port; + } + + return ret; +} + +static int cmp_unix(const struct sockaddr_un *a, const struct sockaddr_un *b) +{ + int len_a = strnlen(a->sun_path, sizeof(a->sun_path)); + int len_b = strnlen(b->sun_path, sizeof(b->sun_path)); + int len_min = len_a <= len_b ? len_a : len_b; + + int ret = strncmp(a->sun_path, b->sun_path, len_min); + if (ret == 0) { + ret = len_a - len_b; + } + + return ret; +} + +int sockaddr_cmp(const struct sockaddr *a, const struct sockaddr *b) +{ + if (a->sa_family != b->sa_family) { + return (int)a->sa_family - (int)b->sa_family; + } + + switch (a->sa_family) { + case AF_UNSPEC: + return 0; + case AF_INET: + return cmp_ipv4((struct sockaddr_in *)a, (struct sockaddr_in *)b); + case AF_INET6: + return cmp_ipv6((struct sockaddr_in6 *)a, (struct sockaddr_in6 *)b); + case AF_UNIX: + return cmp_unix((struct sockaddr_un *)a, (struct sockaddr_un *)b); + default: + return 1; + } +} + +int sockaddr_set(struct sockaddr_storage *ss, int family, const char *straddr, int port) +{ + if (ss == NULL || straddr == NULL) { + return KNOT_EINVAL; + } + + /* Set family and port. */ + memset(ss, 0, sizeof(*ss)); + ss->ss_family = family; + sockaddr_port_set((struct sockaddr *)ss, port); + + /* Initialize address depending on address family. */ + if (family == AF_INET6) { + struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)ss; + if (inet_pton(family, straddr, &ipv6->sin6_addr) < 1) { + return KNOT_ERROR; + } + return KNOT_EOK; + } else if (family == AF_INET) { + struct sockaddr_in *ipv4 = (struct sockaddr_in *)ss; + if (inet_pton(family, straddr, &ipv4->sin_addr) < 1) { + return KNOT_ERROR; + } + return KNOT_EOK; + } else if (family == AF_UNIX) { + struct sockaddr_un *un = (struct sockaddr_un *)ss; + size_t ret = strlcpy(un->sun_path, straddr, sizeof(un->sun_path)); + if (ret >= sizeof(un->sun_path)) { + return KNOT_ESPACE; + } + return KNOT_EOK; + } + + return KNOT_EINVAL; +} + +void *sockaddr_raw(const struct sockaddr *sa, size_t *addr_size) +{ + if (sa == NULL || addr_size == NULL) { + return NULL; + } + + if (sa->sa_family == AF_INET) { + struct sockaddr_in *ipv4 = (struct sockaddr_in *)sa; + *addr_size = sizeof(ipv4->sin_addr); + return &ipv4->sin_addr; + } else if (sa->sa_family == AF_INET6) { + struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)sa; + *addr_size = sizeof(ipv6->sin6_addr); + return &ipv6->sin6_addr; + } else { + return NULL; + } +} + +int sockaddr_set_raw(struct sockaddr_storage *ss, int family, + const uint8_t *raw_addr, size_t raw_addr_size) +{ + if (ss == NULL || raw_addr == NULL) { + return KNOT_EINVAL; + } + + memset(ss, 0, sizeof(*ss)); + ss->ss_family = family; + + size_t sa_size = 0; + void *sa_data = sockaddr_raw((struct sockaddr *)ss, &sa_size); + if (sa_data == NULL || sa_size != raw_addr_size) { + return KNOT_EINVAL; + } + + memcpy(sa_data, raw_addr, sa_size); + + return KNOT_EOK; +} + +int sockaddr_tostr(char *buf, size_t maxlen, const struct sockaddr *sa) +{ + if (sa == NULL || buf == NULL) { + return KNOT_EINVAL; + } + + const char *out = NULL; + + /* Convert network address string. */ + if (sa->sa_family == AF_INET6) { + const struct sockaddr_in6 *s = (const struct sockaddr_in6 *)sa; + out = inet_ntop(sa->sa_family, &s->sin6_addr, buf, maxlen); + } else if (sa->sa_family == AF_INET) { + const struct sockaddr_in *s = (const struct sockaddr_in *)sa; + out = inet_ntop(sa->sa_family, &s->sin_addr, buf, maxlen); + } else if (sa->sa_family == AF_UNIX) { + const struct sockaddr_un *s = (const struct sockaddr_un *)sa; + size_t ret = strlcpy(buf, s->sun_path, maxlen); + out = (ret < maxlen) ? buf : NULL; + } else { + return KNOT_EINVAL; + } + + if (out == NULL) { + *buf = '\0'; + return KNOT_ESPACE; + } + + /* Write separator and port. */ + int written = strlen(buf); + int port = sockaddr_port(sa); + if (port > 0) { + int ret = snprintf(&buf[written], maxlen - written, "@%d", port); + if (ret <= 0 || (size_t)ret >= maxlen - written) { + *buf = '\0'; + return KNOT_ESPACE; + } + + written += ret; + } + + return written; +} + +int sockaddr_port(const struct sockaddr *sa) +{ + if (sa == NULL) { + return KNOT_EINVAL; + } + + if (sa->sa_family == AF_INET6) { + return ntohs(((struct sockaddr_in6 *)sa)->sin6_port); + } else if (sa->sa_family == AF_INET) { + return ntohs(((struct sockaddr_in *)sa)->sin_port); + } else { + return KNOT_EINVAL; + } +} + +void sockaddr_port_set(struct sockaddr *sa, uint16_t port) +{ + if (sa == NULL) { + return; + } + + if (sa->sa_family == AF_INET6) { + ((struct sockaddr_in6 *)sa)->sin6_port = htons(port); + } else if (sa->sa_family == AF_INET) { + ((struct sockaddr_in *)sa)->sin_port = htons(port); + } +} + +char *sockaddr_hostname(void) +{ + /* Fetch hostname. */ + char host[KNOT_DNAME_MAXLEN + 1] = { '\0' }; + if (gethostname(host, sizeof(host)) != 0) { + return NULL; + } + /* Just to be sure. */ + host[sizeof(host) - 1] = '\0'; + + /* Fetch canonical name for this address/DNS. */ + struct addrinfo hints, *info = NULL; + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_flags = AI_CANONNAME; + if (getaddrinfo(host, "domain", &hints, &info) != 0) { + return NULL; + } + + /* Fetch first valid hostname. */ + char *hname = NULL; + struct addrinfo *p = NULL; + for (p = info; p != NULL; p = p->ai_next) { + if (p->ai_canonname) { + hname = strdup(p->ai_canonname); + break; + } + } + + /* No valid hostname found, resort to gethostname() result */ + if (hname == NULL) { + hname = strdup(host); + } + + freeaddrinfo(info); + return hname; +} + +bool sockaddr_is_any(const struct sockaddr *sa) +{ + if (sa == NULL) { + return false; + } + + if (sa->sa_family == AF_INET) { + const struct sockaddr_in *ipv4 = (struct sockaddr_in *)sa; + return ipv4->sin_addr.s_addr == INADDR_ANY; + } + + if (sa->sa_family == AF_INET6) { + const struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)sa; + return memcmp(&ipv6->sin6_addr, &in6addr_any, sizeof(ipv6->sin6_addr)) == 0; + } + + return false; +} + +bool sockaddr_net_match(const struct sockaddr *ss1, + const struct sockaddr *ss2, + unsigned prefix) +{ + if (ss1 == NULL || ss2 == NULL) { + return false; + } + + if (ss1->sa_family != ss2->sa_family) { + return false; + } + + size_t raw_len = 0; + const uint8_t *raw_1 = sockaddr_raw(ss1, &raw_len); + const uint8_t *raw_2 = sockaddr_raw(ss2, &raw_len); + + prefix = MIN(prefix, raw_len * 8); + unsigned bytes = prefix / 8; + unsigned bits = prefix % 8; + + /* Compare full bytes. */ + if (memcmp(raw_1, raw_2, bytes) != 0) { + return false; + } + + /* Compare last partial byte. */ + return bits == 0 || + (raw_1[bytes] >> (8 - bits) == raw_2[bytes] >> (8 - bits)); +} + +bool sockaddr_range_match(const struct sockaddr *sa, + const struct sockaddr *ss_min, + const struct sockaddr *ss_max) +{ + if (sa == NULL || ss_min == NULL || ss_max == NULL) { + return false; + } + + if (ss_min->sa_family != ss_max->sa_family || + ss_min->sa_family != sa->sa_family) { + return false; + } + + return sockaddr_cmp(sa, ss_min) >= 0 && sockaddr_cmp(sa, ss_max) <= 0; +} diff --git a/src/contrib/sockaddr.h b/src/contrib/sockaddr.h new file mode 100644 index 0000000..0fbf1ab --- /dev/null +++ b/src/contrib/sockaddr.h @@ -0,0 +1,160 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +/* BSD IPv6 */ +#ifndef __POSIX_VISIBLE +#define __POSIX_VISIBLE 200112 +#endif + +#include <stdbool.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <sys/un.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <stdint.h> +#include <unistd.h> + +/* Subnet maximum prefix length. */ +#define IPV4_PREFIXLEN 32 +#define IPV6_PREFIXLEN 128 + +/* Address string "address[@port]" maximum length. */ +#define SOCKADDR_STRLEN_EXT (1 + 6) /* '@', 5 digits number, \0 */ +#define SOCKADDR_STRLEN (sizeof(struct sockaddr_un) + SOCKADDR_STRLEN_EXT) + +/*! + * \brief Calculate current structure length based on address family. + * + * \param sa Socket address. + * + * \return Number of bytes or error code. + */ +int sockaddr_len(const struct sockaddr *sa); + +/*! + * \brief Compare addresses. + * + * \return like memcmp(3) + */ +int sockaddr_cmp(const struct sockaddr *k1, const struct sockaddr *k2); + +/*! + * \brief Set address and port. + * + * \param ss Socket address. + * \param family Address family. + * \param straddr IP address in string format. + * \param port Port. + * + * \return KNOT_EOK on success or an error code. + */ +int sockaddr_set(struct sockaddr_storage *ss, int family, const char *straddr, int port); + +/*! + * \brief Return raw network address in network byte order. + * + * \param[in] sa Socket address. + * \param[out] addr_size Address length. + * + * \return Pointer to binary buffer of size addr_size. + */ +void *sockaddr_raw(const struct sockaddr *sa, size_t *addr_size); + +/*! + * \brief Set raw address. + * + * \param ss Socket address. + * \param family Address family. + * \param raw_addr IP address in binary format. + * \param raw_addr_size Size of the binary address. + * + * \return KNOT_EOK on success or an error code. + */ +int sockaddr_set_raw(struct sockaddr_storage *ss, int family, + const uint8_t *raw_addr, size_t raw_addr_size); + +/*! + * \brief Return string representation of socket address. + * + * \note String format: \<address>[@<port>], f.e. '127.0.0.1@53' + * + * \param buf Destination for string representation. + * \param maxlen Maximum number of written bytes. + * \param sa Socket address. + * + * \return Number of bytes written on success, error code on failure. + */ +int sockaddr_tostr(char *buf, size_t maxlen, const struct sockaddr *sa); + +/*! + * \brief Return port number from address. + * + * \param sa Socket address. + * + * \return Port number or error code. + */ +int sockaddr_port(const struct sockaddr *sa); + +/*! + * \brief Set port number. + * + * \param sa Socket address. + * \param port Port to set. + */ +void sockaddr_port_set(struct sockaddr *sa, uint16_t port); + +/*! + * \brief Get host FQDN address. + * + * \return Hostname string or NULL. + */ +char *sockaddr_hostname(void); + +/*! + * \brief Check if address is ANY address. + * + * \param sa Socket address. + */ +bool sockaddr_is_any(const struct sockaddr *sa); + +/*! + * \brief Check if two addresses match the given network prefix. + * + * \param sa1 First address. + * \param sa2 Second address. + * \param prefix Prefix length. + * + * \return True on match. + */ +bool sockaddr_net_match(const struct sockaddr *sa1, + const struct sockaddr *sa2, + unsigned prefix); + +/*! + * \brief Check if the address is within the given address range (inclusive). + * + * \param sa Address to check. + * \param sa_min Minimum address. + * \param sa_max Maximum address. + * + * \return True on match. + */ +bool sockaddr_range_match(const struct sockaddr *sa, + const struct sockaddr *sa_min, + const struct sockaddr *sa_max); diff --git a/src/contrib/string.c b/src/contrib/string.c new file mode 100644 index 0000000..6e0eea9 --- /dev/null +++ b/src/contrib/string.c @@ -0,0 +1,112 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "contrib/string.h" +#include "contrib/ctype.h" + +uint8_t *memdup(const uint8_t *data, size_t data_size) +{ + uint8_t *result = (uint8_t *)malloc(data_size); + if (!result) { + return NULL; + } + + return memcpy(result, data, data_size); +} + +char *sprintf_alloc(const char *fmt, ...) +{ + char *strp = NULL; + va_list ap; + + va_start(ap, fmt); + int ret = vasprintf(&strp, fmt, ap); + va_end(ap); + + if (ret < 0) { + return NULL; + } + return strp; +} + +char *strcdup(const char *s1, const char *s2) +{ + if (!s1 || !s2) { + return NULL; + } + + size_t s1len = strlen(s1); + size_t s2len = strlen(s2); + size_t nlen = s1len + s2len + 1; + + char* dst = malloc(nlen); + if (dst == NULL) { + return NULL; + } + + memcpy(dst, s1, s1len); + memcpy(dst + s1len, s2, s2len + 1); + return dst; +} + +char *strstrip(const char *str) +{ + // leading white-spaces + const char *scan = str; + while (is_space(scan[0])) { + scan += 1; + } + + // trailing white-spaces + size_t len = strlen(scan); + while (len > 0 && is_space(scan[len - 1])) { + len -= 1; + } + + char *trimmed = malloc(len + 1); + if (!trimmed) { + return NULL; + } + + memcpy(trimmed, scan, len); + trimmed[len] = '\0'; + + return trimmed; +} + +int const_time_memcmp(const void *s1, const void *s2, size_t n) +{ + volatile uint8_t equal = 0; + + for (size_t i = 0; i < n; i++) { + equal |= ((uint8_t *)s1)[i] ^ ((uint8_t *)s2)[i]; + } + + return equal; +} + +typedef void *(*memset_t)(void *, int, size_t); +static volatile memset_t volatile_memset = memset; + +void *memzero(void *s, size_t n) +{ + return volatile_memset(s, 0, n); +} diff --git a/src/contrib/string.h b/src/contrib/string.h new file mode 100644 index 0000000..19c74c7 --- /dev/null +++ b/src/contrib/string.h @@ -0,0 +1,82 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \brief String manipulations. + */ + +#pragma once + +#include <stddef.h> +#include <stdint.h> + +/*! + * \brief Create a copy of a binary buffer. + * + * Like \c strdup, but for binary data. + */ +uint8_t *memdup(const uint8_t *data, size_t data_size); + +/*! + * \brief Format string and take care of allocating memory. + * + * \note sprintf(3) manual page reference implementation. + * + * \param fmt Message format. + * \return formatted message or NULL. + */ +char *sprintf_alloc(const char *fmt, ...); + +/*! + * \brief Create new string from a concatenation of s1 and s2. + * + * \param s1 First string. + * \param s2 Second string. + * + * \retval Newly allocated string on success. + * \retval NULL on error. + */ +char *strcdup(const char *s1, const char *s2); + +/*! + * \brief Create a copy of a string skipping leading and trailing white spaces. + * + * \return Newly allocated string, NULL in case of error. + */ +char *strstrip(const char *str); + +/*! + * \brief Compare data in time based on string length. + * This function just checks for (in)equality not for relation + * + * \param s1 The first address to compare. + * \param s2 The second address to compare. + * \param n The size of memory to compare. + * + * \return Non zero on difference and zero if the buffers are identical. + */ +int const_time_memcmp(const void *s1, const void *s2, size_t n); + +/*! + * \brief Fill memory with zeroes. + * + * Inspired by OPENSSL_cleanse. Such a memset shouldn't be optimized out. + * + * \param s The address to fill. + * \param n The size of memory to fill. + * + * \return Pointer to the memory. + */ +void *memzero(void *s, size_t n); diff --git a/src/contrib/strtonum.h b/src/contrib/strtonum.h new file mode 100644 index 0000000..6cfe8bb --- /dev/null +++ b/src/contrib/strtonum.h @@ -0,0 +1,120 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include <assert.h> +#include <errno.h> +#include <inttypes.h> +#include <limits.h> +#include <stddef.h> +#include <stdint.h> + +#include "libknot/errcode.h" +#include "contrib/ctype.h" + +inline static int intmax_from_str(const char *src, intmax_t *dst, + intmax_t min, intmax_t max) +{ + if (!is_digit(*src) && *src != '-' && *src != '+') { + return KNOT_EINVAL; + } + + errno = 0; + char *end = NULL; + intmax_t result = strtoimax(src, &end, 10); + + if (errno == ERANGE) { + return KNOT_ERANGE; + } + + if (src == end || *end != '\0') { + return KNOT_EINVAL; + } + + if (result < min || result > max) { + return KNOT_ERANGE; + } + + *dst = result; + return KNOT_EOK; +} + +inline static int uintmax_from_str(const char *src, uintmax_t *dst, + uintmax_t min, uintmax_t max) +{ + if (!is_digit(*src) && *src != '+') { + return KNOT_EINVAL; + } + + errno = 0; + char *end = NULL; + uintmax_t result = strtoumax(src, &end, 10); + + if (errno == ERANGE) { + return KNOT_ERANGE; + } + + if (src == end || *end != '\0') { + return KNOT_EINVAL; + } + + if (result < min || result > max) { + return KNOT_ERANGE; + } + + *dst = result; + return KNOT_EOK; +} + +#define CONVERT(prefix, type, min, max, src, dst) \ +{ \ + assert(src && dst); \ + prefix##max_t value; \ + int result = prefix##max_from_str(src, &value, min, max); \ + if (result != KNOT_EOK) { \ + return result; \ + } \ + *dst = (type)value; \ + return KNOT_EOK; \ +} + +inline static int str_to_int(const char *src, int *dst, int min, int max) +{ + CONVERT(int, int, min, max, src, dst); +} + +inline static int str_to_u8(const char *src, uint8_t *dst) +{ + CONVERT(uint, uint8_t, 0, UINT8_MAX, src, dst); +} + +inline static int str_to_u16(const char *src, uint16_t *dst) +{ + CONVERT(uint, uint16_t, 0, UINT16_MAX, src, dst); +} + +inline static int str_to_u32(const char *src, uint32_t *dst) +{ + CONVERT(uint, uint32_t, 0, UINT32_MAX, src, dst); +} + +inline static int str_to_size(const char *src, size_t *dst, size_t min, size_t max) +{ + CONVERT(uint, size_t, min, max, src, dst); +} + +#undef CONVERT diff --git a/src/contrib/time.c b/src/contrib/time.c new file mode 100644 index 0000000..8c32c2d --- /dev/null +++ b/src/contrib/time.c @@ -0,0 +1,405 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> +#include <limits.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "contrib/time.h" +#include "contrib/ctype.h" +#ifndef HAVE_CLOCK_GETTIME + #include <sys/time.h> +#endif + +struct timespec time_now(void) +{ + struct timespec result = { 0 }; + +#ifdef HAVE_CLOCK_GETTIME + clock_gettime(CLOCK_MONOTONIC, &result); +#else // OS X < Sierra fallback. + struct timeval tmp = { 0 }; + gettimeofday(&tmp, NULL); + result.tv_sec = tmp.tv_sec; + result.tv_nsec = 1000 * tmp.tv_usec; +#endif + + return result; +} + +struct timespec time_diff(const struct timespec *begin, const struct timespec *end) +{ + struct timespec result = { 0 }; + + if (end->tv_nsec >= begin->tv_nsec) { + result.tv_sec = end->tv_sec - begin->tv_sec; + result.tv_nsec = end->tv_nsec - begin->tv_nsec; + } else { + result.tv_sec = end->tv_sec - begin->tv_sec - 1; + result.tv_nsec = 1000000000 - begin->tv_nsec + end->tv_nsec; + } + + return result; +} + +double time_diff_ms(const struct timespec *begin, const struct timespec *end) +{ + struct timespec result = time_diff(begin, end); + + return (result.tv_sec * 1e3) + (result.tv_nsec / 1e6); +} + +typedef struct { + const char *format; + const char *timespec; + const char *parsed; + knot_timediff_t offset; + char offset_sign; + char offset_unit; + struct tm calendar; + int error; +} time_ctx_t; + +// After casting (struct tm) to (int []), we can use indexes... +static int calendar_index(char ind) +{ + switch (ind) { + case 'Y': return 5; + case 'M': return 4; + case 'D': return 3; + case 'h': return 2; + case 'm': return 1; + case 's': return 0; + default: assert(0); return 6; + } +} + +static size_t calendar_digits(int index) +{ + return index == 5 ? 4 : 2; +} + +static size_t unit_value(char unit) +{ + size_t val = 1; + switch (unit) { + case 'M': + return 3600 * 24 * 30; + case 'Y': + val *= 365; + // FALLTHROUGH + case 'D': + val *= 24; + // FALLTHROUGH + case 'h': + val *= 60; + // FALLTHROUGH + case 'm': + val *= 60; + // FALLTHROUGH + case 's': + default: + return val; + } +} + +static knot_time_t time_ctx_finalize(time_ctx_t *ctx) +{ + if (ctx->offset_sign) { + ctx->offset *= unit_value(ctx->offset_unit); + return knot_time_add(knot_time(), (ctx->offset_sign == '-' ? -1 : 1) * ctx->offset); + } else if (ctx->offset) { + return (knot_time_t)ctx->offset; + } else if (ctx->calendar.tm_year != 0) { + ctx->calendar.tm_isdst = -1; + ctx->calendar.tm_year -= 1900; + ctx->calendar.tm_mon -= 1; + // Set UTC timezone before using mktime + putenv("TZ=UTC"); + tzset(); + return (knot_time_t)mktime(&ctx->calendar); + } else { + return (knot_time_t)0; + } +} + +static void time_ctx_reset(time_ctx_t *ctx) +{ + ctx->parsed = ctx->timespec; + ctx->offset = 0; + ctx->offset_sign = 0; + memset(&ctx->calendar, 0, sizeof(ctx->calendar)); + ctx->error = 0; +} + +static void parse_quote(time_ctx_t *ctx) +{ + while (*ctx->format != '|' && *ctx->format != '\0') { + if (*ctx->format == '\'') { + ctx->format++; + return; + } + if (*ctx->format++ != *ctx->parsed++) { + ctx->error = -1; + return; + } + } + ctx->error = -2; + return; +} + +static void parse_offset(time_ctx_t *ctx) +{ + ctx->offset = 0; + ctx->error = -1; + while (is_digit(*ctx->parsed)) { + ctx->offset *= 10; + ctx->offset += *ctx->parsed++ - '0'; + ctx->error = 0; + } +} + +static void parse_calendar(time_ctx_t *ctx, int index) +{ + int *cal_arr = (int *)&ctx->calendar; + cal_arr[index] = 0; + for (size_t i = 0; i < calendar_digits(index); i++) { + if (!is_digit(*ctx->parsed)) { + ctx->error = -1; + return; + } + cal_arr[index] *= 10; + cal_arr[index] += *ctx->parsed++ - '0'; + } +} + +static void parse_sign(time_ctx_t *ctx) +{ + char sign1 = *(ctx->format - 1), sign2 = *ctx->format; + + bool use_sign2 = (sign2 == '+' || sign2 == '-'); + + bool allow_plus = (sign1 == '+' || (sign1 == '-' && sign2 == '+')); + bool allow_minus = (sign1 == '-' || (sign1 == '+' && sign2 == '-')); + assert(sign1 == '+' || sign1 == '-'); + + if ((*ctx->parsed == '+' && allow_plus) || (*ctx->parsed == '-' && allow_minus)) { + ctx->offset_sign = *ctx->parsed++; + ctx->format += (use_sign2 ? 1 : 0); + } else { + ctx->error = -11; + } +} + +static void parse_unit1(time_ctx_t *ctx) +{ + char u = *ctx->parsed++; + switch (u) { + case 'Y': + case 'M': + case 'D': + case 'h': + case 'm': + case 's': + ctx->offset_unit = u; + break; + default: + ctx->error = -1; + } +} + +static void parse_unit2(time_ctx_t *ctx) +{ + char u = *ctx->parsed++; + switch (u) { + case 'y': + case 'd': + ctx->offset_unit = toupper((unsigned char)u); + break; + case 'h': + case 's': + ctx->offset_unit = u; + break; + case 'm': + switch (*ctx->parsed++) { + case 'o': + ctx->offset_unit = 'M'; + break; + case 'i': + ctx->offset_unit = 'm'; + break; + default: + ctx->error = -1; + } + break; + default: + ctx->error = -1; + } +} + +int knot_time_parse(const char *format, const char *timespec, knot_time_t *time) +{ + if (format == NULL || timespec == NULL || time == NULL) { + return -1; + } + + time_ctx_t ctx = { + .format = format, + .timespec = timespec, + .parsed = timespec, + .offset = 0, + .offset_sign = 0, + // we hope that .calendar is zeroed by default + .error = 0, + }; + + while (ctx.error == 0 && *ctx.format != '\0') { + switch (*ctx.format++) { + case '|': + if (*ctx.parsed == '\0') { + *time = time_ctx_finalize(&ctx); + return 0; + } else { + time_ctx_reset(&ctx); + } + break; + case '\'': + parse_quote(&ctx); + break; + case '#': + parse_offset(&ctx); + break; + case 'Y': + case 'M': + case 'D': + case 'h': + case 'm': + case 's': + parse_calendar(&ctx, calendar_index(*(ctx.format - 1))); + break; + case '+': + case '-': + parse_sign(&ctx); + break; + case 'U': + parse_unit1(&ctx); + break; + case 'u': + parse_unit2(&ctx); + break; + default: + return -1; + } + + if (ctx.error < 0) { + while (*ctx.format != '|' && *ctx.format != '\0') { + ctx.format++; + } + time_ctx_reset(&ctx); + ctx.error = (*ctx.format == '\0' ? -1 : 0); + } + } + + if (ctx.error == 0 && *ctx.parsed == '\0') { + *time = time_ctx_finalize(&ctx); + return 0; + } + return -1; +} + +static char *unit_names_mixed[] = { "Y", "M", "D", "h", "m", "s" }; +static char *unit_names_lower[] = { "y", "mo", "d", "h", "mi", "s" }; +static size_t unit_sizes[] = { 3600*24*365, 3600*24*30, 3600*24, 3600, 60, 1 }; +static const size_t unit_count = 6; + +static int print_unit(char *dst, size_t dst_len, char *unit_names[unit_count], + size_t max_units, knot_time_t time) +{ + int ret; + if (time == 0) { + ret = snprintf(dst, dst_len, "0"); + return (ret < 0 || ret >= dst_len ? -1 : 0); + } + knot_timediff_t diff = knot_time_diff(time, knot_time()); + if (dst_len-- < 1) { + return -1; + } + *dst++ = (diff < 0 ? '-' : '+'); + if (diff < 0) { + diff = -diff; + } else if (diff == 0) { + ret = snprintf(dst, dst_len, "0%s", unit_names[unit_count - 1]); + return (ret < 0 || ret >= dst_len ? -1 : 0); + } + size_t curr_unit = 0, used_units = 0; + while (curr_unit < unit_count && used_units < max_units) { + if (diff >= unit_sizes[curr_unit]) { + ret = snprintf(dst, dst_len, "%"KNOT_TIMEDIFF_PRINTF"%s", + diff / unit_sizes[curr_unit], + unit_names[curr_unit]); + if (ret < 0 || ret >= dst_len) { + return -1; + } + dst += ret; + dst_len -= ret; + used_units++; + diff %= unit_sizes[curr_unit]; + } + curr_unit++; + } + return 0; +} + +int knot_time_print(knot_time_print_t format, knot_time_t time, char *dst, size_t dst_len) +{ + if (dst == NULL) { + return -1; + } + + int ret; + switch (format) { + case TIME_PRINT_UNIX: + ret = snprintf(dst, dst_len, "%"KNOT_TIME_PRINTF, time); + return ((ret >= 0 && ret < dst_len) ? 0 : -1); + case TIME_PRINT_ISO8601: + if (time > LONG_MAX) { + return -1; + } + + // Set timezone to UTC before using timezone dependent functions + putenv("TZ=UTC"); + tzset(); + + struct tm lt; + time_t tt = (time_t)time; + ret = (localtime_r(&tt, <) == NULL ? -1 : + strftime(dst, dst_len, "%Y-%m-%dT%H:%M:%SZ", <)); + return (ret > 0 ? 0 : -1); + case TIME_PRINT_RELSEC: + ret = snprintf(dst, dst_len, "%+"KNOT_TIMEDIFF_PRINTF, + knot_time_diff(time, knot_time())); + return ((ret >= 0 && ret < dst_len) ? 0 : -1); + case TIME_PRINT_HUMAN_MIXED: + return print_unit(dst, dst_len, unit_names_mixed, unit_count, time); + case TIME_PRINT_HUMAN_LOWER: + return print_unit(dst, dst_len, unit_names_lower, unit_count, time); + default: + return -1; + } +} diff --git a/src/contrib/time.h b/src/contrib/time.h new file mode 100644 index 0000000..55b8727 --- /dev/null +++ b/src/contrib/time.h @@ -0,0 +1,171 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <stdint.h> +#include <time.h> +#include <inttypes.h> + +/*! + * \brief Specify output format for knot_time_print(). + */ +typedef enum { + TIME_PRINT_UNIX, // numeric UNIX time + TIME_PRINT_ISO8601, // 2016-12-31T23:59:00 + TIME_PRINT_RELSEC, // relative +6523 + TIME_PRINT_HUMAN_MIXED, // relative with mixed-case units + TIME_PRINT_HUMAN_LOWER, // relative with lower-case units +} knot_time_print_t; + +/*! + * \brief Get current time. + */ +struct timespec time_now(void); + +/*! + * \brief Get time elapsed between two events. + */ +struct timespec time_diff(const struct timespec *begin, const struct timespec *end); + +/*! + * \brief Get time elapsed between two events in miliseconds. + */ +double time_diff_ms(const struct timespec *begin, const struct timespec *end); + +/*! + * \brief Data type for keeping UNIX timestamps. + * + * This is because time_t can be 32-bit on some systems, which is bad. + * Zero value represents infinity. + */ +typedef uint64_t knot_time_t; + +/*! + * \brief Data type for keeping time differences. + */ +typedef int64_t knot_timediff_t; + +#define KNOT_TIMEDIFF_MIN INT64_MIN +#define KNOT_TIMEDIFF_MAX INT64_MAX + +#define KNOT_TIME_PRINTF PRIu64 +#define KNOT_TIMEDIFF_PRINTF PRId64 + +/*! + * \brief Returns current time sice epoch. + */ +inline static knot_time_t knot_time(void) +{ + return (knot_time_t)time(NULL); +} + +/*! + * \brief Compare two timestamps. + * + * \return 0 if equal, -1 if the former is smaller (=earlier), 1 else. + */ +inline static int knot_time_cmp(knot_time_t a, knot_time_t b) +{ + return (a == b ? 0 : 1) * ((a && b) == 0 ? -1 : 1) * (a < b ? -1 : 1); +} + +/*! + * \brief Return the smaller (=earlier) from given two timestamps. + */ +inline static knot_time_t knot_time_min(knot_time_t a, knot_time_t b) +{ + if ((a && b) == 0) { + return a + b; + } else { + return (a < b ? a : b); + } +} + +/*! + * \brief Return the difference between two timestamps (to "minus" from). + * + * \note If both are zero (=infinity), KNOT_TIMEDIFF_MAX is returned. + */ +inline static knot_timediff_t knot_time_diff(knot_time_t to, knot_time_t from) +{ + if ((to && from) == 0) { + return (to > from ? KNOT_TIMEDIFF_MIN : KNOT_TIMEDIFF_MAX); + } else { + return (knot_timediff_t)to - (knot_timediff_t)from; + } +} + +/*! + * \brief Add a time difference to timestamp. + */ +inline static knot_time_t knot_time_add(knot_time_t since, knot_timediff_t howlong) +{ + return (since != 0 ? since + howlong : since); +} + +/*! + * \brief Convert uint32_t-encoded timestamp to knot_time_t. + * + * In RRSIG rdata, there are inception and expiration timestamps in uint32_t format. + * One shall use 'serial arithmetics' to decode them. + * + * \todo However it needs time(now) context which is slow to obtain, so we don't do it + * for now. Please fix this in next 100 years. + */ +inline static knot_time_t knot_time_from_u32(uint32_t u32time) +{ + return (knot_time_t)u32time; +} + +/*! + * \brief Parse a text-formatted timestamp to knot_time_t using format specification. + * + * \param format The timestamp text format specification. + * \param timespec Text-formatted timestamp. + * \param time The parsed timestamp. + * + * The format specification basics: + * <format 1>|<format 2> - The pipe sign separates two time format specifications. Leftmost + * specification matching the timespec is used. + * '<a string>' - Matches exactly <a string> (not containing apostrophes) in timespec. + * # - Hashtag matches for a number in timespec, stands for either a UNIX timestamp, + * or, within a context of an unit, as a number of such units. + * Y, M, D, h, m, s - Matches a number, stands for a number of years, months, days, hours, + * minutes and seconds, respectively. + * +, - - The + and - signs declaring that following timespec is relative to "now". + * A single sign can be used to limit the timestamp being in future or in past, + * or both +- allow the timestamp to select any (just one) of them. + * U - Matches one of Y, M, D, h, m, s in the timespec standing for a time unit. + * u - Like U, but the unit in the timestamp is from: y, mo, d, h, mi, s. + * + * \retval -1 An error occurred, out_time has no sense. + * \return 0 OK, timestamp parsed successfully. + */ +int knot_time_parse(const char *format, const char *timespec, knot_time_t *time); + +/*! + * \brief Print the timestamp in specified format into a string buffer. + * + * \param format The timestamp text format specification. + * \param time The timestamp to be printed. + * \param dst The destination buffer pointer with text-formatted timestamp. + * \param dst_len The destination buffer length. + * + * \retval -1 An error occurred, the buffer may be filled with nonsense. + * \return 0 OK, timestamp printed successfully. + */ +int knot_time_print(knot_time_print_t format, knot_time_t time, char *dst, size_t dst_len); diff --git a/src/contrib/tolower.h b/src/contrib/tolower.h new file mode 100644 index 0000000..8f55182 --- /dev/null +++ b/src/contrib/tolower.h @@ -0,0 +1,51 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \brief Table for converting ASCII characters to lowercase. + */ + +#pragma once + +#include <stdint.h> + +/*! + * \brief Converts binary character to lowercase. + * + * \param c Character code. + * + * \return \a c converted to lowercase (or \a c if not applicable). + */ +static inline uint8_t knot_tolower(uint8_t c) { + const uint8_t *tolower_table = (uint8_t *) + "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" + "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F" + "\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2A\x2B\x2C\x2D\x2E\x2F" + "\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3A\x3B\x3C\x3D\x3E\x3F" + "\x40\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6A\x6B\x6C\x6D\x6E\x6F" + "\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7A\x5B\x5C\x5D\x5E\x5F" + "\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6A\x6B\x6C\x6D\x6E\x6F" + "\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7A\x7B\x7C\x7D\x7E\x7F" + "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F" + "\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F" + "\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF" + "\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF" + "\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF" + "\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF" + "\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF" + "\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xFF"; + + return tolower_table[c]; +} diff --git a/src/contrib/trim.h b/src/contrib/trim.h new file mode 100644 index 0000000..c1b83fa --- /dev/null +++ b/src/contrib/trim.h @@ -0,0 +1,35 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +/*! + * \brief Heap memory trimmer. + */ + +#pragma once + +#ifdef HAVE_MALLOC_TRIM +#include <malloc.h> +#endif + +/*! + * \brief Trim excess heap memory. + */ +static inline void mem_trim(void) +{ +#ifdef HAVE_MALLOC_TRIM + malloc_trim(0); +#endif + return; +} diff --git a/src/contrib/ucw/LICENSE b/src/contrib/ucw/LICENSE new file mode 100644 index 0000000..b463d57 --- /dev/null +++ b/src/contrib/ucw/LICENSE @@ -0,0 +1 @@ +../licenses/LGPL-2.0
\ No newline at end of file diff --git a/src/contrib/ucw/array-sort.h b/src/contrib/ucw/array-sort.h new file mode 100644 index 0000000..1ff1377 --- /dev/null +++ b/src/contrib/ucw/array-sort.h @@ -0,0 +1,195 @@ +/* + * UCW Library -- Universal Simple Array Sorter + * + * (c) 2003--2008 Martin Mares <mj@ucw.cz> + * + * This software may be freely distributed and used according to the terms + * of the GNU Lesser General Public License. + */ + +#pragma once + +#include "contrib/macros.h" + +/* + * This is not a normal header file, it's a generator of sorting + * routines. Each time you include it with parameters set in the + * corresponding preprocessor macros, it generates an array sorter + * with the parameters given. + * + * You might wonder why the heck do we implement our own array sorter + * instead of using qsort(). The primary reason is that qsort handles + * only continuous arrays, but we need to sort array-like data structures + * where the only way to access elements is by using an indexing macro. + * Besides that, we are more than 2 times faster. + * + * So much for advocacy, there are the parameters (those marked with [*] + * are mandatory): + * + * ASORT_PREFIX(x) [*] add a name prefix (used on all global names + * defined by the sorter) + * ASORT_KEY_TYPE [*] data type of a single array entry key + * ASORT_ELT(i) returns the key of i-th element; if this macro is not + * defined, the function gets a pointer to an array to be sorted + * ASORT_LT(x,y) x < y for ASORT_KEY_TYPE (default: "x<y") + * ASORT_SWAP(i,j) swap i-th and j-th element (default: assume _ELT + * is an l-value and swap just the keys) + * ASORT_THRESHOLD threshold for switching between quicksort and insertsort + * ASORT_EXTRA_ARGS extra arguments for the sort function (they are always + * visible in all the macros supplied above), starts with comma + * + * After including this file, a function ASORT_PREFIX(sort)(unsigned array_size) + * or ASORT_PREFIX(sort)(ASORT_KEY_TYPE *array, unsigned array_size) [if ASORT_ELT + * is not defined] is declared and all parameter macros are automatically + * undef'd. + */ + +#ifndef ASORT_LT +#define ASORT_LT(x,y) ((x) < (y)) +#endif + +#ifndef ASORT_SWAP +#define ASORT_SWAP(i,j) do { ASORT_KEY_TYPE tmp = ASORT_ELT(i); ASORT_ELT(i)=ASORT_ELT(j); ASORT_ELT(j)=tmp; } while (0) +#endif + +#ifndef ASORT_THRESHOLD +#define ASORT_THRESHOLD 8 /* Guesswork and experimentation */ +#endif + +#ifndef ASORT_EXTRA_ARGS +#define ASORT_EXTRA_ARGS +#endif + +#ifndef ASORT_ELT +#define ASORT_ARRAY_ARG ASORT_KEY_TYPE *array, +#define ASORT_ELT(i) array[i] +#else +#define ASORT_ARRAY_ARG +#endif + +/** + * The generated sorting function. If `ASORT_ELT` macro is not provided, the + * @ASORT_ARRAY_ARG is equal to `ASORT_KEY_TYPE *array` and is the array to be + * sorted. If the macro is provided, this parameter is omitted. In that case, + * you can sort global variables or pass your structure by @ASORT_EXTRA_ARGS. + **/ +static void ASORT_PREFIX(sort)(ASORT_ARRAY_ARG unsigned array_size ASORT_EXTRA_ARGS) +{ + struct stk { int l, r; } stack[8*sizeof(unsigned)]; + int l, r, left, right, m; + unsigned sp = 0; + ASORT_KEY_TYPE pivot; + + if (array_size <= 1) + return; + + /* QuickSort with optimizations a'la Sedgewick, but stop at ASORT_THRESHOLD */ + + left = 0; + right = array_size - 1; + for(;;) + { + l = left; + r = right; + m = (l+r)/2; + if (ASORT_LT(ASORT_ELT(m), ASORT_ELT(l))) + ASORT_SWAP(l,m); + if (ASORT_LT(ASORT_ELT(r), ASORT_ELT(m))) + { + ASORT_SWAP(m,r); + if (ASORT_LT(ASORT_ELT(m), ASORT_ELT(l))) + ASORT_SWAP(l,m); + } + pivot = ASORT_ELT(m); + do + { + while (ASORT_LT(ASORT_ELT(l), pivot)) + l++; + while (ASORT_LT(pivot, ASORT_ELT(r))) + r--; + if (l < r) + { + ASORT_SWAP(l,r); + l++; + r--; + } + else if (l == r) + { + l++; + r--; + } + } + while (l <= r); + if ((r - left) >= ASORT_THRESHOLD && (right - l) >= ASORT_THRESHOLD) + { + /* Both partitions ok => push the larger one */ + if ((r - left) > (right - l)) + { + stack[sp].l = left; + stack[sp].r = r; + left = l; + } + else + { + stack[sp].l = l; + stack[sp].r = right; + right = r; + } + sp++; + } + else if ((r - left) >= ASORT_THRESHOLD) + { + /* Left partition OK, right undersize */ + right = r; + } + else if ((right - l) >= ASORT_THRESHOLD) + { + /* Right partition OK, left undersize */ + left = l; + } + else + { + /* Both partitions undersize => pop */ + if (!sp) + break; + sp--; + left = stack[sp].l; + right = stack[sp].r; + } + } + + /* + * We have a partially sorted array, finish by insertsort. Inspired + * by qsort() in GNU libc. + */ + + /* Find minimal element which will serve as a barrier */ + r = MIN(array_size, ASORT_THRESHOLD); + m = 0; + for (l=1; l<r; l++) + if (ASORT_LT(ASORT_ELT(l),ASORT_ELT(m))) + m = l; + ASORT_SWAP(0,m); + + /* Insertion sort */ + for (m=1; m<(int)array_size; m++) + { + l=m; + while (ASORT_LT(ASORT_ELT(m),ASORT_ELT(l-1))) + l--; + while (l < m) + { + ASORT_SWAP(l,m); + l++; + } + } +} + +#undef ASORT_PREFIX +#undef ASORT_KEY_TYPE +#undef ASORT_ELT +#undef ASORT_LT +#undef ASORT_SWAP +#undef ASORT_THRESHOLD +#undef ASORT_EXTRA_ARGS +#undef ASORT_ARRAY_ARG diff --git a/src/contrib/ucw/binsearch.h b/src/contrib/ucw/binsearch.h new file mode 100644 index 0000000..b791d39 --- /dev/null +++ b/src/contrib/ucw/binsearch.h @@ -0,0 +1,50 @@ +/* + * UCW Library -- Generic Binary Search + * + * (c) 2005 Martin Mares <mj@ucw.cz> + * + * This software may be freely distributed and used according to the terms + * of the GNU Lesser General Public License. + */ + +#pragma once + +/*** + * [[defs]] + * Definitions + * ----------- + ***/ + +/** + * Find the first element not lower than \p x in the sorted array \p ary of \p N elements (non-decreasing order). + * Returns the index of the found element or \p N if no exists. Uses `ary_lt_x(ary,i,x)` to compare the i'th element with \p x. + * The time complexity is `O(log(N))`. + **/ +#define BIN_SEARCH_FIRST_GE_CMP(ary, N, ary_lt_x, x, ...) ({ \ + unsigned l = 0, r = (N); \ + while (l < r) \ + { \ + unsigned m = (l+r)/2; \ + if (ary_lt_x(ary, m, x, __VA_ARGS__)) \ + l = m+1; \ + else \ + r = m; \ + } \ + l; \ +}) + +/** + * The default comparison macro for \ref BIN_SEARCH_FIRST_GE_CMP(). + **/ +#define ARY_LT_NUM(ary,i,x) (ary)[i] < (x) + +/** + * Same as \ref BIN_SEARCH_FIRST_GE_CMP(), but uses the default `<` operator for comparisons. + **/ +#define BIN_SEARCH_FIRST_GE(ary,N,x) BIN_SEARCH_FIRST_GE_CMP(ary,N,ARY_LT_NUM,x) + +/** + * Search the sorted array \p ary of \p N elements (non-decreasing) for the first occurrence of \p x. + * Returns the index or -1 if no such element exists. Uses the `<` operator for comparisons. + **/ +#define BIN_SEARCH_EQ(ary,N,x) ({ int i = BIN_SEARCH_FIRST_GE(ary,N,x); if (i >= (N) || (ary)[i] != (x)) i=-1; i; }) diff --git a/src/contrib/ucw/heap.c b/src/contrib/ucw/heap.c new file mode 100644 index 0000000..d7ed18e --- /dev/null +++ b/src/contrib/ucw/heap.c @@ -0,0 +1,166 @@ +/* + * Binary heap + * + * (c) 2012 Ondrej Filip <feela@network.cz> + * + * This software may be freely distributed and used according to the terms + * of the GNU Lesser General Public License. + */ + +/*** + * Introduction + * ------------ + * + * Binary heap is a simple data structure, which for example supports efficient insertions, deletions + * and access to the minimal inserted item. We define several macros for such operations. + * Note that because of simplicity of heaps, we have decided to define direct macros instead + * of a <<generic:,macro generator>> as for several other data structures in the Libucw. + * + * A heap is represented by a number of elements and by an array of values. Beware that we + * index this array from one, not from zero as do the standard C arrays. + * + * Most macros use these parameters: + * + * - @num - a variable (signed or unsigned integer) with the number of elements + * - @heap - a C array of type @type; the heap is stored in `heap[1] .. heap[num]`; `heap[0]` is unused + * + * A valid heap must follow these rules: + * + * - `num >= 0` + * - `heap[i] >= heap[i / 2]` for each `i` in `[2, num]` + * + * The first element `heap[1]` is always lower or equal to all other elements. + ***/ + +#include <string.h> +#include <stdlib.h> +#include "contrib/ucw/heap.h" + +static inline void heap_swap(heap_val_t **e1, heap_val_t **e2) +{ + if (e1 == e2) return; /* Stack tmp should be faster than tmpelem. */ + heap_val_t *tmp = *e1; /* Even faster than 2-XOR nowadays. */ + *e1 = *e2; + *e2 = tmp; + int pos = (*e1)->pos; + (*e1)->pos = (*e2)->pos; + (*e2)->pos = pos; +} + +int heap_init(struct heap *h, int (*cmp)(void *, void *), int init_size) +{ + int isize = init_size ? init_size : INITIAL_HEAP_SIZE; + + h->num = 0; + h->max_size = isize; + h->cmp = cmp; + h->data = malloc((isize + 1) * sizeof(heap_val_t*)); /* Temp element unused. */ + + return h->data ? 1 : 0; +} + +void heap_deinit(struct heap *h) +{ + free(h->data); + memset(h, 0, sizeof(*h)); +} + +static inline void _heap_bubble_down(struct heap *h, int e) +{ + int e1; + for (;;) + { + e1 = 2*e; + if(e1 > h->num) break; + if((h->cmp(*HELEMENT(h, e),*HELEMENT(h,e1)) < 0) && (e1 == h->num || (h->cmp(*HELEMENT(h, e),*HELEMENT(h,e1+1)) < 0))) break; + if((e1 != h->num) && (h->cmp(*HELEMENT(h, e1+1), *HELEMENT(h,e1)) < 0)) e1++; + heap_swap(HELEMENT(h,e),HELEMENT(h,e1)); + e = e1; + } +} + +static inline void _heap_bubble_up(struct heap *h, int e) +{ + int e1; + while (e > 1) + { + e1 = e/2; + if(h->cmp(*HELEMENT(h, e1),*HELEMENT(h,e)) < 0) break; + heap_swap(HELEMENT(h,e),HELEMENT(h,e1)); + e = e1; + } + +} + +static void heap_increase(struct heap *h, int pos, heap_val_t *e) +{ + *HELEMENT(h, pos) = e; + e->pos = pos; + _heap_bubble_down(h, pos); +} + +static void heap_decrease(struct heap *h, int pos, heap_val_t *e) +{ + *HELEMENT(h, pos) = e; + e->pos = pos; + _heap_bubble_up(h, pos); +} + +void heap_replace(struct heap *h, int pos, heap_val_t *e) +{ + if (h->cmp(*HELEMENT(h, pos),e) < 0) { + heap_increase(h, pos, e); + } else { + heap_decrease(h, pos, e); + } +} + +void heap_delmin(struct heap *h) +{ + if(h->num == 0) return; + if(h->num > 1) + { + heap_swap(HHEAD(h),HELEMENT(h,h->num)); + } + (*HELEMENT(h, h->num))->pos = 0; + --h->num; + _heap_bubble_down(h, 1); +} + +int heap_insert(struct heap *h, heap_val_t *e) +{ + if(h->num == h->max_size) + { + h->max_size = h->max_size * HEAP_INCREASE_STEP; + h->data = realloc(h->data, (h->max_size + 1) * sizeof(heap_val_t*)); + if (!h->data) { + return 0; + } + } + + h->num++; + *HELEMENT(h,h->num) = e; + e->pos = h->num; + _heap_bubble_up(h,h->num); + return 1; +} + +int heap_find(struct heap *h, heap_val_t *elm) +{ + return ((struct heap_val *) elm)->pos; +} + +void heap_delete(struct heap *h, int e) +{ + heap_swap(HELEMENT(h, e), HELEMENT(h, h->num)); + (*HELEMENT(h, h->num))->pos = 0; + h->num--; + if(h->cmp(*HELEMENT(h, e), *HELEMENT(h, h->num + 1)) < 0) _heap_bubble_up(h, e); + else _heap_bubble_down(h, e); + + if ((h->num > INITIAL_HEAP_SIZE) && (h->num < h->max_size / HEAP_DECREASE_THRESHOLD)) + { + h->max_size = h->max_size / HEAP_INCREASE_STEP; + h->data = realloc(h->data, (h->max_size + 1) * sizeof(heap_val_t*)); + } +} diff --git a/src/contrib/ucw/heap.h b/src/contrib/ucw/heap.h new file mode 100644 index 0000000..7419b34 --- /dev/null +++ b/src/contrib/ucw/heap.h @@ -0,0 +1,46 @@ +/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +struct heap_val { + int pos; +}; + +typedef struct heap_val heap_val_t; + +struct heap { + int num; /* Number of elements */ + int max_size; /* Size of allocated memory */ + int (*cmp)(void *, void *); + heap_val_t **data; +}; /* Array follows */ + +#define INITIAL_HEAP_SIZE 512 /* initial heap size */ +#define HEAP_INCREASE_STEP 2 /* multiplier for each inflation, keep conservative */ +#define HEAP_DECREASE_THRESHOLD 2 /* threshold for deflation, keep conservative */ +#define HELEMENT(h,num) ((h)->data + (num)) +#define HHEAD(h) HELEMENT((h), 1) +#define EMPTY_HEAP(h) ((h)->num == 0) /* h->num == 0 */ + +int heap_init(struct heap *, int (*cmp)(void *, void *), int); +void heap_deinit(struct heap *); + +void heap_delmin(struct heap *); +int heap_insert(struct heap *, heap_val_t *); +int heap_find(struct heap *, heap_val_t *); +void heap_delete(struct heap *, int); +void heap_replace(struct heap *h, int pos, heap_val_t *); diff --git a/src/contrib/ucw/lists.c b/src/contrib/ucw/lists.c new file mode 100644 index 0000000..8a9fa96 --- /dev/null +++ b/src/contrib/ucw/lists.c @@ -0,0 +1,235 @@ +/* + * BIRD Library -- Linked Lists + * + * (c) 1998 Martin Mares <mj@ucw.cz> + * (c) 2015, 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +/** + * DOC: Linked lists + * + * The BIRD library provides a set of functions for operating on linked + * lists. The lists are internally represented as standard doubly linked + * lists with synthetic head and tail which makes all the basic operations + * run in constant time and contain no extra end-of-list checks. Each list + * is described by a &list structure, nodes can have any format as long + * as they start with a &node structure. If you want your nodes to belong + * to multiple lists at once, you can embed multiple &node structures in them + * and use the SKIP_BACK() macro to calculate a pointer to the start of the + * structure from a &node pointer, but beware of obscurity. + * + * There also exist safe linked lists (&slist, &snode and all functions + * being prefixed with |s_|) which support asynchronous walking very + * similar to that used in the &fib structure. + */ + +#include <stdlib.h> +#include <string.h> +#include "contrib/ucw/lists.h" +#include "contrib/mempattern.h" + +/** + * add_tail - append a node to a list + * \p l: linked list + * \p n: list node + * + * add_tail() takes a node \p n and appends it at the end of the list \p l. + */ +void +add_tail(list_t *l, node_t *n) +{ + node_t *z = l->tail; + + n->next = (node_t *) &l->null; + n->prev = z; + z->next = n; + l->tail = n; +} + +/** + * add_head - prepend a node to a list + * \p l: linked list + * \p n: list node + * + * add_head() takes a node \p n and prepends it at the start of the list \p l. + */ +void +add_head(list_t *l, node_t *n) +{ + node_t *z = l->head; + + n->next = z; + n->prev = (node_t *) &l->head; + z->prev = n; + l->head = n; +} + +/** + * insert_node - insert a node to a list + * \p n: a new list node + * \p after: a node of a list + * + * Inserts a node \p n to a linked list after an already inserted + * node \p after. + */ +void +insert_node(node_t *n, node_t *after) +{ + node_t *z = after->next; + + n->next = z; + n->prev = after; + after->next = n; + z->prev = n; +} + +/** + * rem_node - remove a node from a list + * \p n: node to be removed + * + * Removes a node \p n from the list it's linked in. + */ +void +rem_node(node_t *n) +{ + node_t *z = n->prev; + node_t *x = n->next; + + z->next = x; + x->prev = z; + n->prev = 0; + n->next = 0; +} + +/** + * init_list - create an empty list + * \p l: list + * + * init_list() takes a &list structure and initializes its + * fields, so that it represents an empty list. + */ +void +init_list(list_t *l) +{ + l->head = (node_t *) &l->null; + l->null = NULL; + l->tail = (node_t *) &l->head; +} + +/** + * add_tail_list - concatenate two lists + * \p to: destination list + * \p l: source list + * + * This function appends all elements of the list \p l to + * the list \p to in constant time. + */ +void +add_tail_list(list_t *to, list_t *l) +{ + node_t *p = to->tail; + node_t *q = l->head; + + p->next = q; + q->prev = p; + q = l->tail; + q->next = (node_t *) &to->null; + to->tail = q; +} + +/** + * list_dup - duplicate list + * \p to: destination list + * \p l: source list + * + * This function duplicates all elements of the list \p l to + * the list \p to in linear time. + * + * This function only works with a homogenous item size. + */ +void list_dup(list_t *dst, list_t *src, size_t itemsz) +{ + node_t *n = 0; + WALK_LIST(n, *src) { + node_t *i = malloc(itemsz); + memcpy(i, n, itemsz); + add_tail(dst, i); + } +} + +/** + * list_size - gets number of nodes + * \p l: list + * + * This function counts nodes in list \p l and returns this number. + */ +size_t list_size(const list_t *l) +{ + size_t count = 0; + + node_t *n = 0; + WALK_LIST(n, *l) { + count++; + } + + return count; +} + +/** + * ptrlist_add - add pointer to pointer list + * \p to: destination list + * \p val: added pointer + * \p mm: memory context + */ +ptrnode_t *ptrlist_add(list_t *to, void *val, knot_mm_t *mm) +{ + ptrnode_t *node = mm_alloc(mm , sizeof(ptrnode_t)); + if (node == NULL) { + return NULL; + } else { + node->d = val; + } + add_tail(to, &node->n); + return node; +} + +/** + * ptrlist_free - free all nodes in pointer list + * \p list: list nodes + * \p mm: memory context + */ +void ptrlist_free(list_t *list, knot_mm_t *mm) +{ + node_t *n = NULL, *nxt = NULL; + WALK_LIST_DELSAFE(n, nxt, *list) { + mm_free(mm, n); + } + init_list(list); +} + +/** + * ptrlist_rem - remove pointer node + * \p val: pointer to remove + * \p mm: memory context + */ +void ptrlist_rem(ptrnode_t *node, knot_mm_t *mm) +{ + rem_node(&node->n); + mm_free(mm, node); +} + +/** + * ptrlist_deep_free - free all nodes incl referenced data + * \p list: list nodes + * \p mm: memory context + */ +void ptrlist_deep_free(list_t *l, knot_mm_t *mm) +{ + ptrnode_t *n; + WALK_LIST(n, *l) { + mm_free(mm, n->d); + } + ptrlist_free(l, mm); +} diff --git a/src/contrib/ucw/lists.h b/src/contrib/ucw/lists.h new file mode 100644 index 0000000..922e152 --- /dev/null +++ b/src/contrib/ucw/lists.h @@ -0,0 +1,84 @@ +/* + * BIRD Library -- Linked Lists + * + * (c) 1998 Martin Mares <mj@ucw.cz> + * (c) 2015, 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#pragma once + +/* + * I admit the list structure is very tricky and also somewhat awkward, + * but it's both efficient and easy to manipulate once one understands the + * basic trick: The list head always contains two synthetic nodes which are + * always present in the list: the head and the tail. But as the `next' + * entry of the tail and the `prev' entry of the head are both NULL, the + * nodes can overlap each other: + * + * head head_node.next + * null head_node.prev tail_node.next + * tail tail_node.prev + */ + +#include <string.h> +#include "libknot/mm_ctx.h" + +typedef struct node { + struct node *next, *prev; +} node_t; + +typedef struct list { /* In fact two overlayed nodes */ + struct node *head, *null, *tail; +} list_t; + +#define NODE (node_t *) +#define HEAD(list) ((void *)((list).head)) +#define TAIL(list) ((void *)((list).tail)) +#define WALK_LIST(n,list) for(n=HEAD(list);(NODE (n))->next; \ + n=(void *)((NODE (n))->next)) +#define WALK_LIST_DELSAFE(n,nxt,list) \ + for(n=HEAD(list); (nxt=(void *)((NODE (n))->next)); n=(void *) nxt) +/* WALK_LIST_FIRST supposes that called code removes each processed node */ +#define WALK_LIST_FIRST(n,list) \ + while(n=HEAD(list), (NODE (n))->next) +#define WALK_LIST_BACKWARDS(n,list) for(n=TAIL(list);(NODE (n))->prev; \ + n=(void *)((NODE (n))->prev)) +#define WALK_LIST_BACKWARDS_DELSAFE(n,prv,list) \ + for(n=TAIL(list); prv=(void *)((NODE (n))->prev); n=(void *) prv) + +#define EMPTY_LIST(list) (!(list).head->next) + +/*! \brief Free every node in the list. */ +#define WALK_LIST_FREE(list) \ + do { \ + node_t *n=0,*nxt=0; \ + WALK_LIST_DELSAFE(n,nxt,list) { \ + free(n); \ + } \ + init_list(&list); \ + } while(0) + +void add_tail(list_t *, node_t *); +void add_head(list_t *, node_t *); +void rem_node(node_t *); +void add_tail_list(list_t *, list_t *); +void init_list(list_t *); +void insert_node(node_t *, node_t *); +void list_dup(list_t *dst, list_t *src, size_t itemsz); +size_t list_size(const list_t *); + +/*! + * \brief Generic pointer list implementation. + */ +typedef struct ptrnode { + node_t n; + void *d; +} ptrnode_t; + +ptrnode_t *ptrlist_add(list_t *, void *, knot_mm_t *); +void ptrlist_free(list_t *, knot_mm_t *); +void ptrlist_rem(ptrnode_t *node, knot_mm_t *mm); +void ptrlist_deep_free(list_t *, knot_mm_t *); + diff --git a/src/contrib/ucw/mempool.c b/src/contrib/ucw/mempool.c new file mode 100644 index 0000000..bc41345 --- /dev/null +++ b/src/contrib/ucw/mempool.c @@ -0,0 +1,322 @@ +/* + * UCW Library -- Memory Pools (One-Time Allocation) + * + * (c) 1997--2001 Martin Mares <mj@ucw.cz> + * (c) 2007 Pavel Charvat <pchar@ucw.cz> + * (c) 2015, 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + * + * This software may be freely distributed and used according to the terms + * of the GNU Lesser General Public License. + */ + +#undef LOCAL_DEBUG + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <assert.h> +#include "contrib/asan.h" +#include "contrib/macros.h" +#include "contrib/ucw/mempool.h" + +/** \todo This shouldn't be precalculated, but computed on load. */ +#define CPU_PAGE_SIZE 4096 + +/** Align an integer \p s to the nearest higher multiple of \p a (which should be a power of two) **/ +#define ALIGN_TO(s, a) (((s)+a-1)&~(a-1)) +#define MP_CHUNK_TAIL ALIGN_TO(sizeof(struct mempool_chunk), CPU_STRUCT_ALIGN) +#define MP_SIZE_MAX (~0U - MP_CHUNK_TAIL - CPU_PAGE_SIZE) +#define DBG(s, ...) + +/** \note Imported MMAP backend from bigalloc.c */ +#define CONFIG_UCW_POOL_IS_MMAP +#ifdef CONFIG_UCW_POOL_IS_MMAP +#include <sys/mman.h> +static void * +page_alloc(uint64_t len) +{ + if (!len) { + return NULL; + } + if (len > SIZE_MAX) { + return NULL; + } + assert(!(len & (CPU_PAGE_SIZE-1))); + uint8_t *p = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); + if (p == (uint8_t*) MAP_FAILED) { + return NULL; + } + return p; +} + +static void +page_free(void *start, uint64_t len) +{ + assert(!(len & (CPU_PAGE_SIZE-1))); + assert(!((uintptr_t) start & (CPU_PAGE_SIZE-1))); + munmap(start, len); +} +#endif + +struct mempool_chunk { + struct mempool_chunk *next; + unsigned size; +}; + +static unsigned +mp_align_size(unsigned size) +{ +#ifdef CONFIG_UCW_POOL_IS_MMAP + return ALIGN_TO(size + MP_CHUNK_TAIL, CPU_PAGE_SIZE) - MP_CHUNK_TAIL; +#else + return ALIGN_TO(size, CPU_STRUCT_ALIGN); +#endif +} + +void +mp_init(struct mempool *pool, unsigned chunk_size) +{ + chunk_size = mp_align_size(MAX(sizeof(struct mempool), chunk_size)); + *pool = (struct mempool) { + .chunk_size = chunk_size, + .threshold = chunk_size >> 1, + .last_big = &pool->last_big + }; +} + +static void * +mp_new_big_chunk(unsigned size) +{ + uint8_t *data = malloc(size + MP_CHUNK_TAIL); + if (!data) { + return NULL; + } + ASAN_POISON_MEMORY_REGION(data, size); + struct mempool_chunk *chunk = (struct mempool_chunk *)(data + size); + chunk->size = size; + return chunk; +} + +static void +mp_free_big_chunk(struct mempool_chunk *chunk) +{ + void *ptr = (uint8_t *)chunk - chunk->size; + ASAN_UNPOISON_MEMORY_REGION(ptr, chunk->size); + free(ptr); +} + +static void * +mp_new_chunk(unsigned size) +{ +#ifdef CONFIG_UCW_POOL_IS_MMAP + uint8_t *data = page_alloc(size + MP_CHUNK_TAIL); + if (!data) { + return NULL; + } + ASAN_POISON_MEMORY_REGION(data, size); + struct mempool_chunk *chunk = (struct mempool_chunk *)(data + size); + chunk->size = size; + return chunk; +#else + return mp_new_big_chunk(size); +#endif +} + +static void +mp_free_chunk(struct mempool_chunk *chunk) +{ +#ifdef CONFIG_UCW_POOL_IS_MMAP + uint8_t *data = (uint8_t *)chunk - chunk->size; + ASAN_UNPOISON_MEMORY_REGION(data, chunk->size); + page_free(data, chunk->size + MP_CHUNK_TAIL); +#else + mp_free_big_chunk(chunk); +#endif +} + +struct mempool * +mp_new(unsigned chunk_size) +{ + chunk_size = mp_align_size(MAX(sizeof(struct mempool), chunk_size)); + struct mempool_chunk *chunk = mp_new_chunk(chunk_size); + struct mempool *pool = (void *)chunk - chunk_size; + ASAN_UNPOISON_MEMORY_REGION(pool, sizeof(*pool)); + DBG("Creating mempool %p with %u bytes long chunks", pool, chunk_size); + chunk->next = NULL; + ASAN_POISON_MEMORY_REGION(chunk, sizeof(struct mempool_chunk)); + *pool = (struct mempool) { + .state = { .free = { chunk_size - sizeof(*pool) }, .last = { chunk } }, + .chunk_size = chunk_size, + .threshold = chunk_size >> 1, + .last_big = &pool->last_big + }; + return pool; +} + +static void +mp_free_chain(struct mempool_chunk *chunk) +{ + while (chunk) { + ASAN_UNPOISON_MEMORY_REGION(chunk, sizeof(struct mempool_chunk)); + struct mempool_chunk *next = chunk->next; + mp_free_chunk(chunk); + chunk = next; + } +} + +static void +mp_free_big_chain(struct mempool_chunk *chunk) +{ + while (chunk) { + ASAN_UNPOISON_MEMORY_REGION(chunk, sizeof(struct mempool_chunk)); + struct mempool_chunk *next = chunk->next; + mp_free_big_chunk(chunk); + chunk = next; + } +} + +void +mp_delete(struct mempool *pool) +{ + if (pool == NULL) { + return; + } + DBG("Deleting mempool %p", pool); + mp_free_big_chain(pool->state.last[1]); + mp_free_chain(pool->unused); + mp_free_chain(pool->state.last[0]); // can contain the mempool structure +} + +void +mp_flush(struct mempool *pool) +{ + mp_free_big_chain(pool->state.last[1]); + struct mempool_chunk *chunk = pool->state.last[0], *next; + while (chunk) { + ASAN_UNPOISON_MEMORY_REGION(chunk, sizeof(struct mempool_chunk)); + if ((uint8_t *)chunk - chunk->size == (uint8_t *)pool) { + break; + } + next = chunk->next; + chunk->next = pool->unused; + ASAN_POISON_MEMORY_REGION(chunk, sizeof(struct mempool_chunk)); + pool->unused = chunk; + chunk = next; + } + pool->state.last[0] = chunk; + if (chunk) { + pool->state.free[0] = chunk->size - sizeof(*pool); + ASAN_POISON_MEMORY_REGION(chunk, sizeof(struct mempool_chunk)); + } else { + pool->state.free[0] = 0; + } + pool->state.last[1] = NULL; + pool->state.free[1] = 0; + pool->last_big = &pool->last_big; +} + +static void +mp_stats_chain(struct mempool_chunk *chunk, struct mempool_stats *stats, unsigned idx) +{ + struct mempool_chunk *next; + while (chunk) { + ASAN_UNPOISON_MEMORY_REGION(chunk, sizeof(struct mempool_chunk)); + stats->chain_size[idx] += chunk->size + sizeof(*chunk); + stats->chain_count[idx]++; + next = chunk->next; + ASAN_POISON_MEMORY_REGION(chunk, sizeof(struct mempool_chunk)); + chunk = next; + } + stats->total_size += stats->chain_size[idx]; +} + +void +mp_stats(struct mempool *pool, struct mempool_stats *stats) +{ + bzero(stats, sizeof(*stats)); + mp_stats_chain(pool->state.last[0], stats, 0); + mp_stats_chain(pool->state.last[1], stats, 1); + mp_stats_chain(pool->unused, stats, 2); +} + +uint64_t +mp_total_size(struct mempool *pool) +{ + struct mempool_stats stats; + mp_stats(pool, &stats); + return stats.total_size; +} + +static void * +mp_alloc_internal(struct mempool *pool, unsigned size) +{ + struct mempool_chunk *chunk; + if (size <= pool->threshold) { + pool->idx = 0; + if (pool->unused) { + chunk = pool->unused; + ASAN_UNPOISON_MEMORY_REGION(chunk, sizeof(struct mempool_chunk)); + pool->unused = chunk->next; + } else { + chunk = mp_new_chunk(pool->chunk_size); + } + chunk->next = pool->state.last[0]; + ASAN_POISON_MEMORY_REGION(chunk, sizeof(struct mempool_chunk)); + pool->state.last[0] = chunk; + pool->state.free[0] = pool->chunk_size - size; + return (uint8_t *)chunk - pool->chunk_size; + } else if (size <= MP_SIZE_MAX) { + pool->idx = 1; + unsigned aligned = ALIGN_TO(size, CPU_STRUCT_ALIGN); + chunk = mp_new_big_chunk(aligned); + if (!chunk) { + return NULL; + } + chunk->next = pool->state.last[1]; + ASAN_POISON_MEMORY_REGION(chunk, sizeof(struct mempool_chunk)); + pool->state.last[1] = chunk; + pool->state.free[1] = aligned - size; + return pool->last_big = (uint8_t *)chunk - aligned; + } else { + fprintf(stderr, "Cannot allocate %u bytes from a mempool", size); + assert(0); + return NULL; + } +} + +void * +mp_alloc(struct mempool *pool, unsigned size) +{ + unsigned avail = pool->state.free[0] & ~(CPU_STRUCT_ALIGN - 1); + void *ptr = NULL; + if (size <= avail) { + pool->state.free[0] = avail - size; + ptr = (uint8_t*)pool->state.last[0] - avail; + } else { + ptr = mp_alloc_internal(pool, size); + } + ASAN_UNPOISON_MEMORY_REGION(ptr, size); + return ptr; +} + +void * +mp_alloc_noalign(struct mempool *pool, unsigned size) +{ + void *ptr = NULL; + if (size <= pool->state.free[0]) { + ptr = (uint8_t*)pool->state.last[0] - pool->state.free[0]; + pool->state.free[0] -= size; + } else { + ptr = mp_alloc_internal(pool, size); + } + ASAN_UNPOISON_MEMORY_REGION(ptr, size); + return ptr; +} + +void * +mp_alloc_zero(struct mempool *pool, unsigned size) +{ + void *ptr = mp_alloc(pool, size); + bzero(ptr, size); + return ptr; +} diff --git a/src/contrib/ucw/mempool.h b/src/contrib/ucw/mempool.h new file mode 100644 index 0000000..c5a4fa8 --- /dev/null +++ b/src/contrib/ucw/mempool.h @@ -0,0 +1,124 @@ +/* + * UCW Library -- Memory Pools + * + * (c) 1997--2005 Martin Mares <mj@ucw.cz> + * (c) 2007 Pavel Charvat <pchar@ucw.cz> + * (c) 2015, 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + * + * This software may be freely distributed and used according to the terms + * of the GNU Lesser General Public License. + */ + +#pragma once + +#include <string.h> +#include <stdint.h> + +#define CPU_STRUCT_ALIGN (sizeof(void*)) + +/*** + * [[defs]] + * Definitions + * ----------- + ***/ + +/** + * Memory pool state (see mp_push(), ...). + * You should use this one as an opaque handle only, the insides are internal. + **/ +struct mempool_state { + unsigned free[2]; + void *last[2]; +}; + +/** + * Memory pool. + * You should use this one as an opaque handle only, the insides are internal. + **/ +struct mempool { + struct mempool_state state; + void *unused, *last_big; + unsigned chunk_size, threshold, idx; +}; + +struct mempool_stats { /** Mempool statistics. See mp_stats(). **/ + uint64_t total_size; /** Real allocated size in bytes. */ + unsigned chain_count[3]; /** Number of allocated chunks in small/big/unused chains. */ + unsigned chain_size[3]; /** Size of allocated chunks in small/big/unused chains. */ +}; + +/*** + * [[basic]] + * Basic manipulation + * ------------------ + ***/ + +/** + * Initialize a given mempool structure. + * \p chunk_size must be in the interval `[1, UINT_MAX / 2]`. + * It will allocate memory by this large chunks and take + * memory to satisfy requests from them. + * + * Memory pools can be treated as <<trans:respools,resources>>, see <<trans:res_mempool()>>. + **/ +void mp_init(struct mempool *pool, unsigned chunk_size); + +/** + * Allocate and initialize a new memory pool. + * See \ref mp_init() for \p chunk_size limitations. + * + * The new mempool structure is allocated on the new mempool. + * + * Memory pools can be treated as <<trans:respools,resources>>, see <<trans:res_mempool()>>. + **/ +struct mempool *mp_new(unsigned chunk_size); + +/** + * Cleanup mempool initialized by mp_init or mp_new. + * Frees all the memory allocated by this mempool and, + * if created by \ref mp_new(), the \p pool itself. + **/ +void mp_delete(struct mempool *pool); + +/** + * Frees all data on a memory pool, but leaves it working. + * It can keep some of the chunks allocated to serve + * further allocation requests. Leaves the \p pool alive, + * even if it was created with \ref mp_new(). + **/ +void mp_flush(struct mempool *pool); + +/** + * Compute some statistics for debug purposes. + * See the definition of the <<struct_mempool_stats,mempool_stats structure>>. + **/ +void mp_stats(struct mempool *pool, struct mempool_stats *stats); +uint64_t mp_total_size(struct mempool *pool); /** How many bytes were allocated by the pool. **/ + +/*** + * [[alloc]] + * Allocation routines + * ------------------- + ***/ + +/** + * The function allocates new \p size bytes on a given memory pool. + * If the \p size is zero, the resulting pointer is undefined, + * but it may be safely reallocated or used as the parameter + * to other functions below. + * + * The resulting pointer is always aligned to a multiple of + * `CPU_STRUCT_ALIGN` bytes and this condition remains true also + * after future reallocations. + **/ +void *mp_alloc(struct mempool *pool, unsigned size); + +/** + * The same as \ref mp_alloc(), but the result may be unaligned. + **/ +void *mp_alloc_noalign(struct mempool *pool, unsigned size); + +/** + * The same as \ref mp_alloc(), but fills the newly allocated memory with zeroes. + **/ +void *mp_alloc_zero(struct mempool *pool, unsigned size); diff --git a/src/contrib/wire_ctx.h b/src/contrib/wire_ctx.h new file mode 100644 index 0000000..25ff143 --- /dev/null +++ b/src/contrib/wire_ctx.h @@ -0,0 +1,361 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <assert.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> +#include <string.h> +#include <sys/types.h> + +#include "libknot/endian.h" +#include "libknot/errcode.h" + +/*! + * \brief Struct to keep the wire context. + */ +typedef struct wire_ctx { + size_t size; + uint8_t *wire; + uint8_t *position; + int error; + bool readonly; +} wire_ctx_t; + +/*! + * \brief Initialize wire context. + */ +static inline wire_ctx_t wire_ctx_init(uint8_t *data, size_t size) +{ + assert(data); + + wire_ctx_t result = { + .size = size, + .wire = data, + .position = data, + .error = KNOT_EOK, + .readonly = false + }; + + return result; +} + +/*! + * \brief Initialize read only wire context. + * + * \note No write is performed, and error is set to KNOT_EACCES. + * + */ +static inline wire_ctx_t wire_ctx_init_const(const uint8_t *data, size_t size) +{ + assert(data); + + wire_ctx_t result = wire_ctx_init((uint8_t *)data, size); + result.readonly = true; + + return result; +} + +/*! + * \brief Gets actual position. + * + * \return position from the begin. + */ +static inline size_t wire_ctx_offset(wire_ctx_t *ctx) +{ + assert(ctx); + + return ctx->position - ctx->wire; +} + +/*! + * \brief Set position offset from the begin. + * + * \param offset Wire offset (starts from 0). + * + * \note Noop if previous error. + */ +static inline void wire_ctx_set_offset(wire_ctx_t *ctx, size_t offset) +{ + assert(ctx); + + if (ctx->error != KNOT_EOK) { + return; + } + + if (offset > ctx->size) { + ctx->error = KNOT_ERANGE; + return; + } + + ctx->position = ctx->wire + offset; +} + +/*! + * \brief Gets available bytes. + * + * \return Number of bytes to end. + */ +static inline size_t wire_ctx_available(wire_ctx_t *ctx) +{ + assert(ctx); + + return ctx->size - wire_ctx_offset(ctx); +} + +/*! + * \brief Add offset to the current position. + * + * \note Noop if previous error. + */ +static inline void wire_ctx_skip(wire_ctx_t *ctx, ssize_t offset) +{ + assert(ctx); + + if (ctx->error != KNOT_EOK) { + return; + } + + // Check for out of scope skip. + if (offset >= 0) { + if (offset > wire_ctx_available(ctx)) { + ctx->error = KNOT_ERANGE; + return; + } + } else { + if (-offset > wire_ctx_offset(ctx)) { + ctx->error = KNOT_ERANGE; + return; + } + } + + ctx->position += offset; +} + +/*! + * \brief Check the context if reading is possible. + */ +static inline int wire_ctx_can_read(wire_ctx_t *ctx, size_t size) +{ + assert(ctx); + + if (ctx->error != KNOT_EOK) { + return ctx->error; + } + + if (wire_ctx_available(ctx) < size) { + return KNOT_EFEWDATA; + } + + return KNOT_EOK; +} + +/*! + * \brief Check the context if writing is possible. + */ +static inline int wire_ctx_can_write(wire_ctx_t *ctx, size_t size) +{ + assert(ctx); + + if (ctx->error != KNOT_EOK) { + return ctx->error; + } + + if (ctx->readonly) { + return KNOT_EACCES; + } + + if (wire_ctx_available(ctx) < size) { + return KNOT_ESPACE; + } + + return KNOT_EOK; +} + + +static inline void wire_ctx_read(wire_ctx_t *ctx, void *data, size_t size) +{ + assert(ctx); + assert(data); + + if (ctx->error != KNOT_EOK) { + /* Avoid leaving data uninitialized. */ + memset(data, 0, size); + return; + } + + int ret = wire_ctx_can_read(ctx, size); + if (ret != KNOT_EOK) { + ctx->error = ret; + memset(data, 0, size); + return; + } + + memcpy(data, ctx->position, size); + ctx->position += size; +} + +static inline uint8_t wire_ctx_read_u8(wire_ctx_t *ctx) +{ + uint8_t result; + wire_ctx_read(ctx, &result, sizeof(result)); + + return result; +} + +static inline uint16_t wire_ctx_read_u16(wire_ctx_t *ctx) +{ + uint16_t result; + wire_ctx_read(ctx, &result, sizeof(result)); + + return be16toh(result); +} + +static inline uint32_t wire_ctx_read_u32(wire_ctx_t *ctx) +{ + uint32_t result; + wire_ctx_read(ctx, &result, sizeof(result)); + + return be32toh(result); +} + +static inline uint64_t wire_ctx_read_u48(wire_ctx_t *ctx) +{ + /* This case is slightly tricky. */ + uint64_t result = 0; + wire_ctx_read(ctx, (uint8_t *)&result + 1, 6); + + return be64toh(result) >> 8; +} + +static inline uint64_t wire_ctx_read_u64(wire_ctx_t *ctx) +{ + uint64_t result; + wire_ctx_read(ctx, &result, sizeof(result)); + + return be64toh(result); +} + + +static inline void wire_ctx_write(wire_ctx_t *ctx, const void *data, size_t size) +{ + assert(ctx); + + if (ctx->error != KNOT_EOK) { + return; + } + + if (size == 0) { + return; + } + + assert(data); + + int ret = wire_ctx_can_write(ctx, size); + if (ret != KNOT_EOK) { + ctx->error = ret; + return; + } + + memcpy(ctx->position, data, size); + ctx->position += size; +} + +static inline void wire_ctx_write_u8(wire_ctx_t *ctx, uint8_t value) +{ + wire_ctx_write(ctx, &value, sizeof(value)); +} + +static inline void wire_ctx_write_u16(wire_ctx_t *ctx, uint16_t value) +{ + uint16_t beval = htobe16(value); + wire_ctx_write(ctx, &beval, sizeof(beval)); +} + +static inline void wire_ctx_write_u32(wire_ctx_t *ctx, uint32_t value) +{ + uint32_t beval = htobe32(value); + wire_ctx_write(ctx, &beval, sizeof(beval)); +} + +static inline void wire_ctx_write_u48(wire_ctx_t *ctx, uint64_t value) +{ + /* This case is slightly tricky. */ + uint64_t swapped = htobe64(value << 8); + wire_ctx_write(ctx, (uint8_t *)&swapped + 1, 6); +} + +static inline void wire_ctx_write_u64(wire_ctx_t *ctx, uint64_t value) +{ + uint64_t beval = htobe64(value); + wire_ctx_write(ctx, &beval, sizeof(beval)); +} + + +static inline void wire_ctx_memset(wire_ctx_t *dst, int value, size_t size) +{ + assert(dst); + + if (dst->error != KNOT_EOK) { + return; + } + + if (size == 0) { + return; + } + + int ret = wire_ctx_can_write(dst, size); + if (ret != KNOT_EOK) { + dst->error = ret; + return; + } + + memset(dst->position, value, size); + dst->position += size; +} + +static inline void wire_ctx_clear(wire_ctx_t *ctx, size_t size) +{ + wire_ctx_memset(ctx, 0, size); +} + +static inline void wire_ctx_copy(wire_ctx_t *dst, wire_ctx_t *src, size_t size) +{ + assert(dst); + assert(src); + + if (size == 0 || dst->error != KNOT_EOK) { + return; + } + + if (wire_ctx_can_read(src, size) != KNOT_EOK) { + dst->error = KNOT_EFEWDATA; + return; + } + + int ret = wire_ctx_can_write(dst, size); + if (ret != KNOT_EOK) { + dst->error = ret; + return; + } + + memcpy(dst->position, src->position, size); + dst->position += size; + src->position += size; +} + diff --git a/src/knot/Makefile.inc b/src/knot/Makefile.inc new file mode 100644 index 0000000..fc3700c --- /dev/null +++ b/src/knot/Makefile.inc @@ -0,0 +1,196 @@ +libknotd_la_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAG_VISIBILITY) $(systemd_CFLAGS) \ + $(liburcu_CFLAGS) -DKNOTD_MOD_STATIC +libknotd_la_LDFLAGS = $(AM_LDFLAGS) -export-symbols-regex '^knotd_' +libknotd_la_LIBADD = libcontrib.la libknot.la libzscanner.la $(systemd_LIBS) \ + $(liburcu_LIBS) $(pthread_LIBS) $(dlopen_LIBS) + +include_libknotddir = $(includedir)/knot +include_libknotd_HEADERS = \ + knot/include/module.h + +libknotd_la_SOURCES = \ + knot/conf/base.c \ + knot/conf/base.h \ + knot/conf/conf.c \ + knot/conf/conf.h \ + knot/conf/confdb.c \ + knot/conf/confdb.h \ + knot/conf/confio.c \ + knot/conf/confio.h \ + knot/conf/migration.c \ + knot/conf/migration.h \ + knot/conf/module.h \ + knot/conf/module.c \ + knot/conf/schema.c \ + knot/conf/schema.h \ + knot/conf/tools.c \ + knot/conf/tools.h \ + knot/ctl/commands.c \ + knot/ctl/commands.h \ + knot/ctl/process.c \ + knot/ctl/process.h \ + knot/dnssec/context.c \ + knot/dnssec/context.h \ + knot/dnssec/ds_query.c \ + knot/dnssec/ds_query.h \ + knot/dnssec/kasp/kasp_db.c \ + knot/dnssec/kasp/kasp_db.h \ + knot/dnssec/kasp/kasp_zone.c \ + knot/dnssec/kasp/kasp_zone.h \ + knot/dnssec/kasp/keystate.c \ + knot/dnssec/kasp/keystate.h \ + knot/dnssec/kasp/keystore.c \ + knot/dnssec/kasp/keystore.h \ + knot/dnssec/kasp/policy.h \ + knot/dnssec/key-events.c \ + knot/dnssec/key-events.h \ + knot/dnssec/nsec-chain.c \ + knot/dnssec/nsec-chain.h \ + knot/dnssec/nsec3-chain.c \ + knot/dnssec/nsec3-chain.h \ + knot/dnssec/policy.c \ + knot/dnssec/policy.h \ + knot/dnssec/rrset-sign.c \ + knot/dnssec/rrset-sign.h \ + knot/dnssec/zone-events.c \ + knot/dnssec/zone-events.h \ + knot/dnssec/zone-keys.c \ + knot/dnssec/zone-keys.h \ + knot/dnssec/zone-nsec.c \ + knot/dnssec/zone-nsec.h \ + knot/dnssec/zone-sign.c \ + knot/dnssec/zone-sign.h \ + knot/events/events.c \ + knot/events/events.h \ + knot/events/handlers.h \ + knot/events/handlers/dnssec.c \ + knot/events/handlers/expire.c \ + knot/events/handlers/flush.c \ + knot/events/handlers/freeze_thaw.c \ + knot/events/handlers/load.c \ + knot/events/handlers/notify.c \ + knot/events/handlers/nsec3resalt.c \ + knot/events/handlers/refresh.c \ + knot/events/handlers/update.c \ + knot/events/handlers/parent_ds_query.c \ + knot/events/replan.c \ + knot/events/replan.h \ + knot/nameserver/axfr.c \ + knot/nameserver/axfr.h \ + knot/nameserver/chaos.c \ + knot/nameserver/chaos.h \ + knot/nameserver/internet.c \ + knot/nameserver/internet.h \ + knot/nameserver/ixfr.c \ + knot/nameserver/ixfr.h \ + knot/nameserver/log.h \ + knot/nameserver/notify.c \ + knot/nameserver/notify.h \ + knot/nameserver/nsec_proofs.c \ + knot/nameserver/nsec_proofs.h \ + knot/nameserver/process_query.c \ + knot/nameserver/process_query.h \ + knot/nameserver/query_module.c \ + knot/nameserver/query_module.h \ + knot/nameserver/tsig_ctx.c \ + knot/nameserver/tsig_ctx.h \ + knot/nameserver/update.c \ + knot/nameserver/update.h \ + knot/nameserver/xfr.c \ + knot/nameserver/xfr.h \ + knot/query/capture.c \ + knot/query/capture.h \ + knot/query/layer.h \ + knot/query/query.c \ + knot/query/query.h \ + knot/query/requestor.c \ + knot/query/requestor.h \ + knot/common/evsched.c \ + knot/common/evsched.h \ + knot/common/fdset.c \ + knot/common/fdset.h \ + knot/common/log.c \ + knot/common/log.h \ + knot/common/process.c \ + knot/common/process.h \ + knot/common/ref.c \ + knot/common/ref.h \ + knot/common/stats.c \ + knot/common/stats.h \ + knot/server/dthreads.c \ + knot/server/dthreads.h \ + knot/journal/chgset_ctx.c \ + knot/journal/chgset_ctx.h \ + knot/journal/journal.c \ + knot/journal/journal.h \ + knot/journal/serialization.c \ + knot/journal/serialization.h \ + knot/server/server.c \ + knot/server/server.h \ + knot/server/tcp-handler.c \ + knot/server/tcp-handler.h \ + knot/server/udp-handler.c \ + knot/server/udp-handler.h \ + knot/updates/acl.c \ + knot/updates/acl.h \ + knot/updates/apply.c \ + knot/updates/apply.h \ + knot/updates/changesets.c \ + knot/updates/changesets.h \ + knot/updates/ddns.c \ + knot/updates/ddns.h \ + knot/updates/zone-update.c \ + knot/updates/zone-update.h \ + knot/worker/pool.c \ + knot/worker/pool.h \ + knot/worker/queue.c \ + knot/worker/queue.h \ + knot/zone/contents.c \ + knot/zone/contents.h \ + knot/zone/node.c \ + knot/zone/node.h \ + knot/zone/semantic-check.c \ + knot/zone/semantic-check.h \ + knot/zone/serial.c \ + knot/zone/serial.h \ + knot/zone/timers.c \ + knot/zone/timers.h \ + knot/zone/zone-diff.c \ + knot/zone/zone-diff.h \ + knot/zone/zone-dump.c \ + knot/zone/zone-dump.h \ + knot/zone/zone-load.c \ + knot/zone/zone-load.h \ + knot/zone/zone-tree.c \ + knot/zone/zone-tree.h \ + knot/zone/zone.c \ + knot/zone/zone.h \ + knot/zone/zonedb-load.c \ + knot/zone/zonedb-load.h \ + knot/zone/zonedb.c \ + knot/zone/zonedb.h \ + knot/zone/zonefile.c \ + knot/zone/zonefile.h + +if HAVE_DAEMON +noinst_LTLIBRARIES += libknotd.la +pkgconfig_DATA += knotd.pc +endif HAVE_DAEMON + +KNOTD_MOD_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAG_VISIBILITY) +KNOTD_MOD_LDFLAGS = $(AM_LDFLAGS) -module -shared -avoid-version + +pkglibdir = $(module_instdir) +pkglib_LTLIBRARIES = + +include $(srcdir)/knot/modules/cookies/Makefile.inc +include $(srcdir)/knot/modules/dnsproxy/Makefile.inc +include $(srcdir)/knot/modules/dnstap/Makefile.inc +include $(srcdir)/knot/modules/geoip/Makefile.inc +include $(srcdir)/knot/modules/noudp/Makefile.inc +include $(srcdir)/knot/modules/onlinesign/Makefile.inc +include $(srcdir)/knot/modules/queryacl/Makefile.inc +include $(srcdir)/knot/modules/rrl/Makefile.inc +include $(srcdir)/knot/modules/stats/Makefile.inc +include $(srcdir)/knot/modules/synthrecord/Makefile.inc +include $(srcdir)/knot/modules/whoami/Makefile.inc diff --git a/src/knot/common/evsched.c b/src/knot/common/evsched.c new file mode 100644 index 0000000..1c6f7b2 --- /dev/null +++ b/src/knot/common/evsched.c @@ -0,0 +1,267 @@ +/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <sys/time.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> + +#include "libknot/libknot.h" +#include "knot/server/dthreads.h" +#include "knot/common/evsched.h" + +/*! \brief Some implementations of timercmp >= are broken, this is for compat.*/ +static inline int timercmp_ge(struct timeval *a, struct timeval *b) { + return timercmp(a, b, >) || timercmp(a, b, ==); +} + +static int compare_event_heap_nodes(void *e1, void *e2) +{ + if (timercmp(&((event_t *)e1)->tv, &((event_t *)e2)->tv, <)) return -1; + if (timercmp(&((event_t *)e1)->tv, &((event_t *)e2)->tv, >)) return 1; + return 0; +} + +/*! + * \brief Get time T (now) + dt miliseconds. + */ +static struct timeval timeval_in(uint32_t dt) +{ + struct timeval tv = { 0 }; + gettimeofday(&tv, NULL); + + /* Add number of seconds. */ + tv.tv_sec += dt / 1000; + + /* Add the number of microseconds. */ + tv.tv_usec += (dt % 1000) * 1000; + + /* Check for overflow. */ + while (tv.tv_usec > 999999) { + tv.tv_sec += 1; + tv.tv_usec -= 1 * 1000 * 1000; + } + + return tv; +} + +/*! \brief Event scheduler loop. */ +static int evsched_run(dthread_t *thread) +{ + evsched_t *sched = (evsched_t*)thread->data; + if (sched == NULL) { + return KNOT_EINVAL; + } + + /* Run event loop. */ + pthread_mutex_lock(&sched->heap_lock); + while (!dt_is_cancelled(thread)) { + if (!!EMPTY_HEAP(&sched->heap) || sched->paused) { + pthread_cond_wait(&sched->notify, &sched->heap_lock); + continue; + } + + /* Get current time. */ + struct timeval dt; + gettimeofday(&dt, 0); + + /* Get next event. */ + event_t *ev = *((event_t**)HHEAD(&sched->heap)); + assert(ev != NULL); + + if (timercmp_ge(&dt, &ev->tv)) { + heap_delmin(&sched->heap); + ev->cb(ev); + } else { + /* Wait for next event or interrupt. Unlock calendar. */ + struct timespec ts; + ts.tv_sec = ev->tv.tv_sec; + ts.tv_nsec = ev->tv.tv_usec * 1000L; + pthread_cond_timedwait(&sched->notify, &sched->heap_lock, &ts); + } + } + pthread_mutex_unlock(&sched->heap_lock); + + return KNOT_EOK; +} + +int evsched_init(evsched_t *sched, void *ctx) +{ + memset(sched, 0, sizeof(evsched_t)); + sched->ctx = ctx; + + /* Initialize event calendar. */ + pthread_mutex_init(&sched->heap_lock, 0); + pthread_cond_init(&sched->notify, 0); + heap_init(&sched->heap, compare_event_heap_nodes, 0); + + sched->thread = dt_create(1, evsched_run, NULL, sched); + + if (sched->thread == NULL) { + evsched_deinit(sched); + return KNOT_ENOMEM; + } + + return KNOT_EOK; +} + +void evsched_deinit(evsched_t *sched) +{ + if (sched == NULL) { + return; + } + + /* Deinitialize event calendar. */ + pthread_mutex_destroy(&sched->heap_lock); + pthread_cond_destroy(&sched->notify); + + while (!EMPTY_HEAP(&sched->heap)) { + event_t *e = (event_t *)*HHEAD(&sched->heap); + heap_delmin(&sched->heap); + evsched_event_free(e); + } + + heap_deinit(&sched->heap); + + if (sched->thread != NULL) { + dt_delete(&sched->thread); + } + + /* Clear the structure. */ + memset(sched, 0, sizeof(evsched_t)); +} + +event_t *evsched_event_create(evsched_t *sched, event_cb_t cb, void *data) +{ + /* Create event. */ + if (sched == NULL) { + return NULL; + } + + /* Allocate. */ + event_t *e = malloc(sizeof(event_t)); + if (e == NULL) { + return NULL; + } + + /* Initialize. */ + memset(e, 0, sizeof(event_t)); + e->sched = sched; + e->cb = cb; + e->data = data; + e->hpos.pos=0; + + return e; +} + +void evsched_event_free(event_t *ev) +{ + if (ev == NULL) { + return; + } + + free(ev); +} + +int evsched_schedule(event_t *ev, uint32_t dt) +{ + if (ev == NULL || ev->sched == NULL) { + return KNOT_EINVAL; + } + + struct timeval new_time = timeval_in(dt); + + evsched_t *sched = ev->sched; + + /* Lock calendar. */ + pthread_mutex_lock(&sched->heap_lock); + + ev->tv = new_time; + + /* Make sure it's not already enqueued. */ + int found = heap_find(&sched->heap, (heap_val_t *)ev); + if (found > 0) { + heap_replace(&sched->heap, found, (heap_val_t *)ev); + } else { + heap_insert(&sched->heap, (heap_val_t *)ev); + } + + /* Unlock calendar. */ + pthread_cond_signal(&sched->notify); + pthread_mutex_unlock(&sched->heap_lock); + + return KNOT_EOK; +} + +int evsched_cancel(event_t *ev) +{ + if (ev == NULL || ev->sched == NULL) { + return KNOT_EINVAL; + } + + evsched_t *sched = ev->sched; + + /* Lock calendar. */ + pthread_mutex_lock(&sched->heap_lock); + + int found = heap_find(&sched->heap, (heap_val_t *)ev); + if (found > 0) { + heap_delete(&sched->heap, found); + } + + /* Unlock calendar. */ + pthread_cond_signal(&sched->notify); + pthread_mutex_unlock(&sched->heap_lock); + + /* Reset event timer. */ + memset(&ev->tv, 0, sizeof(struct timeval)); + + return KNOT_EOK; +} + +void evsched_start(evsched_t *sched) +{ + dt_start(sched->thread); +} + +void evsched_stop(evsched_t *sched) +{ + pthread_mutex_lock(&sched->heap_lock); + dt_stop(sched->thread); + pthread_cond_signal(&sched->notify); + pthread_mutex_unlock(&sched->heap_lock); +} + +void evsched_join(evsched_t *sched) +{ + dt_join(sched->thread); +} + +void evsched_pause(evsched_t *sched) +{ + pthread_mutex_lock(&sched->heap_lock); + sched->paused = true; + pthread_mutex_unlock(&sched->heap_lock); +} + +void evsched_resume(evsched_t *sched) +{ + pthread_mutex_lock(&sched->heap_lock); + sched->paused = false; + pthread_cond_signal(&sched->notify); + pthread_mutex_unlock(&sched->heap_lock); +} diff --git a/src/knot/common/evsched.h b/src/knot/common/evsched.h new file mode 100644 index 0000000..13a236f --- /dev/null +++ b/src/knot/common/evsched.h @@ -0,0 +1,153 @@ +/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \brief Event scheduler. + */ + +#pragma once + +#include <pthread.h> +#include <stdbool.h> +#include <stdint.h> +#include <sys/time.h> + +#include "knot/server/dthreads.h" +#include "contrib/ucw/heap.h" + +/* Forward decls. */ +struct evsched; +struct event; + +/*! + * \brief Event callback. + * + * Pointer to whole event structure is passed to the callback. + * Callback should return 0 on success and negative integer on error. + * + * Example callback: + * \code + * void print_callback(event_t *t) { + * printf("Callback: %s\n", t->data); + * } + * \endcode + */ +typedef void (*event_cb_t)(struct event *); + +/*! + * \brief Event structure. + */ +typedef struct event { + struct heap_val hpos; + struct timeval tv; /*!< Event scheduled time. */ + void *data; /*!< Usable data ptr. */ + event_cb_t cb; /*!< Event callback. */ + struct evsched *sched; /*!< Scheduler for this event. */ +} event_t; + +/*! + * \brief Event scheduler structure. + */ +typedef struct evsched { + volatile bool paused; /*!< Temporarily stop processing events. */ + pthread_mutex_t heap_lock; /*!< Event heap locking. */ + pthread_cond_t notify; /*!< Event heap notification. */ + struct heap heap; /*!< Event heap. */ + void *ctx; /*!< Scheduler context. */ + dt_unit_t *thread; +} evsched_t; + +/*! + * \brief Initialize event scheduler instance. + * + * \retval New instance on success. + * \retval NULL on error. + */ +int evsched_init(evsched_t *sched, void *ctx); + +/*! + * \brief Deinitialize and free event scheduler instance. + * + * \param sched Pointer to event scheduler instance. + */ +void evsched_deinit(evsched_t *sched); + +/*! + * \brief Create a callback event. + * + * \note Scheduler takes ownership of scheduled events. Created, but unscheduled + * events are in the ownership of the caller. + * + * \param sched Pointer to event scheduler instance. + * \param cb Callback handler. + * \param data Data for callback. + * + * \retval New instance on success. + * \retval NULL on error. + */ +event_t *evsched_event_create(evsched_t *sched, event_cb_t cb, void *data); + +/*! + * \brief Dispose event instance. + * + * \param ev Event instance. + */ +void evsched_event_free(event_t *ev); + +/*! + * \brief Schedule an event. + * + * \note This function checks if the event was already scheduled, if it was + * then it replaces this timer with the newer value. + * Running events are not canceled or waited for. + * + * \param ev Prepared event. + * \param dt Time difference in milliseconds from now (dt is relative). + * + * \retval KNOT_EOK on success. + * \retval KNOT_EINVAL + */ +int evsched_schedule(event_t *ev, uint32_t dt); + +/*! + * \brief Cancel a scheduled event. + * + * \warning May block until current running event is finished (as it cannot + * interrupt running event). + * + * \warning Never cancel event in it's callback. As it never finishes, + * it deadlocks. + * + * \param ev Scheduled event. + * + * \retval KNOT_EOK + * \retval KNOT_EINVAL + */ +int evsched_cancel(event_t *ev); + +/*! \brief Start event processing threads. */ +void evsched_start(evsched_t *sched); + +/*! \brief Stop event processing threads. */ +void evsched_stop(evsched_t *sched); + +/*! \brief Join event processing threads. */ +void evsched_join(evsched_t *sched); + +/*! \brief Temporarily stop processing events. */ +void evsched_pause(evsched_t *sched); + +/*! \brief Resume processing events. */ +void evsched_resume(evsched_t *sched); diff --git a/src/knot/common/fdset.c b/src/knot/common/fdset.c new file mode 100644 index 0000000..3d78fdf --- /dev/null +++ b/src/knot/common/fdset.c @@ -0,0 +1,151 @@ +/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <time.h> +#include "knot/common/fdset.h" +#include "contrib/time.h" +#include "libknot/errcode.h" + +/* Realloc memory or return error (part of fdset_resize). */ +#define MEM_RESIZE(tmp, p, n) \ + if ((tmp = realloc((p), (n) * sizeof(*p))) == NULL) \ + return KNOT_ENOMEM; \ + (p) = tmp; + +static int fdset_resize(fdset_t *set, unsigned size) +{ + void *tmp = NULL; + MEM_RESIZE(tmp, set->ctx, size); + MEM_RESIZE(tmp, set->pfd, size); + MEM_RESIZE(tmp, set->timeout, size); + set->size = size; + return KNOT_EOK; +} + +int fdset_init(fdset_t *set, unsigned size) +{ + if (set == NULL) { + return KNOT_EINVAL; + } + + memset(set, 0, sizeof(fdset_t)); + return fdset_resize(set, size); +} + +int fdset_clear(fdset_t* set) +{ + if (set == NULL) { + return KNOT_EINVAL; + } + + free(set->ctx); + free(set->pfd); + free(set->timeout); + memset(set, 0, sizeof(fdset_t)); + return KNOT_EOK; +} + +int fdset_add(fdset_t *set, int fd, unsigned events, void *ctx) +{ + if (set == NULL || fd < 0) { + return KNOT_EINVAL; + } + + /* Realloc needed. */ + if (set->n == set->size && fdset_resize(set, set->size + FDSET_INIT_SIZE)) + return KNOT_ENOMEM; + + /* Initialize. */ + int i = set->n++; + set->pfd[i].fd = fd; + set->pfd[i].events = events; + set->pfd[i].revents = 0; + set->ctx[i] = ctx; + set->timeout[i] = 0; + + /* Return index to this descriptor. */ + return i; +} + +int fdset_remove(fdset_t *set, unsigned i) +{ + if (set == NULL || i >= set->n) { + return KNOT_EINVAL; + } + + /* Decrement number of elms. */ + --set->n; + + /* Nothing else if it is the last one. + * Move last -> i if some remain. */ + unsigned last = set->n; /* Already decremented */ + if (i < last) { + set->pfd[i] = set->pfd[last]; + set->timeout[i] = set->timeout[last]; + set->ctx[i] = set->ctx[last]; + } + + return KNOT_EOK; +} + +int fdset_set_watchdog(fdset_t* set, int i, int interval) +{ + if (set == NULL || i >= set->n) { + return KNOT_EINVAL; + } + + /* Lift watchdog if interval is negative. */ + if (interval < 0) { + set->timeout[i] = 0; + return KNOT_EOK; + } + + /* Update clock. */ + struct timespec now = time_now(); + + set->timeout[i] = now.tv_sec + interval; /* Only seconds precision. */ + return KNOT_EOK; +} + +int fdset_sweep(fdset_t* set, fdset_sweep_cb_t cb, void *data) +{ + if (set == NULL || cb == NULL) { + return KNOT_EINVAL; + } + + /* Get time threshold. */ + struct timespec now = time_now(); + + unsigned i = 0; + while (i < set->n) { + + /* Check sweep state, remove if requested. */ + if (set->timeout[i] > 0 && set->timeout[i] <= now.tv_sec) { + if (cb(set, i, data) == FDSET_SWEEP) { + if (fdset_remove(set, i) == KNOT_EOK) + continue; /* Stay on the index. */ + } + } + + /* Next descriptor. */ + ++i; + } + + return KNOT_EOK; +} diff --git a/src/knot/common/fdset.h b/src/knot/common/fdset.h new file mode 100644 index 0000000..2d5ce02 --- /dev/null +++ b/src/knot/common/fdset.h @@ -0,0 +1,112 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \brief I/O multiplexing with context and timeouts for each fd. + */ + +#pragma once + +#include <stddef.h> +#include <poll.h> +#include <sys/time.h> +#include <signal.h> + +#define FDSET_INIT_SIZE 256 /* Resize step. */ + +/*! \brief Set of filedescriptors with associated context and timeouts. */ +typedef struct fdset { + unsigned n; /*!< Active fds. */ + unsigned size; /*!< Array size (allocated). */ + void* *ctx; /*!< Context for each fd. */ + struct pollfd *pfd; /*!< poll state for each fd */ + time_t *timeout; /*!< Timeout for each fd (seconds precision). */ +} fdset_t; + +/*! \brief Mark-and-sweep state. */ +enum fdset_sweep_state { + FDSET_KEEP, + FDSET_SWEEP +}; + +/*! \brief Sweep callback (set, index, data) */ +typedef enum fdset_sweep_state (*fdset_sweep_cb_t)(fdset_t*, int, void*); + +/*! + * \brief Initialize fdset to given size. + */ +int fdset_init(fdset_t *set, unsigned size); + +/*! + * \brief Destroy FDSET. + * + * \retval 0 if successful. + * \retval -1 on error. + */ +int fdset_clear(fdset_t* set); + +/*! + * \brief Add file descriptor to watched set. + * + * \param set Target set. + * \param fd Added file descriptor. + * \param events Mask of watched events. + * \param ctx Context (optional). + * + * \retval index of the added fd if successful. + * \retval -1 on errors. + */ +int fdset_add(fdset_t *set, int fd, unsigned events, void *ctx); + +/*! + * \brief Remove file descriptor from watched set. + * + * \param set Target set. + * \param i Index of the removed fd. + * + * \retval 0 if successful. + * \retval -1 on errors. + */ +int fdset_remove(fdset_t *set, unsigned i); + +/*! + * \brief Set file descriptor watchdog interval. + * + * Set time (interval from now) after which the associated file descriptor + * should be sweeped (see fdset_sweep). Good example is setting a grace period + * of N seconds between socket activity. If socket is not active within + * <now, now + interval>, it is sweeped and potentially closed. + * + * \param set Target set. + * \param i Index for the file descriptor. + * \param interval Allowed interval without activity (seconds). + * -1 disables watchdog timer + * + * \retval 0 if successful. + * \retval -1 on errors. + */ +int fdset_set_watchdog(fdset_t* set, int i, int interval); + +/*! + * \brief Sweep file descriptors with exceeding inactivity period. + * + * \param set Target set. + * \param cb Callback for sweeped descriptors. + * \param data Pointer to extra data. + * + * \retval number of sweeped descriptors. + * \retval -1 on errors. + */ +int fdset_sweep(fdset_t* set, fdset_sweep_cb_t cb, void *data); diff --git a/src/knot/common/log.c b/src/knot/common/log.c new file mode 100644 index 0000000..8a35f37 --- /dev/null +++ b/src/knot/common/log.c @@ -0,0 +1,491 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdarg.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/time.h> +#include <time.h> +#include <urcu.h> + +#ifdef ENABLE_SYSTEMD +#define SD_JOURNAL_SUPPRESS_LOCATION 1 +#include <systemd/sd-journal.h> +#include <systemd/sd-daemon.h> +#endif + +#include "knot/common/log.h" +#include "libknot/libknot.h" +#include "contrib/ucw/lists.h" + +/*! Single log message buffer length (one line). */ +#define LOG_BUFLEN 512 +#define NULL_ZONE_STR "?" + +#ifdef ENABLE_SYSTEMD +int use_journal = 0; +#endif + +/*! Log context. */ +typedef struct { + size_t target_count; /*!< Log target count. */ + int *target; /*!< Log targets. */ + size_t file_count; /*!< Open files count. */ + FILE **file; /*!< Open files. */ + log_flag_t flags; /*!< Formatting flags. */ +} log_t; + +/*! Log singleton. */ +log_t *s_log = NULL; + +static bool log_isopen(void) +{ + return s_log != NULL; +} + +static void sink_free(log_t *log) +{ + if (log == NULL) { + return; + } + + // Close open log files. + for (int i = 0; i < log->file_count; ++i) { + fclose(log->file[i]); + } + free(log->target); + free(log->file); + free(log); +} + +/*! + * \brief Create logging targets respecting their canonical order. + * + * Facilities ordering: Syslog, Stderr, Stdout, File0... + */ +static log_t *sink_setup(size_t file_count) +{ + log_t *log = malloc(sizeof(*log)); + if (log == NULL) { + return NULL; + } + memset(log, 0, sizeof(*log)); + + // Reserve space for targets. + log->target_count = LOG_TARGET_FILE + file_count; + log->target = malloc(LOG_SOURCE_ANY * sizeof(int) * log->target_count); + if (!log->target) { + free(log); + return NULL; + } + memset(log->target, 0, LOG_SOURCE_ANY * sizeof(int) * log->target_count); + + // Reserve space for log files. + if (file_count > 0) { + log->file = malloc(sizeof(FILE *) * file_count); + if (!log->file) { + free(log->target); + free(log); + return NULL; + } + memset(log->file, 0, sizeof(FILE *) * file_count); + } + + return log; +} + +static void sink_publish(log_t *log) +{ + log_t **current_log = &s_log; + log_t *old_log = rcu_xchg_pointer(current_log, log); + synchronize_rcu(); + sink_free(old_log); +} + +static int *src_levels(log_t *log, log_target_t target, log_source_t src) +{ + assert(src < LOG_SOURCE_ANY); + return &log->target[LOG_SOURCE_ANY * target + src]; +} + +static void sink_levels_set(log_t *log, log_target_t target, log_source_t src, int levels) +{ + // Assign levels to the specified source. + if (src != LOG_SOURCE_ANY) { + *src_levels(log, target, src) = levels; + } else { + // ANY ~ set levels to all sources. + for (int i = 0; i < LOG_SOURCE_ANY; ++i) { + *src_levels(log, target, i) = levels; + } + } +} + +static void sink_levels_add(log_t *log, log_target_t target, log_source_t src, int levels) +{ + // Add levels to the specified source. + if (src != LOG_SOURCE_ANY) { + *src_levels(log, target, src) |= levels; + } else { + // ANY ~ add levels to all sources. + for (int i = 0; i < LOG_SOURCE_ANY; ++i) { + *src_levels(log, target, i) |= levels; + } + } +} + +void log_init(void) +{ + // Setup initial state. + int emask = LOG_MASK(LOG_CRIT) | LOG_MASK(LOG_ERR) | LOG_MASK(LOG_WARNING); + int imask = LOG_MASK(LOG_NOTICE) | LOG_MASK(LOG_INFO); + + // Publish base log sink. + log_t *log = sink_setup(0); + if (log == NULL) { + fprintf(stderr, "Failed to setup logging\n"); + return; + } + +#ifdef ENABLE_SYSTEMD + // Should only use the journal if system was booted with systemd. + use_journal = sd_booted(); +#endif + + sink_levels_set(log, LOG_TARGET_SYSLOG, LOG_SOURCE_ANY, emask); + sink_levels_set(log, LOG_TARGET_STDERR, LOG_SOURCE_ANY, emask); + sink_levels_set(log, LOG_TARGET_STDOUT, LOG_SOURCE_ANY, imask); + sink_publish(log); + + setlogmask(LOG_UPTO(LOG_DEBUG)); + openlog(PACKAGE_NAME, LOG_PID, LOG_DAEMON); +} + +void log_close(void) +{ + sink_publish(NULL); + + fflush(stdout); + fflush(stderr); + + closelog(); +} + +void log_flag_set(log_flag_t flag) +{ + if (log_isopen()) { + s_log->flags |= flag; + } +} + +void log_levels_set(log_target_t target, log_source_t src, int levels) +{ + if (log_isopen()) { + sink_levels_set(s_log, target, src, levels); + } +} + +void log_levels_add(log_target_t target, log_source_t src, int levels) +{ + if (log_isopen()) { + sink_levels_add(s_log, target, src, levels); + } +} + +static void emit_log_msg(int level, log_source_t src, const char *zone, + size_t zone_len, const char *msg, const char *param) +{ + log_t *log = s_log; + + // Syslog target. + if (*src_levels(log, LOG_TARGET_SYSLOG, src) & LOG_MASK(level)) { +#ifdef ENABLE_SYSTEMD + if (use_journal) { + char *zone_fmt = zone ? "ZONE=%.*s." : NULL; + sd_journal_send("PRIORITY=%d", level, + "MESSAGE=%s", msg, + zone_fmt, zone_len, zone, + param, NULL); + } else +#endif + { + syslog(level, "%s", msg); + } + } + + // Prefix date and time. + char tstr[LOG_BUFLEN] = { 0 }; + if (!(s_log->flags & LOG_FLAG_NOTIMESTAMP)) { + struct tm lt; + struct timeval tv; + gettimeofday(&tv, NULL); + time_t sec = tv.tv_sec; + if (localtime_r(&sec, <) != NULL) { + strftime(tstr, sizeof(tstr), KNOT_LOG_TIME_FORMAT " ", <); + } + } + + // Other log targets. + for (int i = LOG_TARGET_STDERR; i < LOG_TARGET_FILE + log->file_count; ++i) { + if (*src_levels(log, i, src) & LOG_MASK(level)) { + FILE *stream; + switch (i) { + case LOG_TARGET_STDERR: stream = stderr; break; + case LOG_TARGET_STDOUT: stream = stdout; break; + default: stream = log->file[i - LOG_TARGET_FILE]; break; + } + + // Print the message. + fprintf(stream, "%s%s\n", tstr, msg); + if (stream == stdout) { + fflush(stream); + } + } + } +} + +static const char *level_prefix(int level) +{ + switch (level) { + case LOG_DEBUG: return "debug"; + case LOG_INFO: return "info"; + case LOG_NOTICE: return "notice"; + case LOG_WARNING: return "warning"; + case LOG_ERR: return "error"; + case LOG_CRIT: return "critical"; + default: return NULL; + }; +} + +static int log_msg_add(char **write, size_t *capacity, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + int written = vsnprintf(*write, *capacity, fmt, args); + va_end(args); + + if (written < 0 || written >= *capacity) { + return KNOT_ESPACE; + } + + *write += written; + *capacity -= written; + + return KNOT_EOK; +} + +static void log_msg_text(int level, log_source_t src, const char *zone, + const char *fmt, va_list args, const char *param) +{ + if (!log_isopen() || src == LOG_SOURCE_ANY) { + return; + } + + // Buffer for log message. + char buff[LOG_BUFLEN]; + char *write = buff; + size_t capacity = sizeof(buff); + + rcu_read_lock(); + + // Prefix error level. + if (level != LOG_INFO || !(s_log->flags & LOG_FLAG_NOINFO)) { + const char *prefix = level_prefix(level); + int ret = log_msg_add(&write, &capacity, "%s: ", prefix); + if (ret != KNOT_EOK) { + rcu_read_unlock(); + return; + } + } + + // Prefix zone name. + size_t zone_len = 0; + if (zone != NULL) { + zone_len = strlen(zone); + if (zone_len > 0 && zone[zone_len - 1] == '.') { + zone_len--; + } + + int ret = log_msg_add(&write, &capacity, "[%.*s.] ", (int)zone_len, zone); + if (ret != KNOT_EOK) { + rcu_read_unlock(); + return; + } + } + + // Compile log message. + int ret = vsnprintf(write, capacity, fmt, args); + if (ret >= 0) { + // Send to logging targets. + emit_log_msg(level, src, zone, zone_len, buff, param); + } + + rcu_read_unlock(); +} + +void log_fmt(int priority, log_source_t src, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + log_msg_text(priority, src, NULL, fmt, args, NULL); + va_end(args); +} + +void log_fmt_zone(int priority, log_source_t src, const knot_dname_t *zone, + const char *param, const char *fmt, ...) +{ + char buff[KNOT_DNAME_TXT_MAXLEN + 1]; + char *zone_str = knot_dname_to_str(buff, zone, sizeof(buff)); + if (zone_str == NULL) { + zone_str = NULL_ZONE_STR; + } + + va_list args; + va_start(args, fmt); + log_msg_text(priority, src, zone_str, fmt, args, param); + va_end(args); +} + +void log_fmt_zone_str(int priority, log_source_t src, const char *zone, + const char *fmt, ...) +{ + if (zone == NULL) { + zone = NULL_ZONE_STR; + } + + va_list args; + va_start(args, fmt); + log_msg_text(priority, src, zone, fmt, args, NULL); + va_end(args); +} + +int log_update_privileges(int uid, int gid) +{ + if (!log_isopen()) { + return KNOT_EOK; + } + + for (int i = 0; i < s_log->file_count; ++i) { + if (fchown(fileno(s_log->file[i]), uid, gid) < 0) { + return knot_map_errno(); + } + } + + return KNOT_EOK; +} + +static log_target_t get_logtype(const char *logname) +{ + assert(logname); + + if (strcasecmp(logname, "syslog") == 0) { + return LOG_TARGET_SYSLOG; + } else if (strcasecmp(logname, "stderr") == 0) { + return LOG_TARGET_STDERR; + } else if (strcasecmp(logname, "stdout") == 0) { + return LOG_TARGET_STDOUT; + } else { + return LOG_TARGET_FILE; + } +} + +static int log_open_file(log_t *log, const char *filename) +{ + assert(LOG_TARGET_FILE + log->file_count < log->target_count); + + // Open the file. + log->file[log->file_count] = fopen(filename, "a"); + if (log->file[log->file_count] == NULL) { + return knot_map_errno(); + } + + // Disable buffering. + setvbuf(log->file[log->file_count], NULL, _IONBF, 0); + + return LOG_TARGET_FILE + log->file_count++; +} + +void log_reconfigure(conf_t *conf) +{ + // Use defaults if no 'log' section is configured. + if (conf_id_count(conf, C_LOG) == 0) { + log_close(); + log_init(); + return; + } + + // Find maximum log target id. + unsigned files = 0; + for (conf_iter_t iter = conf_iter(conf, C_LOG); iter.code == KNOT_EOK; + conf_iter_next(conf, &iter)) { + conf_val_t id = conf_iter_id(conf, &iter); + if (get_logtype(conf_str(&id)) == LOG_TARGET_FILE) { + ++files; + } + } + + // Initialize logsystem. + log_t *log = sink_setup(files); + if (log == NULL) { + fprintf(stderr, "Failed to setup logging\n"); + return; + } + + // Setup logs. + for (conf_iter_t iter = conf_iter(conf, C_LOG); iter.code == KNOT_EOK; + conf_iter_next(conf, &iter)) { + conf_val_t id = conf_iter_id(conf, &iter); + const char *logname = conf_str(&id); + + // Get target. + int target = get_logtype(logname); + if (target == LOG_TARGET_FILE) { + target = log_open_file(log, logname); + if (target < 0) { + log_error("failed to open log, file '%s' (%s)", + logname, knot_strerror(target)); + continue; + } + } + + conf_val_t levels_val; + unsigned levels; + + // Set SERVER logging. + levels_val = conf_id_get(conf, C_LOG, C_SERVER, &id); + levels = conf_opt(&levels_val); + sink_levels_add(log, target, LOG_SOURCE_SERVER, levels); + + // Set CONTROL logging. + levels_val = conf_id_get(conf, C_LOG, C_CTL, &id); + levels = conf_opt(&levels_val); + sink_levels_add(log, target, LOG_SOURCE_CONTROL, levels); + + // Set ZONE logging. + levels_val = conf_id_get(conf, C_LOG, C_ZONE, &id); + levels = conf_opt(&levels_val); + sink_levels_add(log, target, LOG_SOURCE_ZONE, levels); + + // Set ANY logging. + levels_val = conf_id_get(conf, C_LOG, C_ANY, &id); + levels = conf_opt(&levels_val); + sink_levels_add(log, target, LOG_SOURCE_ANY, levels); + } + + sink_publish(log); +} diff --git a/src/knot/common/log.h b/src/knot/common/log.h new file mode 100644 index 0000000..94b6b39 --- /dev/null +++ b/src/knot/common/log.h @@ -0,0 +1,185 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \brief Logging facility. + * + * Supported log levels/priorities: + * LOG_CRIT, LOG_ERR, LOG_WARNING, LOG_NOTICE, LOG_INFO, and LOG_DEBUG. + * + * \see syslog.h + */ + +#pragma once + +#include <assert.h> +#include <syslog.h> +#include <stdint.h> +#include <stdbool.h> + +#include "libknot/dname.h" +#include "knot/conf/conf.h" + +/*! \brief Format for timestamps in log files. */ +#define KNOT_LOG_TIME_FORMAT "%Y-%m-%dT%H:%M:%S" + +/*! \brief Logging targets. */ +typedef enum { + LOG_TARGET_SYSLOG = 0, /*!< System log. */ + LOG_TARGET_STDERR = 1, /*!< Standard error stream. */ + LOG_TARGET_STDOUT = 2, /*!< Standard output stream. */ + LOG_TARGET_FILE = 3 /*!< Generic logging to a file (unbuffered). */ +} log_target_t; + +/*! \brief Logging sources. */ +typedef enum { + LOG_SOURCE_SERVER = 0, /*!< Server module. */ + LOG_SOURCE_CONTROL = 1, /*!< Server control module. */ + LOG_SOURCE_ZONE = 2, /*!< Zone manipulation module. */ + LOG_SOURCE_ANY = 3 /*!< Any module. */ +} log_source_t; + +/*! \brief Logging format flags. */ +typedef enum { + LOG_FLAG_NOTIMESTAMP = 1 << 0, /*!< Don't print timestamp prefix. */ + LOG_FLAG_NOINFO = 1 << 1 /*!< Don't print info level prefix. */ +} log_flag_t; + +/*! + * \brief Setup logging subsystem. + */ +void log_init(void); + +/*! + * \brief Close and deinitialize log. + */ +void log_close(void); + +/*! + * \brief Set logging format flag. + */ +void log_flag_set(log_flag_t flag); + +/*! + * \brief Set log levels for given target. + * + * \param target Logging target index (LOG_TARGET_SYSLOG...). + * \param src Logging source (LOG_SOURCE_SERVER...LOG_SOURCE_ANY). + * \param levels Bitmask of specified log levels. + */ +void log_levels_set(log_target_t target, log_source_t src, int levels); + +/*! + * \brief Add log levels to a given target. + * + * New levels are added on top of existing, the resulting levels set is + * "old_levels OR new_levels". + * + * \param target Logging target index (LOG_TARGET_SYSLOG...). + * \param src Logging source (LOG_SOURCE_SERVER...LOG_SOURCE_ANY). + * \param levels Bitmask of specified log levels. + */ +void log_levels_add(log_target_t target, log_source_t src, int levels); + +/*! + * \brief Log message into server category. + * + * Function follows printf() format. + * + * \note LOG_SOURCE_ANY is not a valid value for the src parameter. + * + * \param priority Message priority. + * \param src Message source (LOG_SOURCE_SERVER...LOG_SOURCE_ZONE). + * \param fmt Content of the logged message. + */ +void log_fmt(int priority, log_source_t src, const char *fmt, ...) +__attribute__((format(printf, 3, 4))); + +/*! + * \brief Log message into zone category. + * + * \see log_fmt + * + * \param zone Zone name in wire format. + * \param priority Message priority. + * \param src Message source (LOG_SOURCE_SERVER...LOG_SOURCE_ZONE). + * \param fmt Content of the logged message. + */ +void log_fmt_zone(int priority, log_source_t src, const knot_dname_t *zone, + const char *param, const char *fmt, ...) +__attribute__((format(printf, 5, 6))); + +/*! + * \brief Log message into zone category. + * + * \see log_fmt + * + * \param zone Zone name as an ASCII string. + * \param priority Message priority. + * \param src Message source (LOG_SOURCE_SERVER...LOG_SOURCE_ZONE). + * \param fmt Content of the logged message. + */ +void log_fmt_zone_str(int priority, log_source_t src, const char *zone, const char *fmt, ...) +__attribute__((format(printf, 4, 5))); + +/*! + * \brief Convenient logging macros. + */ +#define log_fatal(msg, ...) log_fmt(LOG_CRIT, LOG_SOURCE_SERVER, msg, ##__VA_ARGS__) +#define log_error(msg, ...) log_fmt(LOG_ERR, LOG_SOURCE_SERVER, msg, ##__VA_ARGS__) +#define log_warning(msg, ...) log_fmt(LOG_WARNING, LOG_SOURCE_SERVER, msg, ##__VA_ARGS__) +#define log_notice(msg, ...) log_fmt(LOG_NOTICE, LOG_SOURCE_SERVER, msg, ##__VA_ARGS__) +#define log_info(msg, ...) log_fmt(LOG_INFO, LOG_SOURCE_SERVER, msg, ##__VA_ARGS__) +#define log_debug(msg, ...) log_fmt(LOG_DEBUG, LOG_SOURCE_SERVER, msg, ##__VA_ARGS__) + +#define log_ctl_fatal(msg, ...) log_fmt(LOG_CRIT, LOG_SOURCE_CONTROL, msg, ##__VA_ARGS__) +#define log_ctl_error(msg, ...) log_fmt(LOG_ERR, LOG_SOURCE_CONTROL, msg, ##__VA_ARGS__) +#define log_ctl_warning(msg, ...) log_fmt(LOG_WARNING, LOG_SOURCE_CONTROL, msg, ##__VA_ARGS__) +#define log_ctl_notice(msg, ...) log_fmt(LOG_NOTICE, LOG_SOURCE_CONTROL, msg, ##__VA_ARGS__) +#define log_ctl_info(msg, ...) log_fmt(LOG_INFO, LOG_SOURCE_CONTROL, msg, ##__VA_ARGS__) +#define log_ctl_debug(msg, ...) log_fmt(LOG_DEBUG, LOG_SOURCE_CONTROL, msg, ##__VA_ARGS__) + +#define log_ctl_zone_str_error(zone, msg, ...) log_fmt_zone_str(LOG_ERR, LOG_SOURCE_CONTROL, zone, msg, ##__VA_ARGS__) +#define log_ctl_zone_str_info(zone, msg, ...) log_fmt_zone_str(LOG_INFO, LOG_SOURCE_CONTROL, zone, msg, ##__VA_ARGS__) +#define log_ctl_zone_str_debug(zone, msg, ...) log_fmt_zone_str(LOG_DEBUG, LOG_SOURCE_CONTROL, zone, msg, ##__VA_ARGS__) + +#define log_zone_fatal(zone, msg, ...) log_fmt_zone(LOG_CRIT, LOG_SOURCE_ZONE, zone, NULL, msg, ##__VA_ARGS__) +#define log_zone_error(zone, msg, ...) log_fmt_zone(LOG_ERR, LOG_SOURCE_ZONE, zone, NULL, msg, ##__VA_ARGS__) +#define log_zone_warning(zone, msg, ...) log_fmt_zone(LOG_WARNING, LOG_SOURCE_ZONE, zone, NULL, msg, ##__VA_ARGS__) +#define log_zone_notice(zone, msg, ...) log_fmt_zone(LOG_NOTICE, LOG_SOURCE_ZONE, zone, NULL, msg, ##__VA_ARGS__) +#define log_zone_info(zone, msg, ...) log_fmt_zone(LOG_INFO, LOG_SOURCE_ZONE, zone, NULL, msg, ##__VA_ARGS__) +#define log_zone_debug(zone, msg, ...) log_fmt_zone(LOG_DEBUG, LOG_SOURCE_ZONE, zone, NULL, msg, ##__VA_ARGS__) + +#define log_zone_str_fatal(zone, msg, ...) log_fmt_zone_str(LOG_CRIT, LOG_SOURCE_ZONE, zone, msg, ##__VA_ARGS__) +#define log_zone_str_error(zone, msg, ...) log_fmt_zone_str(LOG_ERR, LOG_SOURCE_ZONE, zone, msg, ##__VA_ARGS__) +#define log_zone_str_warning(zone, msg, ...) log_fmt_zone_str(LOG_WARNING, LOG_SOURCE_ZONE, zone, msg, ##__VA_ARGS__) +#define log_zone_str_notice(zone, msg, ...) log_fmt_zone_str(LOG_NOTICE, LOG_SOURCE_ZONE, zone, msg, ##__VA_ARGS__) +#define log_zone_str_info(zone, msg, ...) log_fmt_zone_str(LOG_INFO, LOG_SOURCE_ZONE, zone, msg, ##__VA_ARGS__) +#define log_zone_str_debug(zone, msg, ...) log_fmt_zone_str(LOG_DEBUG, LOG_SOURCE_ZONE, zone, msg, ##__VA_ARGS__) + +/*! + * \brief Update open files ownership. + * + * \param uid New owner id. + * \param gid New group id. + * + * \return Error code, KNOT_EOK if success. + */ +int log_update_privileges(int uid, int gid); + +/*! + * \brief Setup logging facilities from config. + */ +void log_reconfigure(conf_t *conf); diff --git a/src/knot/common/process.c b/src/knot/common/process.c new file mode 100644 index 0000000..4a46613 --- /dev/null +++ b/src/knot/common/process.c @@ -0,0 +1,189 @@ +/* Copyright (C) 2015 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <errno.h> +#include <fcntl.h> +#include <grp.h> +#include <pwd.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include "knot/common/log.h" +#include "knot/common/process.h" +#include "knot/conf/conf.h" +#include "libknot/errcode.h" + +static char* pid_filename(void) +{ + conf_val_t val = conf_get(conf(), C_SRV, C_RUNDIR); + char *rundir = conf_abs_path(&val, NULL); + val = conf_get(conf(), C_SRV, C_PIDFILE); + char *pidfile = conf_abs_path(&val, rundir); + free(rundir); + + return pidfile; +} + +static pid_t pid_read(const char *filename) +{ + if (filename == NULL) { + return 0; + } + + size_t len = 0; + char buf[64] = { 0 }; + + FILE *fp = fopen(filename, "r"); + if (fp == NULL) { + return 0; + } + + /* Read the content of the file. */ + len = fread(buf, 1, sizeof(buf) - 1, fp); + fclose(fp); + if (len < 1) { + return 0; + } + + /* Convert pid. */ + errno = 0; + char *end = 0; + unsigned long pid = strtoul(buf, &end, 10); + if (end == buf || *end != '\0'|| errno != 0) { + return 0; + } + + return (pid_t)pid; +} + +static int pid_write(const char *filename) +{ + if (filename == NULL) { + return KNOT_EINVAL; + } + + /* Convert. */ + char buf[64]; + int len = 0; + len = snprintf(buf, sizeof(buf), "%lu", (unsigned long)getpid()); + if (len < 0 || len >= sizeof(buf)) { + return KNOT_ENOMEM; + } + + /* Create file. */ + int ret = KNOT_EOK; + int fd = open(filename, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP); + if (fd >= 0) { + if (write(fd, buf, len) != len) { + ret = knot_map_errno(); + } + close(fd); + } else { + ret = knot_map_errno(); + } + + return ret; +} + +char *pid_check_and_create(void) +{ + struct stat st; + char *pidfile = pid_filename(); + pid_t pid = pid_read(pidfile); + + /* Check PID for existence and liveness. */ + if (pid > 0 && pid_running(pid)) { + log_error("server PID found, already running"); + free(pidfile); + return NULL; + } else if (stat(pidfile, &st) == 0) { + log_warning("removing stale PID file '%s'", pidfile); + pid_cleanup(); + } + + /* Create a PID file. */ + int ret = pid_write(pidfile); + if (ret != KNOT_EOK) { + log_error("failed to create a PID file '%s' (%s)", pidfile, + knot_strerror(ret)); + free(pidfile); + return NULL; + } + + return pidfile; +} + +void pid_cleanup(void) +{ + char *pidfile = pid_filename(); + if (pidfile != NULL) { + (void)unlink(pidfile); + free(pidfile); + } +} + +bool pid_running(pid_t pid) +{ + return kill(pid, 0) == 0; +} + +int proc_update_privileges(int uid, int gid) +{ +#ifdef HAVE_SETGROUPS + /* Drop supplementary groups. */ + if ((uid_t)uid != getuid() || (gid_t)gid != getgid()) { + if (setgroups(0, NULL) < 0) { + log_warning("failed to drop supplementary groups for " + "UID %d (%s)", getuid(), strerror(errno)); + } +# ifdef HAVE_INITGROUPS + struct passwd *pw; + if ((pw = getpwuid(uid)) == NULL) { + log_warning("failed to get passwd entry for UID %d (%s)", + uid, strerror(errno)); + } else { + if (initgroups(pw->pw_name, gid) < 0) { + log_warning("failed to set supplementary groups " + "for UID %d (%s)", uid, strerror(errno)); + } + } +# endif /* HAVE_INITGROUPS */ + } +#endif /* HAVE_SETGROUPS */ + + /* Watch uid/gid. */ + if ((gid_t)gid != getgid()) { + log_info("changing GID to %d", gid); + if (setregid(gid, gid) < 0) { + log_error("failed to change GID to %d", gid); + return KNOT_ERROR; + } + } + if ((uid_t)uid != getuid()) { + log_info("changing UID to %d", uid); + if (setreuid(uid, uid) < 0) { + log_error("failed to change UID to %d", uid); + return KNOT_ERROR; + } + } + + return KNOT_EOK; +} diff --git a/src/knot/common/process.h b/src/knot/common/process.h new file mode 100644 index 0000000..04fbc3a --- /dev/null +++ b/src/knot/common/process.h @@ -0,0 +1,59 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \brief Functions for POSIX process handling. + */ + +#pragma once + +#include <stdbool.h> +#include <unistd.h> + +/*! + * \brief Check if PID file exists and create it if possible. + * + * \retval NULL if failed. + * \retval Created PID file path. + */ +char *pid_check_and_create(void); + +/*! + * \brief Remove PID file. + * + * \warning PID file content won't be checked. + */ +void pid_cleanup(void); + +/*! + * \brief Return true if the PID is running. + * + * \param pid Process ID. + * + * \retval 1 if running. + * \retval 0 if not running (or error). + */ +bool pid_running(pid_t pid); + +/*! + * \brief Update process privileges to new UID/GID. + * + * \param uid New user ID. + * \param gid New group ID. + * + * \retval KNOT_EOK on success. + * \retval KNOT_EACCESS if storage is not writeable. + */ +int proc_update_privileges(int uid, int gid); diff --git a/src/knot/common/ref.c b/src/knot/common/ref.c new file mode 100644 index 0000000..5f188e4 --- /dev/null +++ b/src/knot/common/ref.c @@ -0,0 +1,44 @@ +/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdio.h> + +#include "knot/common/ref.h" + +void ref_init(ref_t *p, ref_destructor_t dtor) +{ + if (p) { + p->count = 0; + p->dtor = dtor; + } +} + +void ref_retain(ref_t *p) +{ + if (p) { + __sync_add_and_fetch(&p->count, 1); + } +} + +void ref_release(ref_t *p) +{ + if (p) { + int rc = __sync_sub_and_fetch(&p->count, 1); + if (rc == 0 && p->dtor) { + p->dtor(p); + } + } +} diff --git a/src/knot/common/ref.h b/src/knot/common/ref.h new file mode 100644 index 0000000..5f5ec97 --- /dev/null +++ b/src/knot/common/ref.h @@ -0,0 +1,78 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \brief Atomic reference counting structures. + * + * Reference counting allows implicit sharing of objects + * between threads with custom destructor functions. + */ + +#pragma once + +#include <stddef.h> + +struct ref; + +/*! \brief Prototype for object destructor callback. */ +typedef void (*ref_destructor_t)(struct ref * p); + +/*! + * \brief Structure for reference counting. + * + * Size equals to two sizes of pointer size. + * Structure may be embedded to the structures which + * we want to use for reference counting. + * + * \code + * struct mystruct { + * ref_t ref; + * int mydata; + * char *mystr; + * } + * \endcode + */ +typedef struct ref { + size_t count; /*! \brief Reference counter. */ + ref_destructor_t dtor; /*! \brief Object destructor function. */ +} ref_t; + +/*! + * \brief Initialize reference counter. + * + * Set reference counter to 0 and initialize destructor callback. + * + * \param p Reference-counted object. + * \param dtor Destructor function. + */ +void ref_init(ref_t *p, ref_destructor_t dtor); + +/*! + * \brief Mark object as used by the caller. + * + * Reference counter will be incremented. + * + * \param p Reference-counted object. + */ +void ref_retain(ref_t *p); + +/*! + * \brief Marks object as unused by the caller. + * + * Reference counter will be decremented. + * + * \param p Reference-counted object. + */ +void ref_release(ref_t *p); diff --git a/src/knot/common/stats.c b/src/knot/common/stats.c new file mode 100644 index 0000000..8f49bf9 --- /dev/null +++ b/src/knot/common/stats.c @@ -0,0 +1,295 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <inttypes.h> +#include <sys/stat.h> +#include <time.h> +#include <unistd.h> +#include <urcu.h> + +#include "contrib/files.h" +#include "knot/common/stats.h" +#include "knot/common/log.h" +#include "knot/nameserver/query_module.h" + +struct { + bool active_dumper; + pthread_t dumper; + uint32_t timer; + server_t *server; +} stats = { 0 }; + +typedef struct { + FILE *fd; + const list_t *query_modules; + const knot_dname_t *zone; + bool zone_emitted; +} dump_ctx_t; + +#define DUMP_STR(fd, level, name, ...) do { \ + fprintf(fd, "%-.*s"name": %s\n", level, " ", ##__VA_ARGS__); \ + } while (0) +#define DUMP_CTR(fd, level, name, ...) do { \ + fprintf(fd, "%-.*s"name": %"PRIu64"\n", level, " ", ##__VA_ARGS__); \ + } while (0) + +uint64_t server_zone_count(server_t *server) +{ + return knot_zonedb_size(server->zone_db); +} + +const stats_item_t server_stats[] = { + { "zone-count", server_zone_count }, + { 0 } +}; + +static void dump_counters(FILE *fd, int level, mod_ctr_t *ctr) +{ + for (uint32_t j = 0; j < ctr->count; j++) { + uint64_t counter = ATOMIC_GET(ctr->counters[j]); + + // Skip empty counters. + if (counter == 0) { + continue; + } + + if (ctr->idx_to_str != NULL) { + char *str = ctr->idx_to_str(j, ctr->count); + if (str != NULL) { + DUMP_CTR(fd, level, "%s", str, counter); + free(str); + } + } else { + DUMP_CTR(fd, level, "%u", j, counter); + } + } +} + +static void dump_modules(dump_ctx_t *ctx) +{ + int level = 0; + knotd_mod_t *mod = NULL; + WALK_LIST(mod, *ctx->query_modules) { + // Skip modules without statistics. + if (mod->stats_count == 0) { + continue; + } + + // Dump zone name. + if (ctx->zone != NULL) { + // Prevent from zone section override. + if (!ctx->zone_emitted) { + DUMP_STR(ctx->fd, 0, "zone", ""); + ctx->zone_emitted = true; + } + level = 1; + + char name[KNOT_DNAME_TXT_MAXLEN + 1]; + if (knot_dname_to_str(name, ctx->zone, sizeof(name)) == NULL) { + return; + } + DUMP_STR(ctx->fd, level++, "\"%s\"", name, ""); + } else { + level = 0; + } + + // Dump module counters. + DUMP_STR(ctx->fd, level, "%s", mod->id->name + 1, ""); + for (int i = 0; i < mod->stats_count; i++) { + mod_ctr_t *ctr = mod->stats + i; + if (ctr->name == NULL) { + // Empty counter. + continue; + } + if (ctr->count == 1) { + // Simple counter. + uint64_t counter = ATOMIC_GET(ctr->counter); + DUMP_CTR(ctx->fd, level + 1, "%s", ctr->name, counter); + } else { + // Array of counters. + DUMP_STR(ctx->fd, level + 1, "%s", ctr->name, ""); + dump_counters(ctx->fd, level + 2, ctr); + } + } + } +} + +static void zone_stats_dump(zone_t *zone, dump_ctx_t *ctx) +{ + if (EMPTY_LIST(zone->query_modules)) { + return; + } + + ctx->query_modules = &zone->query_modules; + ctx->zone = zone->name; + + dump_modules(ctx); +} + +static void dump_to_file(FILE *fd, server_t *server) +{ + char date[64] = ""; + + // Get formatted current time string. + struct tm tm; + time_t now = time(NULL); + localtime_r(&now, &tm); + strftime(date, sizeof(date), "%Y-%m-%dT%H:%M:%S%z", &tm); + + // Get the server identity. + conf_val_t val = conf_get(conf(), C_SRV, C_IDENT); + const char *ident = conf_str(&val); + if (ident == NULL || ident[0] == '\0') { + ident = conf()->hostname; + } + + // Dump record header. + fprintf(fd, + "---\n" + "time: %s\n" + "identity: %s\n", + date, ident); + + // Dump server statistics. + DUMP_STR(fd, 0, "server", ""); + for (const stats_item_t *item = server_stats; item->name != NULL; item++) { + DUMP_CTR(fd, 1, "%s", item->name, item->val(server)); + } + + dump_ctx_t ctx = { + .fd = fd, + .query_modules = conf()->query_modules, + }; + + // Dump global statistics. + dump_modules(&ctx); + + // Dump zone statistics. + knot_zonedb_foreach(server->zone_db, zone_stats_dump, &ctx); +} + +static void dump_stats(server_t *server) +{ + conf_val_t val = conf_get(conf(), C_SRV, C_RUNDIR); + char *rundir = conf_abs_path(&val, NULL); + val = conf_get(conf(), C_STATS, C_FILE); + char *file_name = conf_abs_path(&val, rundir); + free(rundir); + + val = conf_get(conf(), C_STATS, C_APPEND); + bool append = conf_bool(&val); + + // Open or create output file. + FILE *fd = NULL; + char *tmp_name = NULL; + if (append) { + fd = fopen(file_name, "a"); + if (fd == NULL) { + log_error("stats, failed to append file '%s' (%s)", + file_name, knot_strerror(knot_map_errno())); + free(file_name); + return; + } + } else { + int ret = open_tmp_file(file_name, &tmp_name, &fd, + S_IRUSR | S_IWUSR | S_IRGRP); + if (ret != KNOT_EOK) { + log_error("stats, failed to open file '%s' (%s)", + file_name, knot_strerror(ret)); + free(file_name); + return; + } + } + assert(fd); + + // Dump stats into the file. + dump_to_file(fd, server); + + fflush(fd); + fclose(fd); + + // Switch the file contents. + if (!append) { + int ret = rename(tmp_name, file_name); + if (ret != 0) { + log_error("stats, failed to access file '%s' (%s)", + file_name, knot_strerror(knot_map_errno())); + unlink(tmp_name); + } + free(tmp_name); + } + + log_debug("stats, dumped into file '%s'", file_name); + free(file_name); +} + +static void *dumper(void *data) +{ + while (true) { + assert(stats.timer > 0); + sleep(stats.timer); + + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + rcu_read_lock(); + dump_stats(stats.server); + rcu_read_unlock(); + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + } + + return NULL; +} + +void stats_reconfigure(conf_t *conf, server_t *server) +{ + if (conf == NULL || server == NULL) { + return; + } + + // Update server context. + stats.server = server; + + conf_val_t val = conf_get(conf, C_STATS, C_TIMER); + stats.timer = conf_int(&val); + if (stats.timer > 0) { + // Check if dumping is already running. + if (stats.active_dumper) { + return; + } + + int ret = pthread_create(&stats.dumper, NULL, dumper, NULL); + if (ret != 0) { + log_error("stats, failed to launch periodic dumping (%s)", + knot_strerror(knot_map_errno_code(ret))); + } else { + stats.active_dumper = true; + } + // Stop current dumping. + } else if (stats.active_dumper) { + pthread_cancel(stats.dumper); + pthread_join(stats.dumper, NULL); + stats.active_dumper = false; + } +} + +void stats_deinit(void) +{ + if (stats.active_dumper) { + pthread_cancel(stats.dumper); + pthread_join(stats.dumper, NULL); + } + + memset(&stats, 0, sizeof(stats)); +} diff --git a/src/knot/common/stats.h b/src/knot/common/stats.h new file mode 100644 index 0000000..3ce73c6 --- /dev/null +++ b/src/knot/common/stats.h @@ -0,0 +1,47 @@ +/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \brief Server statistics general API. + */ + +#pragma once + +#include "knot/server/server.h" + +typedef uint64_t (*stats_val_f)(server_t *server); + +/*! + * \brief Statistics metrics item. + */ +typedef struct { + const char *name; /*!< Metrics name. */ + stats_val_f val; /*!< Metrics value getter. */ +} stats_item_t; + +/*! + * \brief Basic server metrics. + */ +extern const stats_item_t server_stats[]; + +/*! + * \brief Reconfigures the statistics facility. + */ +void stats_reconfigure(conf_t *conf, server_t *server); + +/*! + * \brief Deinitializes the statistics facility. + */ +void stats_deinit(void); diff --git a/src/knot/conf/base.c b/src/knot/conf/base.c new file mode 100644 index 0000000..7fd8ba9 --- /dev/null +++ b/src/knot/conf/base.c @@ -0,0 +1,924 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <string.h> +#include <urcu.h> + +#include "knot/conf/base.h" +#include "knot/conf/confdb.h" +#include "knot/conf/module.h" +#include "knot/conf/tools.h" +#include "knot/common/log.h" +#include "knot/nameserver/query_module.h" +#include "libknot/libknot.h" +#include "libknot/yparser/ypformat.h" +#include "libknot/yparser/yptrafo.h" +#include "contrib/files.h" +#include "contrib/sockaddr.h" +#include "contrib/string.h" + +// The active configuration. +conf_t *s_conf; + +conf_t* conf(void) { + return s_conf; +} + +static int init_and_check( + conf_t *conf, + conf_flag_t flags) +{ + if (conf == NULL) { + return KNOT_EINVAL; + } + + knot_db_txn_t txn; + unsigned txn_flags = (flags & CONF_FREADONLY) ? KNOT_DB_RDONLY : 0; + int ret = conf->api->txn_begin(conf->db, &txn, txn_flags); + if (ret != KNOT_EOK) { + return ret; + } + + // Initialize the database. + if (!(flags & CONF_FREADONLY)) { + ret = conf_db_init(conf, &txn, false); + if (ret != KNOT_EOK) { + conf->api->txn_abort(&txn); + return ret; + } + } + + // Check the database. + if (!(flags & CONF_FNOCHECK)) { + ret = conf_db_check(conf, &txn); + if (ret < KNOT_EOK) { + conf->api->txn_abort(&txn); + return ret; + } + } + + if (flags & CONF_FREADONLY) { + conf->api->txn_abort(&txn); + return KNOT_EOK; + } else { + return conf->api->txn_commit(&txn); + } +} + +int conf_refresh_txn( + conf_t *conf) +{ + if (conf == NULL) { + return KNOT_EINVAL; + } + + // Close previously opened transaction. + conf->api->txn_abort(&conf->read_txn); + + return conf->api->txn_begin(conf->db, &conf->read_txn, KNOT_DB_RDONLY); +} + +void conf_refresh_hostname( + conf_t *conf) +{ + if (conf == NULL) { + return; + } + + free(conf->hostname); + conf->hostname = sockaddr_hostname(); + if (conf->hostname == NULL) { + // Empty hostname fallback, NULL cannot be passed to strlen! + conf->hostname = strdup(""); + } +} + +static void init_cache( + conf_t *conf) +{ + conf_val_t val = conf_get(conf, C_SRV, C_MAX_IPV4_UDP_PAYLOAD); + if (val.code != KNOT_EOK) { + val = conf_get(conf, C_SRV, C_MAX_UDP_PAYLOAD); + } + conf->cache.srv_max_ipv4_udp_payload = conf_int(&val); + + val = conf_get(conf, C_SRV, C_MAX_IPV6_UDP_PAYLOAD); + if (val.code != KNOT_EOK) { + val = conf_get(conf, C_SRV, C_MAX_UDP_PAYLOAD); + } + conf->cache.srv_max_ipv6_udp_payload = conf_int(&val); + + val = conf_get(conf, C_SRV, C_TCP_HSHAKE_TIMEOUT); + conf->cache.srv_tcp_hshake_timeout = conf_int(&val); + + val = conf_get(conf, C_SRV, C_TCP_IDLE_TIMEOUT); + conf->cache.srv_tcp_idle_timeout = conf_int(&val); + + val = conf_get(conf, C_SRV, C_TCP_REPLY_TIMEOUT); + conf->cache.srv_tcp_reply_timeout = conf_int(&val); + + val = conf_get(conf, C_SRV, C_MAX_TCP_CLIENTS); + conf->cache.srv_max_tcp_clients = conf_int(&val); + + val = conf_get(conf, C_CTL, C_TIMEOUT); + conf->cache.ctl_timeout = conf_int(&val) * 1000; + + conf->cache.srv_nsid = conf_get(conf, C_SRV, C_NSID); + + val = conf_get(conf, C_SRV, C_ECS); + conf->cache.use_ecs = conf_bool(&val); + + val = conf_get(conf, C_SRV, C_ANS_ROTATION); + conf->cache.srv_ans_rotate = conf_bool(&val); +} + +int conf_new( + conf_t **conf, + const yp_item_t *schema, + const char *db_dir, + size_t max_conf_size, + conf_flag_t flags) +{ + if (conf == NULL) { + return KNOT_EINVAL; + } + + conf_t *out = malloc(sizeof(conf_t)); + if (out == NULL) { + return KNOT_ENOMEM; + } + memset(out, 0, sizeof(conf_t)); + + // Initialize config schema. + int ret = yp_schema_copy(&out->schema, schema); + if (ret != KNOT_EOK) { + goto new_error; + } + + // Initialize query modules list. + out->query_modules = malloc(sizeof(list_t)); + if (out->query_modules == NULL) { + ret = KNOT_ENOMEM; + goto new_error; + } + init_list(out->query_modules); + + // Set the DB api. + out->api = knot_db_lmdb_api(); + struct knot_db_lmdb_opts lmdb_opts = KNOT_DB_LMDB_OPTS_INITIALIZER; + lmdb_opts.mapsize = max_conf_size; + lmdb_opts.maxreaders = CONF_MAX_DB_READERS; + lmdb_opts.flags.env = KNOT_DB_LMDB_NOTLS; + + // Open the database. + if (db_dir == NULL) { + // Prepare a temporary database. + char tpl[] = "/tmp/knot-confdb.XXXXXX"; + lmdb_opts.path = mkdtemp(tpl); + if (lmdb_opts.path == NULL) { + CONF_LOG(LOG_ERR, "failed to create temporary directory (%s)", + knot_strerror(knot_map_errno())); + ret = KNOT_ENOMEM; + goto new_error; + } + + ret = out->api->init(&out->db, NULL, &lmdb_opts); + + // Remove the database to ensure it is temporary. + if (!remove_path(lmdb_opts.path)) { + CONF_LOG(LOG_WARNING, "failed to purge temporary directory '%s'", + lmdb_opts.path); + } + } else { + // Set the specified database. + lmdb_opts.path = db_dir; + + // Set the read-only mode. + if (flags & CONF_FREADONLY) { + lmdb_opts.flags.env |= KNOT_DB_LMDB_RDONLY; + } + + ret = out->api->init(&out->db, NULL, &lmdb_opts); + } + if (ret != KNOT_EOK) { + goto new_error; + } + + // Initialize and check the database. + ret = init_and_check(out, flags); + if (ret != KNOT_EOK) { + goto new_error; + } + + // Open common read-only transaction. + ret = conf_refresh_txn(out); + if (ret != KNOT_EOK) { + goto new_error; + } + + // Cache the current hostname. + if (!(flags & CONF_FNOHOSTNAME)) { + conf_refresh_hostname(out); + } + + // Initialize cached values. + init_cache(out); + + // Load module schemas. + if (flags & (CONF_FREQMODULES | CONF_FOPTMODULES)) { + ret = conf_mod_load_common(out); + if (ret != KNOT_EOK && (flags & CONF_FREQMODULES)) { + goto new_error; + } + + for (conf_iter_t iter = conf_iter(out, C_MODULE); + iter.code == KNOT_EOK; conf_iter_next(out, &iter)) { + conf_val_t id = conf_iter_id(out, &iter); + conf_val_t file = conf_id_get(out, C_MODULE, C_FILE, &id); + ret = conf_mod_load_extra(out, conf_str(&id), conf_str(&file), false); + if (ret != KNOT_EOK && (flags & CONF_FREQMODULES)) { + conf_iter_finish(out, &iter); + goto new_error; + } + } + + conf_mod_load_purge(out, false); + } + + *conf = out; + + return KNOT_EOK; +new_error: + conf_free(out); + + return ret; +} + +int conf_clone( + conf_t **conf) +{ + if (conf == NULL || s_conf == NULL) { + return KNOT_EINVAL; + } + + conf_t *out = malloc(sizeof(conf_t)); + if (out == NULL) { + return KNOT_ENOMEM; + } + memset(out, 0, sizeof(conf_t)); + + // Initialize config schema. + int ret = yp_schema_copy(&out->schema, s_conf->schema); + if (ret != KNOT_EOK) { + free(out); + return ret; + } + + // Set shared items. + out->api = s_conf->api; + out->db = s_conf->db; + + // Initialize query modules list. + out->query_modules = malloc(sizeof(list_t)); + if (out->query_modules == NULL) { + yp_schema_free(out->schema); + free(out); + return KNOT_ENOMEM; + } + init_list(out->query_modules); + + // Open common read-only transaction. + ret = conf_refresh_txn(out); + if (ret != KNOT_EOK) { + free(out->query_modules); + yp_schema_free(out->schema); + free(out); + return ret; + } + + // Copy the filename. + if (s_conf->filename != NULL) { + out->filename = strdup(s_conf->filename); + } + + // Copy the hostname. + if (s_conf->hostname != NULL) { + out->hostname = strdup(s_conf->hostname); + } + + // Initialize cached values. + init_cache(out); + + out->is_clone = true; + + *conf = out; + + return KNOT_EOK; +} + +conf_t *conf_update( + conf_t *conf, + conf_update_flag_t flags) +{ + // Remove the clone flag for new master configuration. + if (conf != NULL) { + conf->is_clone = false; + + if ((flags & CONF_UPD_FCONFIO) && s_conf != NULL) { + conf->io.flags = s_conf->io.flags; + conf->io.zones = s_conf->io.zones; + } + if ((flags & CONF_UPD_FMODULES) && s_conf != NULL) { + free(conf->query_modules); + conf->query_modules = s_conf->query_modules; + conf->query_plan = s_conf->query_plan; + } + } + + conf_t **current_conf = &s_conf; + conf_t *old_conf = rcu_xchg_pointer(current_conf, conf); + + synchronize_rcu(); + + if (old_conf != NULL) { + // Remove the clone flag if a single configuration. + old_conf->is_clone = (conf != NULL) ? true : false; + + if (flags & CONF_UPD_FCONFIO) { + old_conf->io.zones = NULL; + } + if (flags & CONF_UPD_FMODULES) { + old_conf->query_modules = NULL; + old_conf->query_plan = NULL; + } + if (!(flags & CONF_UPD_FNOFREE)) { + conf_free(old_conf); + old_conf = NULL; + } + } + + return old_conf; +} + +void conf_free( + conf_t *conf) +{ + if (conf == NULL) { + return; + } + + yp_schema_free(conf->schema); + free(conf->filename); + free(conf->hostname); + if (conf->api != NULL) { + conf->api->txn_abort(&conf->read_txn); + } + + if (conf->io.txn != NULL && conf->api != NULL) { + conf->api->txn_abort(conf->io.txn_stack); + } + if (conf->io.zones != NULL) { + trie_free(conf->io.zones); + } + + conf_mod_load_purge(conf, false); + conf_deactivate_modules(conf->query_modules, &conf->query_plan); + free(conf->query_modules); + conf_mod_unload_shared(conf); + + if (!conf->is_clone) { + if (conf->api != NULL) { + conf->api->deinit(conf->db); + } + } + + free(conf); +} + +#define CONF_LOG_LINE(file, line, msg, ...) do { \ + CONF_LOG(LOG_ERR, "%s%s%sline %zu, " msg, \ + (file != NULL ? "file '" : ""), (file != NULL ? file : ""), \ + (file != NULL ? "', " : ""), line, ##__VA_ARGS__); \ + } while (0) + +static void log_parser_err( + yp_parser_t *parser, + int ret) +{ + CONF_LOG_LINE(parser->file.name, parser->line_count, + "item '%s'%s%s%s (%s)", + parser->key, + (parser->data_len > 0) ? ", value '" : "", + (parser->data_len > 0) ? parser->data : "", + (parser->data_len > 0) ? "'" : "", + knot_strerror(ret)); +} + +static void log_parser_schema_err( + yp_parser_t *parser, + int ret) +{ + // Emit better message for 'unknown module' error. + if (ret == KNOT_YP_EINVAL_ITEM && parser->event == YP_EKEY0 && + strncmp(parser->key, KNOTD_MOD_NAME_PREFIX, strlen(KNOTD_MOD_NAME_PREFIX)) == 0) { + CONF_LOG_LINE(parser->file.name, parser->line_count, + "unknown module '%s'", parser->key); + } else { + log_parser_err(parser, ret); + } +} + +static void log_call_err( + yp_parser_t *parser, + knotd_conf_check_args_t *args, + int ret) +{ + CONF_LOG_LINE(args->extra->file_name, args->extra->line, + "item '%s'%s%s%s (%s)", args->item->name + 1, + (parser->data_len > 0) ? ", value '" : "", + (parser->data_len > 0) ? parser->data : "", + (parser->data_len > 0) ? "'" : "", + (args->err_str != NULL) ? args->err_str : knot_strerror(ret)); +} + +static void log_prev_err( + knotd_conf_check_args_t *args, + int ret) +{ + char buff[512] = { 0 }; + size_t len = sizeof(buff); + + // Get the previous textual identifier. + if ((args->item->flags & YP_FMULTI) != 0) { + if (yp_item_to_txt(args->item->var.g.id, args->id, args->id_len, + buff, &len, YP_SNOQUOTE) != KNOT_EOK) { + buff[0] = '\0'; + } + } + + CONF_LOG_LINE(args->extra->file_name, args->extra->line, + "%s '%s' (%s)", args->item->name + 1, buff, + args->err_str != NULL ? args->err_str : knot_strerror(ret)); +} + +static int finalize_previous_section( + conf_t *conf, + knot_db_txn_t *txn, + yp_parser_t *parser, + yp_check_ctx_t *ctx) +{ + yp_node_t *node = &ctx->nodes[0]; + + // Return if no previous section or include or empty multi-section. + if (node->item == NULL || node->item->type != YP_TGRP || + (node->id_len == 0 && (node->item->flags & YP_FMULTI) != 0)) { + return KNOT_EOK; + } + + knotd_conf_check_extra_t extra = { + .conf = conf, + .txn = txn, + .file_name = parser->file.name, + .line = parser->line_count + }; + knotd_conf_check_args_t args = { + .item = node->item, + .id = node->id, + .id_len = node->id_len, + .data = node->data, + .data_len = node->data_len, + .extra = &extra + }; + + int ret = conf_exec_callbacks(&args); + if (ret != KNOT_EOK) { + log_prev_err(&args, ret); + } + + return ret; +} + +static int finalize_item( + conf_t *conf, + knot_db_txn_t *txn, + yp_parser_t *parser, + yp_check_ctx_t *ctx) +{ + yp_node_t *node = &ctx->nodes[ctx->current]; + + // Section callbacks are executed before another section. + if (node->item->type == YP_TGRP && node->id_len == 0) { + return KNOT_EOK; + } + + knotd_conf_check_extra_t extra = { + .conf = conf, + .txn = txn, + .file_name = parser->file.name, + .line = parser->line_count + }; + knotd_conf_check_args_t args = { + .item = (parser->event == YP_EID) ? node->item->var.g.id : node->item, + .id = node->id, + .id_len = node->id_len, + .data = node->data, + .data_len = node->data_len, + .extra = &extra + }; + + int ret = conf_exec_callbacks(&args); + if (ret != KNOT_EOK) { + log_call_err(parser, &args, ret); + } + + return ret; +} + +int conf_parse( + conf_t *conf, + knot_db_txn_t *txn, + const char *input, + bool is_file) +{ + if (conf == NULL || txn == NULL || input == NULL) { + return KNOT_EINVAL; + } + + yp_parser_t *parser = malloc(sizeof(yp_parser_t)); + if (parser == NULL) { + return KNOT_ENOMEM; + } + yp_init(parser); + + int ret; + + // Set parser source. + if (is_file) { + ret = yp_set_input_file(parser, input); + } else { + ret = yp_set_input_string(parser, input, strlen(input)); + } + if (ret != KNOT_EOK) { + CONF_LOG(LOG_ERR, "failed to load file '%s' (%s)", + input, knot_strerror(ret)); + goto parse_error; + } + + // Initialize parser check context. + yp_check_ctx_t *ctx = yp_schema_check_init(&conf->schema); + if (ctx == NULL) { + ret = KNOT_ENOMEM; + goto parse_error; + } + + int check_ret = KNOT_EOK; + + // Parse the configuration. + while ((ret = yp_parse(parser)) == KNOT_EOK) { + if (parser->event == YP_EKEY0 || parser->event == YP_EID) { + check_ret = finalize_previous_section(conf, txn, parser, ctx); + if (check_ret != KNOT_EOK) { + break; + } + } + + check_ret = yp_schema_check_parser(ctx, parser); + if (check_ret != KNOT_EOK) { + log_parser_schema_err(parser, check_ret); + break; + } + + yp_node_t *node = &ctx->nodes[ctx->current]; + yp_node_t *parent = node->parent; + + if (parent == NULL) { + check_ret = conf_db_set(conf, txn, node->item->name, + NULL, node->id, node->id_len, + node->data, node->data_len); + } else { + check_ret = conf_db_set(conf, txn, parent->item->name, + node->item->name, parent->id, + parent->id_len, node->data, + node->data_len); + } + if (check_ret != KNOT_EOK) { + log_parser_err(parser, check_ret); + break; + } + + check_ret = finalize_item(conf, txn, parser, ctx); + if (check_ret != KNOT_EOK) { + break; + } + } + + if (ret == KNOT_EOF) { + ret = finalize_previous_section(conf, txn, parser, ctx); + } else if (ret != KNOT_EOK) { + log_parser_err(parser, ret); + } else { + ret = check_ret; + } + + yp_schema_check_deinit(ctx); +parse_error: + yp_deinit(parser); + free(parser); + + return ret; +} + +int conf_import( + conf_t *conf, + const char *input, + bool is_file) +{ + if (conf == NULL || input == NULL) { + return KNOT_EINVAL; + } + + int ret; + + knot_db_txn_t txn; + ret = conf->api->txn_begin(conf->db, &txn, 0); + if (ret != KNOT_EOK) { + goto import_error; + } + + // Initialize the DB. + ret = conf_db_init(conf, &txn, true); + if (ret != KNOT_EOK) { + conf->api->txn_abort(&txn); + goto import_error; + } + + // Parse and import given file. + ret = conf_parse(conf, &txn, input, is_file); + if (ret != KNOT_EOK) { + conf->api->txn_abort(&txn); + goto import_error; + } + // Load purge must be here as conf_parse may be called recursively! + conf_mod_load_purge(conf, false); + + // Commit new configuration. + ret = conf->api->txn_commit(&txn); + if (ret != KNOT_EOK) { + goto import_error; + } + + // Update read-only transaction. + ret = conf_refresh_txn(conf); + if (ret != KNOT_EOK) { + goto import_error; + } + + // Update cached values. + init_cache(conf); + + // Reset the filename. + free(conf->filename); + conf->filename = NULL; + if (is_file) { + conf->filename = strdup(input); + } + + ret = KNOT_EOK; +import_error: + + return ret; +} + +static int export_group_name( + FILE *fp, + const yp_item_t *group, + char *out, + size_t out_len, + yp_style_t style) +{ + int ret = yp_format_key0(group, NULL, 0, out, out_len, style, true, true); + if (ret != KNOT_EOK) { + return ret; + } + + fprintf(fp, "%s", out); + + return KNOT_EOK; +} + +static int export_group( + conf_t *conf, + FILE *fp, + const yp_item_t *group, + const uint8_t *id, + size_t id_len, + char *out, + size_t out_len, + yp_style_t style, + bool *exported) +{ + // Export the multi-group name. + if ((group->flags & YP_FMULTI) != 0 && !(*exported)) { + int ret = export_group_name(fp, group, out, out_len, style); + if (ret != KNOT_EOK) { + return ret; + } + *exported = true; + } + + // Iterate through all possible group items. + for (yp_item_t *item = group->sub_items; item->name != NULL; item++) { + // Export the identifier. + if (group->var.g.id == item && (group->flags & YP_FMULTI) != 0) { + int ret = yp_format_id(group->var.g.id, id, id_len, out, + out_len, style); + if (ret != KNOT_EOK) { + return ret; + } + fprintf(fp, "%s", out); + continue; + } + + conf_val_t bin; + conf_db_get(conf, &conf->read_txn, group->name, item->name, + id, id_len, &bin); + if (bin.code == KNOT_ENOENT) { + continue; + } else if (bin.code != KNOT_EOK) { + return bin.code; + } + + // Export the single-group name if an item is set. + if ((group->flags & YP_FMULTI) == 0 && !(*exported)) { + int ret = export_group_name(fp, group, out, out_len, style); + if (ret != KNOT_EOK) { + return ret; + } + *exported = true; + } + + // Format single/multiple-valued item. + size_t values = conf_val_count(&bin); + for (size_t i = 1; i <= values; i++) { + conf_val(&bin); + int ret = yp_format_key1(item, bin.data, bin.len, out, + out_len, style, i == 1, + i == values); + if (ret != KNOT_EOK) { + return ret; + } + fprintf(fp, "%s", out); + + if (values > 1) { + conf_val_next(&bin); + } + } + } + + if (*exported) { + fprintf(fp, "\n"); + } + + return KNOT_EOK; +} + +static int export_item( + conf_t *conf, + FILE *fp, + const yp_item_t *item, + char *buff, + size_t buff_len, + yp_style_t style) +{ + bool exported = false; + + // Skip non-group items (include). + if (item->type != YP_TGRP) { + return KNOT_EOK; + } + + // Export simple group without identifiers. + if (!(item->flags & YP_FMULTI)) { + return export_group(conf, fp, item, NULL, 0, buff, buff_len, + style, &exported); + } + + // Iterate over all identifiers. + conf_iter_t iter; + int ret = conf_db_iter_begin(conf, &conf->read_txn, item->name, &iter); + switch (ret) { + case KNOT_EOK: + break; + case KNOT_ENOENT: + return KNOT_EOK; + default: + return ret; + } + + while (ret == KNOT_EOK) { + const uint8_t *id; + size_t id_len; + ret = conf_db_iter_id(conf, &iter, &id, &id_len); + if (ret != KNOT_EOK) { + conf_db_iter_finish(conf, &iter); + return ret; + } + + // Export group with identifiers. + ret = export_group(conf, fp, item, id, id_len, buff, buff_len, + style, &exported); + if (ret != KNOT_EOK) { + conf_db_iter_finish(conf, &iter); + return ret; + } + + ret = conf_db_iter_next(conf, &iter); + } + if (ret != KNOT_EOF) { + return ret; + } + + return KNOT_EOK; +} + +int conf_export( + conf_t *conf, + const char *file_name, + yp_style_t style) +{ + if (conf == NULL) { + return KNOT_EINVAL; + } + + // Prepare common buffer; + const size_t buff_len = 2 * CONF_MAX_DATA_LEN; // Rough limit. + char *buff = malloc(buff_len); + if (buff == NULL) { + return KNOT_ENOMEM; + } + + FILE *fp = (file_name != NULL) ? fopen(file_name, "w") : stdout; + if (fp == NULL) { + free(buff); + return knot_map_errno(); + } + + fprintf(fp, "# Configuration export (Knot DNS %s)\n\n", PACKAGE_VERSION); + + const char *mod_prefix = KNOTD_MOD_NAME_PREFIX; + const size_t mod_prefix_len = strlen(mod_prefix); + + int ret; + + // Iterate over the schema. + for (yp_item_t *item = conf->schema; item->name != NULL; item++) { + // Don't export module sections again. + if (strncmp(item->name + 1, mod_prefix, mod_prefix_len) == 0) { + break; + } + + // Export module sections before the template section. + if (strcmp(item->name + 1, C_TPL + 1) == 0) { + for (yp_item_t *mod = item + 1; mod->name != NULL; mod++) { + // Skip non-module sections. + if (strncmp(mod->name + 1, mod_prefix, mod_prefix_len) != 0) { + continue; + } + + // Export module section. + ret = export_item(conf, fp, mod, buff, buff_len, style); + if (ret != KNOT_EOK) { + goto export_error; + } + } + } + + // Export non-module section. + ret = export_item(conf, fp, item, buff, buff_len, style); + if (ret != KNOT_EOK) { + goto export_error; + } + } + + ret = KNOT_EOK; +export_error: + if (file_name != NULL) { + fclose(fp); + } + free(buff); + + return ret; +} diff --git a/src/knot/conf/base.h b/src/knot/conf/base.h new file mode 100644 index 0000000..b7ef61c --- /dev/null +++ b/src/knot/conf/base.h @@ -0,0 +1,279 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include "libknot/libknot.h" +#include "libknot/yparser/ypschema.h" +#include "contrib/qp-trie/trie.h" +#include "contrib/ucw/lists.h" +#include "contrib/dynarray.h" +#include "knot/include/module.h" + +/*! Default template identifier. */ +#define CONF_DEFAULT_ID ((uint8_t *)"\x08""default\0") +/*! Default configuration file. */ +#define CONF_DEFAULT_FILE (CONFIG_DIR "/knot.conf") +/*! Default configuration database. */ +#define CONF_DEFAULT_DBDIR (STORAGE_DIR "/confdb") +/*! Maximum depth of nested transactions. */ +#define CONF_MAX_TXN_DEPTH 5 +/*! Maximum number of concurrent DB readers. */ +#define CONF_MAX_DB_READERS 630 + +/*! Configuration specific logging. */ +#define CONF_LOG(severity, msg, ...) do { \ + log_fmt(severity, LOG_SOURCE_SERVER, "config, " msg, ##__VA_ARGS__); \ + } while (0) + +#define CONF_LOG_ZONE(severity, zone, msg, ...) do { \ + log_fmt_zone(severity, LOG_SOURCE_ZONE, zone, NULL, "config, " msg, ##__VA_ARGS__); \ + } while (0) + +/*! Configuration getter output. */ +typedef struct { + /*! Item description. */ + const yp_item_t *item; + /*! Whole data (can be array). */ + const uint8_t *blob; + /*! Whole data length. */ + size_t blob_len; + // Public items. + /*! Current single data. */ + const uint8_t *data; + /*! Current single data length. */ + size_t len; + /*! Value getter return code. */ + int code; +} conf_val_t; + +/*! Query module context. */ +typedef struct { + /*! Module interface. */ + const knotd_mod_api_t *api; + /*! Shared library dlopen handler. */ + void *lib_handle; + /*! Indication of a temporary module created during confio check. */ + bool temporary; +} module_t; + +dynarray_declare(mod, module_t *, DYNARRAY_VISIBILITY_PUBLIC, 16) +dynarray_declare(old_schema, yp_item_t *, DYNARRAY_VISIBILITY_PUBLIC, 16) + +/*! Configuration context. */ +typedef struct { + /*! Cloned configuration indicator. */ + bool is_clone; + /*! Currently used namedb api. */ + const struct knot_db_api *api; + /*! Configuration schema. */ + yp_item_t *schema; + /*! Configuration database. */ + knot_db_t *db; + + /*! Read-only transaction for config access. */ + knot_db_txn_t read_txn; + + struct { + /*! The current writing transaction. */ + knot_db_txn_t *txn; + /*! Stack of nested writing transactions. */ + knot_db_txn_t txn_stack[CONF_MAX_TXN_DEPTH]; + /*! Master transaction flags. */ + yp_flag_t flags; + /*! Changed zones. */ + trie_t *zones; + } io; + + /*! Current config file (for reload if started with config file). */ + char *filename; + + /*! Prearranged hostname string (for automatic NSID or CH ident value). */ + char *hostname; + + /*! Cached critical confdb items. */ + struct { + int16_t srv_max_ipv4_udp_payload; + int16_t srv_max_ipv6_udp_payload; + int32_t srv_tcp_hshake_timeout; + int32_t srv_tcp_idle_timeout; + int32_t srv_tcp_reply_timeout; + int32_t srv_max_tcp_clients; + int32_t ctl_timeout; + conf_val_t srv_nsid; + bool use_ecs; + bool srv_ans_rotate; + } cache; + + /*! List of dynamically loaded modules. */ + mod_dynarray_t modules; + /*! List of old schemas (lazy freed). */ + old_schema_dynarray_t old_schemas; + /*! List of active query modules. */ + list_t *query_modules; + /*! Default query modules plan. */ + struct query_plan *query_plan; +} conf_t; + +/*! + * Configuration access flags. + */ +typedef enum { + CONF_FNONE = 0, /*!< Empty flag. */ + CONF_FREADONLY = 1 << 0, /*!< Read only access. */ + CONF_FNOCHECK = 1 << 1, /*!< Disabled confdb check. */ + CONF_FNOHOSTNAME = 1 << 2, /*!< Don't set the hostname. */ + CONF_FREQMODULES = 1 << 3, /*!< Load module schemas (must succeed). */ + CONF_FOPTMODULES = 1 << 4, /*!< Load module schemas (may fail). */ +} conf_flag_t; + +/*! + * Configuration update flags. + */ +typedef enum { + CONF_UPD_FNONE = 0, /*!< Empty flag. */ + CONF_UPD_FNOFREE = 1 << 0, /*!< Disable auto-free of previous config. */ + CONF_UPD_FMODULES = 1 << 1, /*!< Reuse previous global modules. */ + CONF_UPD_FCONFIO = 1 << 2, /*!< Reuse previous confio reload context. */ +} conf_update_flag_t; + +/*! + * Returns the active configuration. + */ +conf_t* conf(void); + +/*! + * Refreshes common read-only transaction. + * + * \param[in] conf Configuration. + * + * \return Error code, KNOT_EOK if success. + */ +int conf_refresh_txn( + conf_t *conf +); + +/*! + * Refreshes cached hostname. + * + * \param[in] conf Configuration. + */ +void conf_refresh_hostname( + conf_t *conf +); + +/*! + * Creates new or opens old configuration database. + * + * \param[out] conf Configuration. + * \param[in] schema Configuration schema. + * \param[in] db_dir Database path or NULL. + * \param[in] max_conf_size Maximum configuration DB size in bytes (LMDB mapsize). + * \param[in] flags Access flags. + * + * \return Error code, KNOT_EOK if success. + */ +int conf_new( + conf_t **conf, + const yp_item_t *schema, + const char *db_dir, + size_t max_conf_size, + conf_flag_t flags +); + +/*! + * Creates a partial copy of the active configuration. + * + * Shared objects: api, mm, db, filename. + * + * \param[out] conf Configuration. + * + * \return Error code, KNOT_EOK if success. + */ +int conf_clone( + conf_t **conf +); + +/*! + * Replaces the active configuration with the specified one. + * + * \param[in] conf New configuration. + * \param[in] flags Update flags. + * + * \return Previous config if CONF_UPD_FNOFREE, else NULL. + */ +conf_t *conf_update( + conf_t *conf, + conf_update_flag_t flags +); + +/*! + * Removes the specified configuration. + * + * \param[in] conf Configuration. + */ +void conf_free( + conf_t *conf +); + +/*! + * Parses textual configuration from the string or from the file. + * + * This function is not for direct using, just for includes processing! + * + * \param[in] conf Configuration. + * \param[in] txn Transaction. + * \param[in] input Configuration string or filename. + * \param[in] is_file Specifies if the input is string or input filename. + * + * \return Error code, KNOT_EOK if success. + */ +int conf_parse( + conf_t *conf, + knot_db_txn_t *txn, + const char *input, + bool is_file +); + +/*! + * Imports textual configuration. + * + * \param[in] conf Configuration. + * \param[in] input Configuration string or input filename. + * \param[in] is_file Specifies if the input is string or filename. + * + * \return Error code, KNOT_EOK if success. + */ +int conf_import( + conf_t *conf, + const char *input, + bool is_file +); + +/*! + * Exports configuration to textual file. + * + * \param[in] conf Configuration. + * \param[in] file_name Output filename (stdout is used if NULL). + * \param[in] style Formatting style. + * + * \return Error code, KNOT_EOK if success. + */ +int conf_export( + conf_t *conf, + const char *file_name, + yp_style_t style +); diff --git a/src/knot/conf/conf.c b/src/knot/conf/conf.c new file mode 100644 index 0000000..8bdac68 --- /dev/null +++ b/src/knot/conf/conf.c @@ -0,0 +1,1216 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <assert.h> +#include <grp.h> +#include <pwd.h> +#include <stdio.h> + +#include "knot/conf/base.h" +#include "knot/conf/confdb.h" +#include "knot/common/log.h" +#include "knot/server/dthreads.h" +#include "libknot/libknot.h" +#include "libknot/yparser/yptrafo.h" +#include "contrib/macros.h" +#include "contrib/sockaddr.h" +#include "contrib/string.h" +#include "contrib/wire_ctx.h" +#include "contrib/openbsd/strlcat.h" + +#define DBG_LOG(err) CONF_LOG(LOG_DEBUG, "%s (%s)", __func__, knot_strerror((err))); + +conf_val_t conf_get_txn( + conf_t *conf, + knot_db_txn_t *txn, + const yp_name_t *key0_name, + const yp_name_t *key1_name) +{ + conf_val_t val = { NULL }; + + if (key0_name == NULL || key1_name == NULL) { + val.code = KNOT_EINVAL; + DBG_LOG(val.code); + return val; + } + + conf_db_get(conf, txn, key0_name, key1_name, NULL, 0, &val); + switch (val.code) { + default: + CONF_LOG(LOG_ERR, "failed to read '%s/%s' (%s)", + key0_name + 1, key1_name + 1, knot_strerror(val.code)); + // FALLTHROUGH + case KNOT_EOK: + case KNOT_ENOENT: + return val; + } +} + +conf_val_t conf_rawid_get_txn( + conf_t *conf, + knot_db_txn_t *txn, + const yp_name_t *key0_name, + const yp_name_t *key1_name, + const uint8_t *id, + size_t id_len) +{ + conf_val_t val = { NULL }; + + if (key0_name == NULL || key1_name == NULL || id == NULL) { + val.code = KNOT_EINVAL; + DBG_LOG(val.code); + return val; + } + + conf_db_get(conf, txn, key0_name, key1_name, id, id_len, &val); + switch (val.code) { + default: + CONF_LOG(LOG_ERR, "failed to read '%s/%s' with identifier (%s)", + key0_name + 1, key1_name + 1, knot_strerror(val.code)); + // FALLTHROUGH + case KNOT_EOK: + case KNOT_ENOENT: + case KNOT_YP_EINVAL_ID: + return val; + } +} + +conf_val_t conf_id_get_txn( + conf_t *conf, + knot_db_txn_t *txn, + const yp_name_t *key0_name, + const yp_name_t *key1_name, + conf_val_t *id) +{ + conf_val_t val = { NULL }; + + if (key0_name == NULL || key1_name == NULL || id == NULL || + id->code != KNOT_EOK) { + val.code = KNOT_EINVAL; + DBG_LOG(val.code); + return val; + } + + conf_val(id); + + conf_db_get(conf, txn, key0_name, key1_name, id->data, id->len, &val); + switch (val.code) { + default: + CONF_LOG(LOG_ERR, "failed to read '%s/%s' with identifier (%s)", + key0_name + 1, key1_name + 1, knot_strerror(val.code)); + // FALLTHROUGH + case KNOT_EOK: + case KNOT_ENOENT: + case KNOT_YP_EINVAL_ID: + return val; + } +} + +conf_val_t conf_mod_get_txn( + conf_t *conf, + knot_db_txn_t *txn, + const yp_name_t *key1_name, + const conf_mod_id_t *mod_id) +{ + conf_val_t val = { NULL }; + + if (key1_name == NULL || mod_id == NULL) { + val.code = KNOT_EINVAL; + DBG_LOG(val.code); + return val; + } + + conf_db_get(conf, txn, mod_id->name, key1_name, mod_id->data, mod_id->len, + &val); + switch (val.code) { + default: + CONF_LOG(LOG_ERR, "failed to read '%s/%s' (%s)", + mod_id->name + 1, key1_name + 1, knot_strerror(val.code)); + // FALLTHROUGH + case KNOT_EOK: + case KNOT_ENOENT: + case KNOT_YP_EINVAL_ID: + return val; + } +} + +conf_val_t conf_zone_get_txn( + conf_t *conf, + knot_db_txn_t *txn, + const yp_name_t *key1_name, + const knot_dname_t *dname) +{ + conf_val_t val = { NULL }; + + if (key1_name == NULL || dname == NULL) { + val.code = KNOT_EINVAL; + DBG_LOG(val.code); + return val; + } + + size_t dname_size = knot_dname_size(dname); + + // Try to get explicit value. + conf_db_get(conf, txn, C_ZONE, key1_name, dname, dname_size, &val); + switch (val.code) { + case KNOT_EOK: + return val; + default: + CONF_LOG_ZONE(LOG_ERR, dname, "failed to read '%s/%s' (%s)", + C_ZONE + 1, key1_name + 1, knot_strerror(val.code)); + // FALLTHROUGH + case KNOT_ENOENT: + break; + } + + // Check if a template is available. + conf_db_get(conf, txn, C_ZONE, C_TPL, dname, dname_size, &val); + switch (val.code) { + case KNOT_EOK: + // Use the specified template. + conf_val(&val); + conf_db_get(conf, txn, C_TPL, key1_name, val.data, val.len, &val); + break; + default: + CONF_LOG_ZONE(LOG_ERR, dname, "failed to read '%s/%s' (%s)", + C_ZONE + 1, C_TPL + 1, knot_strerror(val.code)); + // FALLTHROUGH + case KNOT_ENOENT: + case KNOT_YP_EINVAL_ID: + // Use the default template. + conf_db_get(conf, txn, C_TPL, key1_name, CONF_DEFAULT_ID + 1, + CONF_DEFAULT_ID[0], &val); + } + + switch (val.code) { + default: + CONF_LOG_ZONE(LOG_ERR, dname, "failed to read '%s/%s' (%s)", + C_TPL + 1, key1_name + 1, knot_strerror(val.code)); + // FALLTHROUGH + case KNOT_EOK: + case KNOT_ENOENT: + case KNOT_YP_EINVAL_ID: + break; + } + + return val; +} + +conf_val_t conf_default_get_txn( + conf_t *conf, + knot_db_txn_t *txn, + const yp_name_t *key1_name) +{ + conf_val_t val = { NULL }; + + if (key1_name == NULL) { + val.code = KNOT_EINVAL; + DBG_LOG(val.code); + return val; + } + + conf_db_get(conf, txn, C_TPL, key1_name, CONF_DEFAULT_ID + 1, + CONF_DEFAULT_ID[0], &val); + switch (val.code) { + default: + CONF_LOG(LOG_ERR, "failed to read default '%s/%s' (%s)", + C_TPL + 1, key1_name + 1, knot_strerror(val.code)); + // FALLTHROUGH + case KNOT_EOK: + case KNOT_ENOENT: + case KNOT_YP_EINVAL_ID: + break; + } + + return val; +} + +bool conf_rawid_exists_txn( + conf_t *conf, + knot_db_txn_t *txn, + const yp_name_t *key0_name, + const uint8_t *id, + size_t id_len) +{ + if (key0_name == NULL || id == NULL) { + DBG_LOG(KNOT_EINVAL); + return false; + } + + int ret = conf_db_get(conf, txn, key0_name, NULL, id, id_len, NULL); + switch (ret) { + case KNOT_EOK: + return true; + default: + CONF_LOG(LOG_ERR, "failed to check '%s' for identifier (%s)", + key0_name + 1, knot_strerror(ret)); + // FALLTHROUGH + case KNOT_ENOENT: + case KNOT_YP_EINVAL_ID: + return false; + } +} + +bool conf_id_exists_txn( + conf_t *conf, + knot_db_txn_t *txn, + const yp_name_t *key0_name, + conf_val_t *id) +{ + if (key0_name == NULL || id == NULL || id->code != KNOT_EOK) { + DBG_LOG(KNOT_EINVAL); + return false; + } + + conf_val(id); + + int ret = conf_db_get(conf, txn, key0_name, NULL, id->data, id->len, NULL); + switch (ret) { + case KNOT_EOK: + return true; + default: + CONF_LOG(LOG_ERR, "failed to check '%s' for identifier (%s)", + key0_name + 1, knot_strerror(ret)); + // FALLTHROUGH + case KNOT_ENOENT: + case KNOT_YP_EINVAL_ID: + return false; + } +} + +size_t conf_id_count_txn( + conf_t *conf, + knot_db_txn_t *txn, + const yp_name_t *key0_name) +{ + size_t count = 0; + + for (conf_iter_t iter = conf_iter_txn(conf, txn, key0_name); + iter.code == KNOT_EOK; conf_iter_next(conf, &iter)) { + count++; + } + + return count; +} + +conf_iter_t conf_iter_txn( + conf_t *conf, + knot_db_txn_t *txn, + const yp_name_t *key0_name) +{ + conf_iter_t iter = { NULL }; + + (void)conf_db_iter_begin(conf, txn, key0_name, &iter); + switch (iter.code) { + default: + CONF_LOG(LOG_ERR, "failed to iterate through '%s' (%s)", + key0_name + 1, knot_strerror(iter.code)); + // FALLTHROUGH + case KNOT_EOK: + case KNOT_ENOENT: + return iter; + } +} + +void conf_iter_next( + conf_t *conf, + conf_iter_t *iter) +{ + (void)conf_db_iter_next(conf, iter); + switch (iter->code) { + default: + CONF_LOG(LOG_ERR, "failed to read next item (%s)", + knot_strerror(iter->code)); + // FALLTHROUGH + case KNOT_EOK: + case KNOT_EOF: + return; + } +} + +conf_val_t conf_iter_id( + conf_t *conf, + conf_iter_t *iter) +{ + conf_val_t val = { NULL }; + + val.code = conf_db_iter_id(conf, iter, &val.blob, &val.blob_len); + switch (val.code) { + default: + CONF_LOG(LOG_ERR, "failed to read identifier (%s)", + knot_strerror(val.code)); + // FALLTHROUGH + case KNOT_EOK: + val.item = iter->item; + return val; + } +} + +void conf_iter_finish( + conf_t *conf, + conf_iter_t *iter) +{ + conf_db_iter_finish(conf, iter); +} + +size_t conf_val_count( + conf_val_t *val) +{ + if (val == NULL || val->code != KNOT_EOK) { + return 0; + } + + if (!(val->item->flags & YP_FMULTI)) { + return 1; + } + + size_t count = 0; + conf_val(val); + while (val->code == KNOT_EOK) { + count++; + conf_val_next(val); + } + if (val->code != KNOT_EOF) { + return 0; + } + + // Reset to the initial state. + conf_val(val); + + return count; +} + +void conf_val( + conf_val_t *val) +{ + assert(val != NULL); + assert(val->code == KNOT_EOK || val->code == KNOT_EOF); + + if (val->item->flags & YP_FMULTI) { + // Check if already called and not at the end. + if (val->data != NULL && val->code != KNOT_EOF) { + return; + } + + assert(val->blob != NULL); + wire_ctx_t ctx = wire_ctx_init_const(val->blob, val->blob_len); + uint16_t len = wire_ctx_read_u16(&ctx); + assert(ctx.error == KNOT_EOK); + + val->data = ctx.position; + val->len = len; + val->code = KNOT_EOK; + } else { + // Check for empty data. + if (val->blob_len == 0) { + val->data = NULL; + val->len = 0; + val->code = KNOT_EOK; + return; + } else { + assert(val->blob != NULL); + val->data = val->blob; + val->len = val->blob_len; + val->code = KNOT_EOK; + } + } +} + +void conf_val_next( + conf_val_t *val) +{ + assert(val != NULL); + assert(val->code == KNOT_EOK); + assert(val->item->flags & YP_FMULTI); + + // Check for the 'zero' call. + if (val->data == NULL) { + conf_val(val); + return; + } + + if (val->data + val->len < val->blob + val->blob_len) { + wire_ctx_t ctx = wire_ctx_init_const(val->blob, val->blob_len); + size_t offset = val->data + val->len - val->blob; + wire_ctx_skip(&ctx, offset); + uint16_t len = wire_ctx_read_u16(&ctx); + assert(ctx.error == KNOT_EOK); + + val->data = ctx.position; + val->len = len; + val->code = KNOT_EOK; + } else { + val->data = NULL; + val->len = 0; + val->code = KNOT_EOF; + } +} + +bool conf_val_equal( + conf_val_t *val1, + conf_val_t *val2) +{ + if (val1->blob_len == val2->blob_len && + memcmp(val1->blob, val2->blob, val1->blob_len) == 0) { + return true; + } + + return false; +} + +int64_t conf_int( + conf_val_t *val) +{ + assert(val != NULL && val->item != NULL); + assert(val->item->type == YP_TINT || + (val->item->type == YP_TREF && + val->item->var.r.ref->var.g.id->type == YP_TINT)); + + if (val->code == KNOT_EOK) { + conf_val(val); + return yp_int(val->data); + } else { + return val->item->var.i.dflt; + } +} + +bool conf_bool( + conf_val_t *val) +{ + assert(val != NULL && val->item != NULL); + assert(val->item->type == YP_TBOOL || + (val->item->type == YP_TREF && + val->item->var.r.ref->var.g.id->type == YP_TBOOL)); + + if (val->code == KNOT_EOK) { + conf_val(val); + return yp_bool(val->data); + } else { + return val->item->var.b.dflt; + } +} + +unsigned conf_opt( + conf_val_t *val) +{ + assert(val != NULL && val->item != NULL); + assert(val->item->type == YP_TOPT || + (val->item->type == YP_TREF && + val->item->var.r.ref->var.g.id->type == YP_TOPT)); + + if (val->code == KNOT_EOK) { + conf_val(val); + return yp_opt(val->data); + } else { + return val->item->var.o.dflt; + } +} + +const char* conf_str( + conf_val_t *val) +{ + assert(val != NULL && val->item != NULL); + assert(val->item->type == YP_TSTR || + (val->item->type == YP_TREF && + val->item->var.r.ref->var.g.id->type == YP_TSTR)); + + if (val->code == KNOT_EOK) { + conf_val(val); + return yp_str(val->data); + } else { + return val->item->var.s.dflt; + } +} + +const knot_dname_t* conf_dname( + conf_val_t *val) +{ + assert(val != NULL && val->item != NULL); + assert(val->item->type == YP_TDNAME || + (val->item->type == YP_TREF && + val->item->var.r.ref->var.g.id->type == YP_TDNAME)); + + if (val->code == KNOT_EOK) { + conf_val(val); + return yp_dname(val->data); + } else { + return (const knot_dname_t *)val->item->var.d.dflt; + } +} + +const uint8_t* conf_bin( + conf_val_t *val, + size_t *len) +{ + assert(val != NULL && val->item != NULL && len != NULL); + assert(val->item->type == YP_THEX || val->item->type == YP_TB64 || + (val->item->type == YP_TREF && + (val->item->var.r.ref->var.g.id->type == YP_THEX || + val->item->var.r.ref->var.g.id->type == YP_TB64))); + + if (val->code == KNOT_EOK) { + conf_val(val); + *len = yp_bin_len(val->data); + return yp_bin(val->data); + } else { + *len = val->item->var.d.dflt_len; + return val->item->var.d.dflt; + } +} + +const uint8_t* conf_data( + conf_val_t *val, + size_t *len) +{ + assert(val != NULL && val->item != NULL); + assert(val->item->type == YP_TDATA || + (val->item->type == YP_TREF && + val->item->var.r.ref->var.g.id->type == YP_TDATA)); + + if (val->code == KNOT_EOK) { + conf_val(val); + *len = val->len; + return val->data; + } else { + *len = val->item->var.d.dflt_len; + return val->item->var.d.dflt; + } +} + +struct sockaddr_storage conf_addr( + conf_val_t *val, + const char *sock_base_dir) +{ + assert(val != NULL && val->item != NULL); + assert(val->item->type == YP_TADDR || + (val->item->type == YP_TREF && + val->item->var.r.ref->var.g.id->type == YP_TADDR)); + + struct sockaddr_storage out = { AF_UNSPEC }; + + if (val->code == KNOT_EOK) { + bool no_port; + conf_val(val); + out = yp_addr(val->data, &no_port); + + if (out.ss_family == AF_UNIX) { + // val->data[0] is socket type identifier! + if (val->data[1] != '/' && sock_base_dir != NULL) { + char *tmp = sprintf_alloc("%s/%s", sock_base_dir, + val->data + 1); + val->code = sockaddr_set(&out, AF_UNIX, tmp, 0); + free(tmp); + } + } else if (no_port) { + sockaddr_port_set((struct sockaddr *)&out, + val->item->var.a.dflt_port); + } + } else { + const char *dflt_socket = val->item->var.a.dflt_socket; + if (dflt_socket != NULL) { + if (dflt_socket[0] == '/' || sock_base_dir == NULL) { + val->code = sockaddr_set(&out, AF_UNIX, + dflt_socket, 0); + } else { + char *tmp = sprintf_alloc("%s/%s", sock_base_dir, + dflt_socket); + val->code = sockaddr_set(&out, AF_UNIX, tmp, 0); + free(tmp); + } + } + } + + return out; +} + +struct sockaddr_storage conf_addr_range( + conf_val_t *val, + struct sockaddr_storage *max_ss, + int *prefix_len) +{ + assert(val != NULL && val->item != NULL && max_ss != NULL && + prefix_len != NULL); + assert(val->item->type == YP_TNET || + (val->item->type == YP_TREF && + val->item->var.r.ref->var.g.id->type == YP_TNET)); + + struct sockaddr_storage out = { AF_UNSPEC }; + + if (val->code == KNOT_EOK) { + conf_val(val); + out = yp_addr_noport(val->data); + // addr_type, addr, format, formatted_data (port| addr| empty). + const uint8_t *format = val->data + sizeof(uint8_t) + + ((out.ss_family == AF_INET) ? + IPV4_PREFIXLEN / 8 : IPV6_PREFIXLEN / 8); + // See addr_range_to_bin. + switch (*format) { + case 1: + max_ss->ss_family = AF_UNSPEC; + *prefix_len = yp_int(format + sizeof(uint8_t)); + break; + case 2: + *max_ss = yp_addr_noport(format + sizeof(uint8_t)); + *prefix_len = -1; + break; + default: + max_ss->ss_family = AF_UNSPEC; + *prefix_len = -1; + break; + } + } else { + max_ss->ss_family = AF_UNSPEC; + *prefix_len = -1; + } + + return out; +} + +bool conf_addr_range_match( + conf_val_t *range, + const struct sockaddr_storage *addr) +{ + if (range == NULL || addr == NULL) { + return false; + } + + while (range->code == KNOT_EOK) { + int mask; + struct sockaddr_storage min, max; + + min = conf_addr_range(range, &max, &mask); + if (max.ss_family == AF_UNSPEC) { + if (sockaddr_net_match((struct sockaddr *)addr, + (struct sockaddr *)&min, mask)) { + return true; + } + } else { + if (sockaddr_range_match((struct sockaddr *)addr, + (struct sockaddr *)&min, + (struct sockaddr *)&max)) { + return true; + } + } + + conf_val_next(range); + } + + return false; +} + +char* conf_abs_path( + conf_val_t *val, + const char *base_dir) +{ + const char *path = conf_str(val); + if (path == NULL) { + return NULL; + } else if (path[0] == '/') { + return strdup(path); + } else { + char *abs_path; + if (base_dir == NULL) { + char *cwd = realpath("./", NULL); + abs_path = sprintf_alloc("%s/%s", cwd, path); + free(cwd); + } else { + abs_path = sprintf_alloc("%s/%s", base_dir, path); + } + return abs_path; + } +} + +conf_mod_id_t* conf_mod_id( + conf_val_t *val) +{ + assert(val != NULL && val->item != NULL); + assert(val->item->type == YP_TDATA || + (val->item->type == YP_TREF && + val->item->var.r.ref->var.g.id->type == YP_TDATA)); + + conf_mod_id_t *mod_id = NULL; + + if (val->code == KNOT_EOK) { + conf_val(val); + + mod_id = malloc(sizeof(conf_mod_id_t)); + if (mod_id == NULL) { + return NULL; + } + + // Set module name in yp_name_t format + add zero termination. + size_t name_len = 1 + val->data[0]; + mod_id->name = malloc(name_len + 1); + if (mod_id->name == NULL) { + free(mod_id); + return NULL; + } + memcpy(mod_id->name, val->data, name_len); + mod_id->name[name_len] = '\0'; + + // Set module identifier. + mod_id->len = val->len - name_len; + mod_id->data = malloc(mod_id->len); + if (mod_id->data == NULL) { + free(mod_id->name); + free(mod_id); + return NULL; + } + memcpy(mod_id->data, val->data + name_len, mod_id->len); + } + + return mod_id; +} + +void conf_free_mod_id( + conf_mod_id_t *mod_id) +{ + free(mod_id->name); + free(mod_id->data); + free(mod_id); +} + +static int get_index( + const char **start, + const char *end, + unsigned *index1, + unsigned *index2) +{ + char c, *p; + if (sscanf(*start, "[%u%c", index1, &c) != 2) { + return KNOT_EINVAL; + } + switch (c) { + case '-': + p = strchr(*start, '-') + 1; + if (end - p < 2 || index2 == NULL || + sscanf(p, "%u%c", index2, &c) != 2 || c != ']') { + return KNOT_EINVAL; + } + break; + case ']': + if (index2 != NULL) { + *index2 = *index1; + } + break; + default: + return KNOT_EINVAL; + } + + *start = strchr(*start, ']') + 1; + return ((*index1 < 256 && (index2 == NULL || *index2 < 256) + && end - *start >= 0 && (index2 == NULL || *index2 >= *index1)) + ? KNOT_EOK : KNOT_EINVAL); +} + +static void replace_slashes( + char *name, + bool remove_dot) +{ + // Replace possible slashes with underscores. + char *ch; + for (ch = name; *ch != '\0'; ch++) { + if (*ch == '/') { + *ch = '_'; + } + } + + // Remove trailing dot. + if (remove_dot && ch > name) { + assert(*(ch - 1) == '.'); + *(ch - 1) = '\0'; + } +} + +static int str_char( + const knot_dname_t *zone, + char *buff, + size_t buff_len, + unsigned index1, + unsigned index2) +{ + if (knot_dname_to_str(buff, zone, buff_len) == NULL) { + return KNOT_EINVAL; + } + + size_t zone_len = strlen(buff); + assert(zone_len > 0); + + // Get the block length. + size_t len = index2 - index1 + 1; + + // Check for out of scope block. + if (index1 >= zone_len) { + buff[0] = '\0'; + return KNOT_EOK; + } + // Check for partial block. + if (index2 >= zone_len) { + len = zone_len - index1; + } + + // Copy the block. + memmove(buff, buff + index1, len); + buff[len] = '\0'; + + // Replace possible slashes with underscores. + replace_slashes(buff, false); + + return KNOT_EOK; +} + +static int str_zone( + const knot_dname_t *zone, + char *buff, + size_t buff_len) +{ + if (knot_dname_to_str(buff, zone, buff_len) == NULL) { + return KNOT_EINVAL; + } + + // Replace possible slashes with underscores. + replace_slashes(buff, true); + + return KNOT_EOK; +} + +static int str_label( + const knot_dname_t *zone, + char *buff, + size_t buff_len, + size_t right_index) +{ + size_t labels = knot_dname_labels(zone, NULL); + + // Check for root label of the root zone. + if (labels == 0 && right_index == 0) { + return str_zone(zone, buff, buff_len); + // Check for labels error or for an exceeded index. + } else if (labels < 1 || labels <= right_index) { + buff[0] = '\0'; + return KNOT_EOK; + } + + // ~ Label length + label + root label. + knot_dname_t label[1 + KNOT_DNAME_MAXLABELLEN + 1]; + + // Compute the index from the left. + assert(labels > right_index); + size_t index = labels - right_index - 1; + + // Create a dname from the single label. + size_t prefix_len = knot_dname_prefixlen(zone, index, NULL); + size_t label_len = *(zone + prefix_len); + memcpy(label, zone + prefix_len, 1 + label_len); + label[1 + label_len] = '\0'; + + return str_zone(label, buff, buff_len); +} + +static char* get_filename( + conf_t *conf, + knot_db_txn_t *txn, + const knot_dname_t *zone, + const char *name) +{ + assert(name); + + const char *end = name + strlen(name); + char out[1024] = ""; + + do { + // Search for a formatter. + const char *pos = strchr(name, '%'); + + // If no formatter, copy the rest of the name. + if (pos == NULL) { + if (strlcat(out, name, sizeof(out)) >= sizeof(out)) { + CONF_LOG_ZONE(LOG_WARNING, zone, "too long zonefile name"); + return NULL; + } + break; + } + + // Copy constant block. + char *block = strndup(name, pos - name); + if (block == NULL || + strlcat(out, block, sizeof(out)) >= sizeof(out)) { + CONF_LOG_ZONE(LOG_WARNING, zone, "too long zonefile name"); + free(block); + return NULL; + } + free(block); + + // Move name pointer behind the formatter. + name = pos + 2; + + char buff[512] = ""; + unsigned idx1, idx2; + bool failed = false; + + const char type = *(pos + 1); + switch (type) { + case '%': + strlcat(buff, "%", sizeof(buff)); + break; + case 'c': + if (get_index(&name, end, &idx1, &idx2) != KNOT_EOK || + str_char(zone, buff, sizeof(buff), idx1, idx2) != KNOT_EOK) { + failed = true; + } + break; + case 'l': + if (get_index(&name, end, &idx1, NULL) != KNOT_EOK || + str_label(zone, buff, sizeof(buff), idx1) != KNOT_EOK) { + failed = true; + } + break; + case 's': + if (str_zone(zone, buff, sizeof(buff)) != KNOT_EOK) { + failed = true; + } + break; + case '\0': + CONF_LOG_ZONE(LOG_WARNING, zone, "ignoring missing " + "trailing zonefile formatter"); + continue; + default: + CONF_LOG_ZONE(LOG_WARNING, zone, "ignoring zonefile " + "formatter '%%%c'", type); + continue; + } + + if (failed) { + CONF_LOG_ZONE(LOG_WARNING, zone, "failed to process " + "zonefile formatter '%%%c'", type); + return NULL; + } + + if (strlcat(out, buff, sizeof(out)) >= sizeof(out)) { + CONF_LOG_ZONE(LOG_WARNING, zone, "too long zonefile name"); + return NULL; + } + } while (name < end); + + // Use storage prefix if not absolute path. + if (out[0] == '/') { + return strdup(out); + } else { + conf_val_t val = conf_zone_get_txn(conf, txn, C_STORAGE, zone); + char *storage = conf_abs_path(&val, NULL); + if (storage == NULL) { + return NULL; + } + char *abs = sprintf_alloc("%s/%s", storage, out); + free(storage); + return abs; + } +} + +char* conf_zonefile_txn( + conf_t *conf, + knot_db_txn_t *txn, + const knot_dname_t *zone) +{ + if (zone == NULL) { + return NULL; + } + + conf_val_t val = conf_zone_get_txn(conf, txn, C_FILE, zone); + const char *file = conf_str(&val); + + // Use default zonefile name pattern if not specified. + if (file == NULL) { + file = "%s.zone"; + } + + return get_filename(conf, txn, zone, file); +} + +char* conf_journalfile_txn( + conf_t *conf, + knot_db_txn_t *txn) +{ + conf_val_t val = conf_default_get_txn(conf, txn, C_STORAGE); + char *storage = conf_abs_path(&val, NULL); + val = conf_default_get_txn(conf, txn, C_JOURNAL_DB); + char *journaldir = conf_abs_path(&val, storage); + free(storage); + + return journaldir; +} + +char* conf_kaspdir_txn( + conf_t *conf, + knot_db_txn_t *txn) +{ + conf_val_t val = conf_default_get_txn(conf, txn, C_STORAGE); + char *storage = conf_abs_path(&val, NULL); + val = conf_default_get_txn(conf, txn, C_KASP_DB); + char *kaspdir = conf_abs_path(&val, storage); + free(storage); + + return kaspdir; +} + +size_t conf_udp_threads_txn( + conf_t *conf, + knot_db_txn_t *txn) +{ + conf_val_t val = conf_get_txn(conf, txn, C_SRV, C_UDP_WORKERS); + int64_t workers = conf_int(&val); + if (workers == YP_NIL) { + return dt_optimal_size(); + } + + return workers; +} + +size_t conf_tcp_threads_txn( + conf_t *conf, + knot_db_txn_t *txn) +{ + conf_val_t val = conf_get_txn(conf, txn, C_SRV, C_TCP_WORKERS); + int64_t workers = conf_int(&val); + if (workers == YP_NIL) { + return MAX(conf_udp_threads_txn(conf, txn) * 2, CONF_XFERS); + } + + return workers; +} + +size_t conf_bg_threads_txn( + conf_t *conf, + knot_db_txn_t *txn) +{ + conf_val_t val = conf_get_txn(conf, txn, C_SRV, C_BG_WORKERS); + int64_t workers = conf_int(&val); + if (workers == YP_NIL) { + return MIN(dt_optimal_size(), CONF_XFERS); + } + + return workers; +} + +int conf_user_txn( + conf_t *conf, + knot_db_txn_t *txn, + int *uid, + int *gid) +{ + if (uid == NULL || gid == NULL) { + return KNOT_EINVAL; + } + + conf_val_t val = conf_get_txn(conf, txn, C_SRV, C_USER); + if (val.code == KNOT_EOK) { + char *user = strdup(conf_str(&val)); + + // Search for user:group separator. + char *sep_pos = strchr(user, ':'); + if (sep_pos != NULL) { + // Process group name. + struct group *grp = getgrnam(sep_pos + 1); + if (grp != NULL) { + *gid = grp->gr_gid; + } else { + CONF_LOG(LOG_ERR, "invalid group name '%s'", + sep_pos + 1); + free(user); + return KNOT_EINVAL; + } + + // Cut off group part. + *sep_pos = '\0'; + } else { + *gid = getgid(); + } + + // Process user name. + struct passwd *pwd = getpwnam(user); + if (pwd != NULL) { + *uid = pwd->pw_uid; + } else { + CONF_LOG(LOG_ERR, "invalid user name '%s'", user); + free(user); + return KNOT_EINVAL; + } + + free(user); + return KNOT_EOK; + } else if (val.code == KNOT_ENOENT) { + *uid = getuid(); + *gid = getgid(); + return KNOT_EOK; + } else { + return val.code; + } +} + +conf_remote_t conf_remote_txn( + conf_t *conf, + knot_db_txn_t *txn, + conf_val_t *id, + size_t index) +{ + assert(id != NULL && id->item != NULL); + assert(id->item->type == YP_TSTR || + (id->item->type == YP_TREF && + id->item->var.r.ref->var.g.id->type == YP_TSTR)); + + conf_remote_t out = { { AF_UNSPEC } }; + + conf_val_t rundir_val = conf_get_txn(conf, txn, C_SRV, C_RUNDIR); + char *rundir = conf_abs_path(&rundir_val, NULL); + + // Get indexed remote address. + conf_val_t val = conf_id_get_txn(conf, txn, C_RMT, C_ADDR, id); + for (size_t i = 0; val.code == KNOT_EOK && i < index; i++) { + if (i == 0) { + conf_val(&val); + } + conf_val_next(&val); + } + // Index overflow causes empty socket. + out.addr = conf_addr(&val, rundir); + + // Get outgoing address if family matches (optional). + val = conf_id_get_txn(conf, txn, C_RMT, C_VIA, id); + while (val.code == KNOT_EOK) { + struct sockaddr_storage via = conf_addr(&val, rundir); + if (via.ss_family == out.addr.ss_family) { + out.via = conf_addr(&val, rundir); + break; + } + conf_val_next(&val); + } + + // Get TSIG key (optional). + conf_val_t key_id = conf_id_get_txn(conf, txn, C_RMT, C_KEY, id); + if (key_id.code == KNOT_EOK) { + out.key.name = (knot_dname_t *)conf_dname(&key_id); + + val = conf_id_get_txn(conf, txn, C_KEY, C_ALG, &key_id); + out.key.algorithm = conf_opt(&val); + + val = conf_id_get_txn(conf, txn, C_KEY, C_SECRET, &key_id); + out.key.secret.data = (uint8_t *)conf_bin(&val, &out.key.secret.size); + } + + free(rundir); + + return out; +} diff --git a/src/knot/conf/conf.h b/src/knot/conf/conf.h new file mode 100644 index 0000000..bc3183e --- /dev/null +++ b/src/knot/conf/conf.h @@ -0,0 +1,715 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include <sys/socket.h> + +#include "knot/conf/base.h" +#include "knot/conf/schema.h" + +#define CONF_XFERS 10 + +/*! Configuration remote getter output. */ +typedef struct { + /*! Target socket address. */ + struct sockaddr_storage addr; + /*! Local outgoing socket address. */ + struct sockaddr_storage via; + /*! TSIG key. */ + knot_tsig_key_t key; +} conf_remote_t; + +/*! Configuration section iterator. */ +typedef struct { + /*! Item description. */ + const yp_item_t *item; + /*! Namedb iterator. */ + knot_db_iter_t *iter; + /*! Key0 database code. */ + uint8_t key0_code; + // Public items. + /*! Iterator return code. */ + int code; +} conf_iter_t; + +/*! Configuration module getter output. */ +typedef struct { + /*! Module name. */ + yp_name_t *name; + /*! Module id data. */ + uint8_t *data; + /*! Module id data length. */ + size_t len; +} conf_mod_id_t; + +/*! + * Gets the configuration item value of the section without identifiers. + * + * \param[in] conf Configuration. + * \param[in] txn Configuration DB transaction. + * \param[in] key0_name Section name. + * \param[in] key1_name Item name. + * + * \return Item value. + */ +conf_val_t conf_get_txn( + conf_t *conf, + knot_db_txn_t *txn, + const yp_name_t *key0_name, + const yp_name_t *key1_name +); +static inline conf_val_t conf_get( + conf_t *conf, + const yp_name_t *key0_name, + const yp_name_t *key1_name) +{ + return conf_get_txn(conf, &conf->read_txn, key0_name, key1_name); +} + +/*! + * Gets the configuration item value of the section with identifiers (raw version). + * + * \param[in] conf Configuration. + * \param[in] txn Configuration DB transaction. + * \param[in] key0_name Section name. + * \param[in] key1_name Item name. + * \param[in] id Section identifier (raw value). + * \param[in] id_len Length of the section identifier. + * + * \return Item value. + */ +conf_val_t conf_rawid_get_txn( + conf_t *conf, + knot_db_txn_t *txn, + const yp_name_t *key0_name, + const yp_name_t *key1_name, + const uint8_t *id, + size_t id_len +); +static inline conf_val_t conf_rawid_get( + conf_t *conf, + const yp_name_t *key0_name, + const yp_name_t *key1_name, + const uint8_t *id, + size_t id_len) +{ + return conf_rawid_get_txn(conf, &conf->read_txn, key0_name, key1_name, + id, id_len); +} + +/*! + * Gets the configuration item value of the section with identifiers. + * + * \param[in] conf Configuration. + * \param[in] txn Configuration DB transaction. + * \param[in] key0_name Section name. + * \param[in] key1_name Item name. + * \param[in] id Section identifier (output of a config getter). + * + * \return Item value. + */ +conf_val_t conf_id_get_txn( + conf_t *conf, + knot_db_txn_t *txn, + const yp_name_t *key0_name, + const yp_name_t *key1_name, + conf_val_t *id +); +static inline conf_val_t conf_id_get( + conf_t *conf, + const yp_name_t *key0_name, + const yp_name_t *key1_name, + conf_val_t *id) +{ + return conf_id_get_txn(conf, &conf->read_txn, key0_name, key1_name, id); +} + +/*! + * Gets the configuration item value of the module section. + * + * \param[in] conf Configuration. + * \param[in] txn Configuration DB transaction. + * \param[in] key1_name Item name. + * \param[in] mod_id Module identifier. + * + * \return Item value. + */ +conf_val_t conf_mod_get_txn( + conf_t *conf, + knot_db_txn_t *txn, + const yp_name_t *key1_name, + const conf_mod_id_t *mod_id +); +static inline conf_val_t conf_mod_get( + conf_t *conf, + const yp_name_t *key1_name, + const conf_mod_id_t *mod_id) +{ + return conf_mod_get_txn(conf, &conf->read_txn, key1_name, mod_id); +} + +/*! + * Gets the configuration item value of the zone section. + * + * \note A possibly associated template is taken into account. + * + * \param[in] conf Configuration. + * \param[in] txn Configuration DB transaction. + * \param[in] key1_name Item name. + * \param[in] dname Zone name. + * + * \return Item value. + */ +conf_val_t conf_zone_get_txn( + conf_t *conf, + knot_db_txn_t *txn, + const yp_name_t *key1_name, + const knot_dname_t *dname +); +static inline conf_val_t conf_zone_get( + conf_t *conf, + const yp_name_t *key1_name, + const knot_dname_t *dname) +{ + return conf_zone_get_txn(conf, &conf->read_txn, key1_name, dname); +} + +/*! + * Gets the configuration item value of the default template. + * + * \param[in] conf Configuration. + * \param[in] txn Configuration DB transaction. + * \param[in] key1_name Item name. + * + * \return Item value. + */ +conf_val_t conf_default_get_txn( + conf_t *conf, + knot_db_txn_t *txn, + const yp_name_t *key1_name +); +static inline conf_val_t conf_default_get( + conf_t *conf, + const yp_name_t *key1_name) +{ + return conf_default_get_txn(conf, &conf->read_txn, key1_name); +} + +/*! + * Checks the configuration section for the identifier (raw version). + * + * \param[in] conf Configuration. + * \param[in] txn Configuration DB transaction. + * \param[in] key0_name Section name. + * \param[in] id Section identifier (raw value). + * \param[in] id_len Length of the section identifier. + * + * \return True if exists. + */ +bool conf_rawid_exists_txn( + conf_t *conf, + knot_db_txn_t *txn, + const yp_name_t *key0_name, + const uint8_t *id, + size_t id_len +); +static inline bool conf_rawid_exists( + conf_t *conf, + const yp_name_t *key0_name, + const uint8_t *id, + size_t id_len) +{ + return conf_rawid_exists_txn(conf, &conf->read_txn, key0_name, id, id_len); +} + +/*! + * Checks the configuration section for the identifier. + * + * \param[in] conf Configuration. + * \param[in] txn Configuration DB transaction. + * \param[in] key0_name Section name. + * \param[in] id Section identifier (output of a config getter). + * + * \return True if exists. + */ +bool conf_id_exists_txn( + conf_t *conf, + knot_db_txn_t *txn, + const yp_name_t *key0_name, + conf_val_t *id +); +static inline bool conf_id_exists( + conf_t *conf, + const yp_name_t *key0_name, + conf_val_t *id) +{ + return conf_id_exists_txn(conf, &conf->read_txn, key0_name, id); +} + +/*! + * Gets the number of section identifiers. + * + * \param[in] conf Configuration. + * \param[in] txn Configuration DB transaction. + * \param[in] key0_name Section name. + * + * \return Number of identifiers. + */ +size_t conf_id_count_txn( + conf_t *conf, + knot_db_txn_t *txn, + const yp_name_t *key0_name +); +static inline size_t conf_id_count( + conf_t *conf, + const yp_name_t *key0_name) +{ + return conf_id_count_txn(conf, &conf->read_txn, key0_name); +} + +/*! + * Gets a configuration section iterator. + * + * \param[in] conf Configuration. + * \param[in] txn Configuration DB transaction. + * \param[in] key0_name Section name. + * + * \return Section iterator. + */ +conf_iter_t conf_iter_txn( + conf_t *conf, + knot_db_txn_t *txn, + const yp_name_t *key0_name +); +static inline conf_iter_t conf_iter( + conf_t *conf, + const yp_name_t *key0_name) +{ + return conf_iter_txn(conf, &conf->read_txn, key0_name); +} + +/*! + * Moves the configuration section iterator to the next identifier. + * + * \param[in] conf Configuration. + * \param[in] iter Configuration iterator. + */ +void conf_iter_next( + conf_t *conf, + conf_iter_t *iter +); + +/*! + * Gets the current iterator value (identifier). + * + * \param[in] conf Configuration. + * \param[in] iter Configuration iterator. + * + * \return Section identifier. + */ +conf_val_t conf_iter_id( + conf_t *conf, + conf_iter_t *iter +); + +/*! + * Deletes the section iterator. + * + * This function should be called when the iterating is early interrupted, + * otherwise this is done automaticaly at KNOT_EOF. + * + * \param[in] conf Configuration. + * \param[in] iter Configuration iterator. + */ +void conf_iter_finish( + conf_t *conf, + conf_iter_t *iter +); + +/*! + * Prepares the value for the direct access. + * + * The following access is through val->len and val->data. + * + * \param[in] val Item value. + */ +void conf_val( + conf_val_t *val +); + +/*! + * Moves to the next item value. + * + * \param[in] val Item value. + */ +void conf_val_next( + conf_val_t *val +); + +/*! + * Gets the number of values if multivalued item. + * + * \param[in] val Item value. + * + * \return Number of values. + */ +size_t conf_val_count( + conf_val_t *val +); + +/*! + * Checks if two item values are equal. + * + * \param[in] val1 First item value. + * \param[in] val2 Second item value. + * + * \return true if equal, false if not. + */ +bool conf_val_equal( + conf_val_t *val1, + conf_val_t *val2 +); + +/*! + * Gets the numeric value of the item. + * + * \param[in] val Item value. + * + * \return Integer. + */ +int64_t conf_int( + conf_val_t *val +); + +/*! + * Gets the boolean value of the item. + * + * \param[in] val Item value. + * + * \return Boolean. + */ +bool conf_bool( + conf_val_t *val +); + +/*! + * Gets the option value of the item. + * + * \param[in] val Item value. + * + * \return Option id. + */ +unsigned conf_opt( + conf_val_t *val +); + +/*! + * Gets the string value of the item. + * + * \param[in] val Item value. + * + * \return String pointer. + */ +const char* conf_str( + conf_val_t *val +); + +/*! + * Gets the dname value of the item. + * + * \param[in] val Item value. + * + * \return Dname pointer. + */ +const knot_dname_t* conf_dname( + conf_val_t *val +); + +/*! + * Gets the length-prefixed data value of the item. + * + * \param[in] val Item value. + * \param[out] len Output length. + * + * \return Data pointer. + */ +const uint8_t* conf_bin( + conf_val_t *val, + size_t *len +); + +/*! + * Gets the generic data value of the item. + * + * \param[in] val Item value. + * \param[out] len Output length. + * + * \return Data pointer. + */ +const uint8_t* conf_data( + conf_val_t *val, + size_t *len +); + +/*! + * Gets the socket address value of the item. + * + * \param[in] val Item value. + * \param[in] sock_base_dir Path prefix for a relative UNIX socket location. + * + * \return Socket address. + */ +struct sockaddr_storage conf_addr( + conf_val_t *val, + const char *sock_base_dir +); + +/*! + * Gets the socket address range value of the item. + * + * \param[in] val Item value. + * \param[out] max_ss Upper address bound or AF_UNSPEC family if not specified. + * \param[out] prefix_len Network subnet prefix length or -1 if not specified. + * + * \return Socket address. + */ +struct sockaddr_storage conf_addr_range( + conf_val_t *val, + struct sockaddr_storage *max_ss, + int *prefix_len +); + +/*! + * Checks the address if matches given address range/network block. + * + * \param[in] range Address range/network block. + * \param[in] addr Address to check. + * + * \return True if matches. + */ +bool conf_addr_range_match( + conf_val_t *range, + const struct sockaddr_storage *addr +); + +/*! + * Gets the absolute string value of the item. + * + * \note The result must be explicitly deallocated. + * + * \param[in] val Item value. + * \param[in] base_dir Path prefix for a relative string. + * + * \return Absolute path string pointer. + */ +char* conf_abs_path( + conf_val_t *val, + const char *base_dir +); + +/*! + * Ensures empty 'default' identifier value. + * + * \param[in] val Item value. + * + * \return Empty item value. + */ +static inline void conf_id_fix_default(conf_val_t *val) +{ + if (val->code != KNOT_EOK) { + conf_val_t empty = { + .item = val->item, + .code = KNOT_EOK + }; + + *val = empty; + } +} + +/*! + * Gets the module identifier value of the item. + * + * \param[in] val Item value. + * + * \return Module identifier. + */ +conf_mod_id_t* conf_mod_id( + conf_val_t *val +); + +/*! + * Destroys the module identifier. + * + * \param[in] mod_id Module identifier. + */ +void conf_free_mod_id( + conf_mod_id_t *mod_id +); + +/*! + * Gets the absolute zone file path. + * + * \note The result must be explicitly deallocated. + * + * \param[in] conf Configuration. + * \param[in] txn Configuration DB transaction. + * \param[in] zone Zone name. + * + * \return Absolute zonef ile path string pointer. + */ +char* conf_zonefile_txn( + conf_t *conf, + knot_db_txn_t *txn, + const knot_dname_t *zone +); +static inline char* conf_zonefile( + conf_t *conf, + const knot_dname_t *zone) +{ + return conf_zonefile_txn(conf, &conf->read_txn, zone); +} + +/*! + * Gets the absolute journal file path. + * + * \note The result must be explicitly deallocated. + * + * \param[in] conf Configuration. + * \param[in] txn Configuration DB transaction. + * + * \return Absolute journal file path string pointer. + */ +char* conf_journalfile_txn( + conf_t *conf, + knot_db_txn_t *txn); +static inline char* conf_journalfile( + conf_t *conf) +{ + return conf_journalfile_txn(conf, &conf->read_txn); +} + +char* conf_kaspdir_txn( + conf_t *conf, + knot_db_txn_t *txn); +static inline char* conf_kaspdir( + conf_t *conf) +{ + return conf_kaspdir_txn(conf, &conf->read_txn); +} + +/*! + * Gets the configured number of UDP threads. + * + * \param[in] conf Configuration. + * \param[in] txn Configuration DB transaction. + * + * \return Number of threads. + */ +size_t conf_udp_threads_txn( + conf_t *conf, + knot_db_txn_t *txn +); +static inline size_t conf_udp_threads( + conf_t *conf) +{ + return conf_udp_threads_txn(conf, &conf->read_txn); +} + +/*! + * Gets the configured number of TCP threads. + * + * \param[in] conf Configuration. + * \param[in] txn Configuration DB transaction. + * + * \return Number of threads. + */ +size_t conf_tcp_threads_txn( + conf_t *conf, + knot_db_txn_t *txn +); +static inline size_t conf_tcp_threads( + conf_t *conf) +{ + return conf_tcp_threads_txn(conf, &conf->read_txn); +} + +/*! + * Gets the configured number of worker threads. + * + * \param[in] conf Configuration. + * \param[in] txn Configuration DB transaction. + * + * \return Number of threads. + */ +size_t conf_bg_threads_txn( + conf_t *conf, + knot_db_txn_t *txn +); +static inline size_t conf_bg_threads( + conf_t *conf) +{ + return conf_bg_threads_txn(conf, &conf->read_txn); +} + +/*! + * Gets the configured user and group identifiers. + * + * \param[in] conf Configuration. + * \param[in] txn Configuration DB transaction. + * \param[out] uid User identifier. + * \param[out] gid Group identifier. + * + * \return Knot error code. + */ +int conf_user_txn( + conf_t *conf, + knot_db_txn_t *txn, + int *uid, + int *gid +); +static inline int conf_user( + conf_t *conf, + int *uid, + int *gid) +{ + return conf_user_txn(conf, &conf->read_txn, uid, gid); +} + +/*! + * Gets the remote parameters for the given identifier. + * + * \param[in] conf Configuration. + * \param[in] txn Configuration DB transaction. + * \param[in] id Remote identifier. + * \param[in] index Remote index (counted from 0). + * + * \return Remote parameters. + */ +conf_remote_t conf_remote_txn( + conf_t *conf, + knot_db_txn_t *txn, + conf_val_t *id, + size_t index +); +static inline conf_remote_t conf_remote( + conf_t *conf, + conf_val_t *id, + size_t index) +{ + return conf_remote_txn(conf, &conf->read_txn, id, index); + +} diff --git a/src/knot/conf/confdb.c b/src/knot/conf/confdb.c new file mode 100644 index 0000000..364142a --- /dev/null +++ b/src/knot/conf/confdb.c @@ -0,0 +1,951 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <assert.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <time.h> +#include <unistd.h> + +#include "knot/conf/confdb.h" +#include "libknot/errcode.h" +#include "libknot/yparser/yptrafo.h" +#include "contrib/openbsd/strlcpy.h" +#include "contrib/wire_ctx.h" + +/* + * A simple configuration: + * + * server.identity: "knot" + * server.version: "version" + * template[tpl1].storage: "directory1" + * template[tpl2].storage: "directory2" + * template[tpl2].master: [ "master1", "master2" ] + * + * And the corresponding configuration DB content: + * + * # DB structure version. + * [00][FF]: [02] + * # Sections codes. + * [00][00]server: [02] + * [00][00]template: [03] + * # Server section items codes. + * [02][00]identity: [02] + * [02][00]version: [03] + * # Server items values. + * [02][02]: knot\0 + * [02][03]: version\0 + * # Template section items codes. + * [03][00]master: [03] + * [03][00]storage: [02] + * # Template identificators. + * [03][01]tpl1\0 + * [03][01]tpl2\0 + * # Template items values. + * [03][02]tpl1\0: directory1\0 + * [03][02]tpl2\0: directory2\0 + * [03][03]tpl2\0: [00][08]master1\0 [00][08]master2\0 + */ + +typedef enum { + KEY0_ROOT = 0, + KEY1_ITEMS = 0, + KEY1_ID = 1, + KEY1_FIRST = 2, + KEY1_LAST = 200, + KEY1_VERSION = 255 +} db_code_t; + +typedef enum { + KEY0_POS = 0, + KEY1_POS = 1, + NAME_POS = 2 +} db_code_pos_t; + +typedef enum { + DB_GET, + DB_SET, + DB_DEL +} db_action_t; + +static int db_check_version( + conf_t *conf, + knot_db_txn_t *txn) +{ + uint8_t k[2] = { KEY0_ROOT, KEY1_VERSION }; + knot_db_val_t key = { k, sizeof(k) }; + knot_db_val_t data; + + // Get conf-DB version. + int ret = conf->api->find(txn, &key, &data, 0); + if (ret != KNOT_EOK) { + return ret; + } + + // Check conf-DB version. + if (data.len != 1 || ((uint8_t *)data.data)[0] != CONF_DB_VERSION) { + return KNOT_CONF_EVERSION; + } + + return KNOT_EOK; +} + +int conf_db_init( + conf_t *conf, + knot_db_txn_t *txn, + bool purge) +{ + if (conf == NULL || txn == NULL) { + return KNOT_EINVAL; + } + + uint8_t k[2] = { KEY0_ROOT, KEY1_VERSION }; + knot_db_val_t key = { k, sizeof(k) }; + + int ret = conf->api->count(txn); + if (ret == 0) { // Initialize empty DB with DB version. + uint8_t d[1] = { CONF_DB_VERSION }; + knot_db_val_t data = { d, sizeof(d) }; + return conf->api->insert(txn, &key, &data, 0); + } else if (ret > 0) { // Non-empty DB. + if (purge) { + // Purge the DB. + ret = conf->api->clear(txn); + if (ret != KNOT_EOK) { + return ret; + } + return conf_db_init(conf, txn, false); + } + return KNOT_EOK; + } else { // DB error. + return ret; + } +} + +int conf_db_check( + conf_t *conf, + knot_db_txn_t *txn) +{ + int ret = conf->api->count(txn); + if (ret == 0) { // Not initialized DB. + return KNOT_CONF_ENOTINIT; + } else if (ret > 0) { // Check the DB. + int count = ret; + + ret = db_check_version(conf, txn); + if (ret != KNOT_EOK) { + return ret; + } else if (count == 1) { + return KNOT_EOK; // Empty but initialized DB. + } else { + return count - 1; // Non-empty DB. + } + } else { // DB error. + return ret; + } +} + +static int db_code( + conf_t *conf, + knot_db_txn_t *txn, + uint8_t section_code, + const yp_name_t *name, + db_action_t action, + uint8_t *code) +{ + if (name == NULL) { + return KNOT_EINVAL; + } + + knot_db_val_t key; + uint8_t k[CONF_MIN_KEY_LEN + YP_MAX_ITEM_NAME_LEN]; + k[KEY0_POS] = section_code; + k[KEY1_POS] = KEY1_ITEMS; + memcpy(k + NAME_POS, name + 1, name[0]); + key.data = k; + key.len = CONF_MIN_KEY_LEN + name[0]; + + // Check if the item is already registered. + knot_db_val_t data; + int ret = conf->api->find(txn, &key, &data, 0); + switch (ret) { + case KNOT_EOK: + if (action == DB_DEL) { + return conf->api->del(txn, &key); + } + if (code != NULL) { + *code = ((uint8_t *)data.data)[0]; + } + return KNOT_EOK; + case KNOT_ENOENT: + if (action != DB_SET) { + return KNOT_ENOENT; + } + break; + default: + return ret; + } + + // Reduce the key to common prefix only. + key.len = CONF_MIN_KEY_LEN; + + bool codes[KEY1_LAST + 1] = { false }; + + // Find all used item codes. + knot_db_iter_t *it = conf->api->iter_begin(txn, KNOT_DB_NOOP); + it = conf->api->iter_seek(it, &key, KNOT_DB_GEQ); + while (it != NULL) { + knot_db_val_t iter_key; + ret = conf->api->iter_key(it, &iter_key); + if (ret != KNOT_EOK) { + conf->api->iter_finish(it); + return ret; + } + uint8_t *key_data = (uint8_t *)iter_key.data; + + // Check for database prefix end. + if (key_data[KEY0_POS] != k[KEY0_POS] || + key_data[KEY1_POS] != k[KEY1_POS]) { + break; + } + + knot_db_val_t iter_val; + ret = conf->api->iter_val(it, &iter_val); + if (ret != KNOT_EOK) { + conf->api->iter_finish(it); + return ret; + } + uint8_t code = ((uint8_t *)iter_val.data)[0]; + codes[code] = true; + + it = conf->api->iter_next(it); + } + conf->api->iter_finish(it); + + // Find the smallest unused item code. + uint8_t new_code = KEY1_FIRST; + while (codes[new_code]) { + new_code++; + if (new_code > KEY1_LAST) { + return KNOT_ESPACE; + } + } + + // Restore the full key. + key.len = CONF_MIN_KEY_LEN + name[0]; + + // Fill the data with a new code. + data.data = &new_code; + data.len = sizeof(new_code); + + // Register new item code. + ret = conf->api->insert(txn, &key, &data, 0); + if (ret != KNOT_EOK) { + return ret; + } + + if (code != NULL) { + *code = new_code; + } + + return KNOT_EOK; +} + +static uint8_t *find_data( + const knot_db_val_t *value, + const knot_db_val_t *current) +{ + wire_ctx_t ctx = wire_ctx_init_const(current->data, current->len); + + // Loop over the data array. Each item has 2B length prefix. + while (wire_ctx_available(&ctx) > 0) { + uint16_t len = wire_ctx_read_u16(&ctx); + assert(ctx.error == KNOT_EOK); + + // Check for the same data. + if (len == value->len && + memcmp(ctx.position, value->data, value->len) == 0) { + wire_ctx_skip(&ctx, -sizeof(uint16_t)); + assert(ctx.error == KNOT_EOK); + return ctx.position; + } + wire_ctx_skip(&ctx, len); + } + + assert(ctx.error == KNOT_EOK && wire_ctx_available(&ctx) == 0); + + return NULL; +} + +static int db_set( + conf_t *conf, + knot_db_txn_t *txn, + knot_db_val_t *key, + knot_db_val_t *data, + bool multi) +{ + if (!multi) { + if (data->len > CONF_MAX_DATA_LEN) { + return KNOT_ERANGE; + } + + // Insert new (overwrite old) data. + return conf->api->insert(txn, key, data, 0); + } + + knot_db_val_t d; + + if (data->len > UINT16_MAX) { + return KNOT_ERANGE; + } + + int ret = conf->api->find(txn, key, &d, 0); + if (ret == KNOT_ENOENT) { + d.len = 0; + } else if (ret == KNOT_EOK) { + // Check for duplicate data. + if (find_data(data, &d) != NULL) { + return KNOT_EOK; + } + } else { + return ret; + } + + // Prepare buffer for all data. + size_t new_len = d.len + sizeof(uint16_t) + data->len; + if (new_len > CONF_MAX_DATA_LEN) { + return KNOT_ESPACE; + } + + uint8_t *new_data = malloc(new_len); + if (new_data == NULL) { + return KNOT_ENOMEM; + } + + wire_ctx_t ctx = wire_ctx_init(new_data, new_len); + + // Copy current data array. + wire_ctx_write(&ctx, d.data, d.len); + // Copy length prefix for the new data item. + wire_ctx_write_u16(&ctx, data->len); + // Copy the new data item. + wire_ctx_write(&ctx, data->data, data->len); + + assert(ctx.error == KNOT_EOK && wire_ctx_available(&ctx) == 0); + + d.data = new_data; + d.len = new_len; + + // Insert new (or append) data. + ret = conf->api->insert(txn, key, &d, 0); + + free(new_data); + + return ret; +} + +int conf_db_set( + conf_t *conf, + knot_db_txn_t *txn, + const yp_name_t *key0, + const yp_name_t *key1, + const uint8_t *id, + size_t id_len, + const uint8_t *data, + size_t data_len) +{ + if (conf == NULL || txn == NULL || key0 == NULL || + (id == NULL && id_len > 0) || (data == NULL && data_len > 0)) { + return KNOT_EINVAL; + } + + // Check for valid keys. + const yp_item_t *item = yp_schema_find(key1 != NULL ? key1 : key0, + key1 != NULL ? key0 : NULL, + conf->schema); + if (item == NULL) { + return KNOT_YP_EINVAL_ITEM; + } + + // Ignore alone key0 insertion. + if (key1 == NULL && id_len == 0) { + return KNOT_EOK; + } + + // Ignore group id as a key1. + if (item->parent != NULL && (item->parent->flags & YP_FMULTI) != 0 && + item->parent->var.g.id == item) { + key1 = NULL; + } + + uint8_t k[CONF_MAX_KEY_LEN] = { 0 }; + knot_db_val_t key = { k, CONF_MIN_KEY_LEN }; + + // Set key0 code. + int ret = db_code(conf, txn, KEY0_ROOT, key0, DB_SET, &k[KEY0_POS]); + if (ret != KNOT_EOK) { + return ret; + } + + // Set id part. + if (id_len > 0) { + if (id_len > YP_MAX_ID_LEN) { + return KNOT_YP_EINVAL_ID; + } + memcpy(k + CONF_MIN_KEY_LEN, id, id_len); + key.len += id_len; + + k[KEY1_POS] = KEY1_ID; + knot_db_val_t val = { NULL }; + + // Insert id. + if (key1 == NULL) { + ret = conf->api->find(txn, &key, &val, 0); + if (ret == KNOT_EOK) { + return KNOT_CONF_EREDEFINE; + } + ret = db_set(conf, txn, &key, &val, false); + if (ret != KNOT_EOK) { + return ret; + } + // Check for existing id. + } else { + ret = conf->api->find(txn, &key, &val, 0); + if (ret != KNOT_EOK) { + return KNOT_YP_EINVAL_ID; + } + } + } + + // Insert key1 data. + if (key1 != NULL) { + // Set key1 code. + ret = db_code(conf, txn, k[KEY0_POS], key1, DB_SET, &k[KEY1_POS]); + if (ret != KNOT_EOK) { + return ret; + } + + knot_db_val_t val = { (uint8_t *)data, data_len }; + ret = db_set(conf, txn, &key, &val, item->flags & YP_FMULTI); + if (ret != KNOT_EOK) { + return ret; + } + } + + return KNOT_EOK; +} + +static int db_unset( + conf_t *conf, + knot_db_txn_t *txn, + knot_db_val_t *key, + knot_db_val_t *data, + bool multi) +{ + // No item data can be zero length. + if (data->len == 0) { + return conf->api->del(txn, key); + } + + knot_db_val_t d; + + int ret = conf->api->find(txn, key, &d, 0); + if (ret != KNOT_EOK) { + return ret; + } + + // Process singlevalued data. + if (!multi) { + if (d.len != data->len || + memcmp((uint8_t *)d.data, data->data, d.len) != 0) { + return KNOT_ENOENT; + } + return conf->api->del(txn, key); + } + + // Check if the data exists. + uint8_t *pos = find_data(data, &d); + if (pos == NULL) { + return KNOT_ENOENT; + } + + // Prepare buffer for reduced data. + size_t total_len = d.len - sizeof(uint16_t) - data->len; + if (total_len == 0) { + return conf->api->del(txn, key); + } + + uint8_t *new_data = malloc(total_len); + if (new_data == NULL) { + return KNOT_ENOMEM; + } + + size_t new_len = 0; + + // Copy leading data block. + assert(pos >= (uint8_t *)d.data); + size_t head_len = pos - (uint8_t *)d.data; + if (head_len > 0) { + memcpy(new_data, d.data, head_len); + new_len += head_len; + } + + pos += sizeof(uint16_t) + data->len; + + // Copy trailing data block. + assert(pos <= (uint8_t *)d.data + d.len); + size_t tail_len = (uint8_t *)d.data + d.len - pos; + if (tail_len > 0) { + memcpy(new_data + new_len, pos, tail_len); + new_len += tail_len; + } + + d.data = new_data; + d.len = new_len; + + // Insert reduced data. + ret = conf->api->insert(txn, key, &d, 0); + + free(new_data); + + return ret; +} + +int conf_db_unset( + conf_t *conf, + knot_db_txn_t *txn, + const yp_name_t *key0, + const yp_name_t *key1, + const uint8_t *id, + size_t id_len, + const uint8_t *data, + size_t data_len, + bool delete_key1) +{ + if (conf == NULL || txn == NULL || key0 == NULL || + (id == NULL && id_len > 0) || (data == NULL && data_len > 0)) { + return KNOT_EINVAL; + } + + // Check for valid keys. + const yp_item_t *item = yp_schema_find(key1 != NULL ? key1 : key0, + key1 != NULL ? key0 : NULL, + conf->schema); + if (item == NULL) { + return KNOT_YP_EINVAL_ITEM; + } + + // Delete the key0. + if (key1 == NULL && id_len == 0) { + return db_code(conf, txn, KEY0_ROOT, key0, DB_DEL, NULL); + } + + // Ignore group id as a key1. + if (item->parent != NULL && (item->parent->flags & YP_FMULTI) != 0 && + item->parent->var.g.id == item) { + key1 = NULL; + } + + uint8_t k[CONF_MAX_KEY_LEN] = { 0 }; + knot_db_val_t key = { k, CONF_MIN_KEY_LEN }; + + // Set the key0 code. + int ret = db_code(conf, txn, KEY0_ROOT, key0, DB_GET, &k[KEY0_POS]); + if (ret != KNOT_EOK) { + return ret; + } + + // Set the id part. + if (id_len > 0) { + if (id_len > YP_MAX_ID_LEN) { + return KNOT_YP_EINVAL_ID; + } + memcpy(k + CONF_MIN_KEY_LEN, id, id_len); + key.len += id_len; + + k[KEY1_POS] = KEY1_ID; + knot_db_val_t val = { NULL }; + + // Delete the id. + if (key1 == NULL) { + return conf->api->del(txn, &key); + // Check for existing id. + } else { + ret = conf->api->find(txn, &key, &val, 0); + if (ret != KNOT_EOK) { + return KNOT_YP_EINVAL_ID; + } + } + } + + if (key1 != NULL) { + // Set the key1 code. + ret = db_code(conf, txn, k[KEY0_POS], key1, DB_GET, &k[KEY1_POS]); + if (ret != KNOT_EOK) { + return ret; + } + + // Delete the key1. + if (data_len == 0 && delete_key1) { + ret = db_code(conf, txn, k[KEY0_POS], key1, DB_DEL, NULL); + if (ret != KNOT_EOK) { + return ret; + } + // Delete the item data. + } else { + knot_db_val_t val = { (uint8_t *)data, data_len }; + ret = db_unset(conf, txn, &key, &val, item->flags & YP_FMULTI); + if (ret != KNOT_EOK) { + return ret; + } + } + } + + return KNOT_EOK; +} + +int conf_db_get( + conf_t *conf, + knot_db_txn_t *txn, + const yp_name_t *key0, + const yp_name_t *key1, + const uint8_t *id, + size_t id_len, + conf_val_t *data) +{ + conf_val_t out = { NULL }; + + if (conf == NULL || txn == NULL || key0 == NULL || + (id == NULL && id_len > 0)) { + out.code = KNOT_EINVAL; + goto get_error; + } + + // Check for valid keys. + out.item = yp_schema_find(key1 != NULL ? key1 : key0, + key1 != NULL ? key0 : NULL, + conf->schema); + if (out.item == NULL) { + out.code = KNOT_YP_EINVAL_ITEM; + goto get_error; + } + + // At least key1 or id must be specified. + if (key1 == NULL && id_len == 0) { + out.code = KNOT_EINVAL; + goto get_error; + } + + // Ignore group id as a key1. + if (out.item->parent != NULL && (out.item->parent->flags & YP_FMULTI) != 0 && + out.item->parent->var.g.id == out.item) { + key1 = NULL; + } + + uint8_t k[CONF_MAX_KEY_LEN] = { 0 }; + knot_db_val_t key = { k, CONF_MIN_KEY_LEN }; + knot_db_val_t val = { NULL }; + + // Set the key0 code. + out.code = db_code(conf, txn, KEY0_ROOT, key0, DB_GET, &k[KEY0_POS]); + if (out.code != KNOT_EOK) { + if (id_len > 0) { + out.code = KNOT_YP_EINVAL_ID; + } + goto get_error; + } + + // Set the id part. + if (id_len > 0) { + if (id_len > YP_MAX_ID_LEN) { + out.code = KNOT_YP_EINVAL_ID; + goto get_error; + } + memcpy(k + CONF_MIN_KEY_LEN, id, id_len); + key.len += id_len; + + k[KEY1_POS] = KEY1_ID; + + // Check for existing id. + out.code = conf->api->find(txn, &key, &val, 0); + if (out.code != KNOT_EOK) { + out.code = KNOT_YP_EINVAL_ID; + goto get_error; + } + } + + // Set the key1 code. + if (key1 != NULL) { + out.code = db_code(conf, txn, k[KEY0_POS], key1, DB_GET, &k[KEY1_POS]); + if (out.code != KNOT_EOK) { + goto get_error; + } + } + + // Get the data. + out.code = conf->api->find(txn, &key, &val, 0); + if (out.code == KNOT_EOK) { + out.blob = val.data; + out.blob_len = val.len; + } +get_error: + // Set the output. + if (data != NULL) { + *data = out; + } + + return out.code; +} + +static int check_iter( + conf_t *conf, + conf_iter_t *iter) +{ + knot_db_val_t key; + + // Get the current key. + int ret = conf->api->iter_key(iter->iter, &key); + if (ret != KNOT_EOK) { + return KNOT_ENOENT; + } + uint8_t *key_data = (uint8_t *)key.data; + + // Check for key overflow. + if (key_data[KEY0_POS] != iter->key0_code || key_data[KEY1_POS] != KEY1_ID) { + return KNOT_EOF; + } + + return KNOT_EOK; +} + +int conf_db_iter_begin( + conf_t *conf, + knot_db_txn_t *txn, + const yp_name_t *key0, + conf_iter_t *iter) +{ + conf_iter_t out = { NULL }; + + if (conf == NULL || txn == NULL || key0 == NULL || iter == NULL) { + out.code = KNOT_EINVAL; + goto iter_begin_error; + } + + // Look-up group id item in the schema. + const yp_item_t *grp = yp_schema_find(key0, NULL, conf->schema); + if (grp == NULL) { + out.code = KNOT_YP_EINVAL_ITEM; + goto iter_begin_error; + } + if (grp->type != YP_TGRP || (grp->flags & YP_FMULTI) == 0) { + out.code = KNOT_ENOTSUP; + goto iter_begin_error; + } + out.item = grp->var.g.id; + + // Get key0 code. + out.code = db_code(conf, txn, KEY0_ROOT, key0, DB_GET, &out.key0_code); + if (out.code != KNOT_EOK) { + goto iter_begin_error; + } + + // Prepare key prefix. + uint8_t k[2] = { out.key0_code, KEY1_ID }; + knot_db_val_t key = { k, sizeof(k) }; + + // Get the data. + out.iter = conf->api->iter_begin(txn, KNOT_DB_NOOP); + out.iter = conf->api->iter_seek(out.iter, &key, KNOT_DB_GEQ); + + // Check for no section id. + out.code = check_iter(conf, &out); + if (out.code != KNOT_EOK) { + out.code = KNOT_ENOENT; // Treat all errors as no entry. + conf_db_iter_finish(conf, &out); + goto iter_begin_error; + } + +iter_begin_error: + // Set the output. + if (iter != NULL) { + *iter = out; + } + + return out.code; +} + +int conf_db_iter_next( + conf_t *conf, + conf_iter_t *iter) +{ + if (conf == NULL || iter == NULL) { + return KNOT_EINVAL; + } + + if (iter->code != KNOT_EOK) { + return iter->code; + } + assert(iter->iter != NULL); + + // Move to the next key-value. + iter->iter = conf->api->iter_next(iter->iter); + if (iter->iter == NULL) { + conf_db_iter_finish(conf, iter); + iter->code = KNOT_EOF; + return iter->code; + } + + // Check for key overflow. + iter->code = check_iter(conf, iter); + if (iter->code != KNOT_EOK) { + conf_db_iter_finish(conf, iter); + return iter->code; + } + + return KNOT_EOK; +} + +int conf_db_iter_id( + conf_t *conf, + conf_iter_t *iter, + const uint8_t **data, + size_t *data_len) +{ + if (conf == NULL || iter == NULL || iter->iter == NULL || + data == NULL || data_len == NULL) { + return KNOT_EINVAL; + } + + knot_db_val_t key; + int ret = conf->api->iter_key(iter->iter, &key); + if (ret != KNOT_EOK) { + return ret; + } + + *data = (uint8_t *)key.data + CONF_MIN_KEY_LEN; + *data_len = key.len - CONF_MIN_KEY_LEN; + + return KNOT_EOK; +} + +int conf_db_iter_del( + conf_t *conf, + conf_iter_t *iter) +{ + if (conf == NULL || iter == NULL || iter->iter == NULL) { + return KNOT_EINVAL; + } + + return knot_db_lmdb_iter_del(iter->iter); +} + +void conf_db_iter_finish( + conf_t *conf, + conf_iter_t *iter) +{ + if (conf == NULL || iter == NULL) { + return; + } + + if (iter->iter != NULL) { + conf->api->iter_finish(iter->iter); + iter->iter = NULL; + } +} + +int conf_db_raw_dump( + conf_t *conf, + knot_db_txn_t *txn, + const char *file_name) +{ + if (conf == NULL) { + return KNOT_EINVAL; + } + + // Use the current config read transaction if not specified. + if (txn == NULL) { + txn = &conf->read_txn; + } + + FILE *fp = stdout; + if (file_name != NULL) { + fp = fopen(file_name, "w"); + if (fp == NULL) { + return KNOT_ERROR; + } + } + + int ret = KNOT_EOK; + + knot_db_iter_t *it = conf->api->iter_begin(txn, KNOT_DB_FIRST); + while (it != NULL) { + knot_db_val_t key; + ret = conf->api->iter_key(it, &key); + if (ret != KNOT_EOK) { + break; + } + + knot_db_val_t data; + ret = conf->api->iter_val(it, &data); + if (ret != KNOT_EOK) { + break; + } + + uint8_t *k = (uint8_t *)key.data; + uint8_t *d = (uint8_t *)data.data; + if (k[1] == KEY1_ITEMS) { + fprintf(fp, "[%i][%i]%.*s", k[0], k[1], + (int)key.len - 2, k + 2); + fprintf(fp, ": %u\n", d[0]); + } else if (k[1] == KEY1_ID) { + fprintf(fp, "[%i][%i](%zu){", k[0], k[1], key.len - 2); + for (size_t i = 2; i < key.len; i++) { + fprintf(fp, "%02x", (uint8_t)k[i]); + } + fprintf(fp, "}\n"); + } else { + fprintf(fp, "[%i][%i]", k[0], k[1]); + if (key.len > 2) { + fprintf(fp, "(%zu){", key.len - 2); + for (size_t i = 2; i < key.len; i++) { + fprintf(fp, "%02x", (uint8_t)k[i]); + } + fprintf(fp, "}"); + } + fprintf(fp, ": (%zu)<", data.len); + for (size_t i = 0; i < data.len; i++) { + fprintf(fp, "%02x", (uint8_t)d[i]); + } + fprintf(fp, ">\n"); + } + + it = conf->api->iter_next(it); + } + conf->api->iter_finish(it); + + if (file_name != NULL) { + fclose(fp); + } else { + fflush(fp); + } + + return ret; +} diff --git a/src/knot/conf/confdb.h b/src/knot/conf/confdb.h new file mode 100644 index 0000000..04e222d --- /dev/null +++ b/src/knot/conf/confdb.h @@ -0,0 +1,230 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include <stdbool.h> +#include <stdint.h> + +#include "knot/conf/conf.h" +#include "libknot/libknot.h" +#include "libknot/yparser/ypschema.h" + +/*! Current version of the configuration database structure. */ +#define CONF_DB_VERSION 2 +/*! Minimum length of a database key ([category_id, item_id]. */ +#define CONF_MIN_KEY_LEN (2 * sizeof(uint8_t)) +/*! Maximum length of a database key ([category_id, item_id, identifier]. */ +#define CONF_MAX_KEY_LEN (CONF_MIN_KEY_LEN + YP_MAX_ID_LEN) +/*! Maximum size of database data. */ +#define CONF_MAX_DATA_LEN 65536 + +/*! + * Initializes the configuration DB if empty. + * + * \param[in] conf Configuration. + * \param[in] txn Configuration DB transaction. + * \param[in] purge Purge the DB indicator. + * + * \return Error code, KNOT_EOK if success. + */ +int conf_db_init( + conf_t *conf, + knot_db_txn_t *txn, + bool purge +); + +/*! + * Checks the configuration DB and returns the number of items. + * + * \param[in] conf Configuration. + * \param[in] txn Configuration DB transaction. + * + * \return Error code, KNOT_EOK if ok and empty, > 0 number of records. + */ +int conf_db_check( + conf_t *conf, + knot_db_txn_t *txn +); + +/*! + * Sets the item with data in the configuration DB. + * + * Singlevalued data is rewritten, multivalued data is appended. + * + * \note Setting of key0 without key1 has no effect. + * + * \param[in] conf Configuration. + * \param[in] txn Configuration DB transaction. + * \param[in] key0 Section name. + * \param[in] key1 Item name. + * \param[in] id Section identifier. + * \param[in] id_len Length of the section identifier. + * \param[in] data Item data. + * \param[in] data_len Length of the item data. + * + * \return Error code, KNOT_EOK if success. + */ +int conf_db_set( + conf_t *conf, + knot_db_txn_t *txn, + const yp_name_t *key0, + const yp_name_t *key1, + const uint8_t *id, + size_t id_len, + const uint8_t *data, + size_t data_len +); + +/*! + * Unsets the item data in the configuration DB. + * + * If no data is provided, the whole item is remove. + * + * \param[in] conf Configuration. + * \param[in] txn Configuration DB transaction. + * \param[in] key0 Section name. + * \param[in] key1 Item name. + * \param[in] id Section identifier. + * \param[in] id_len Length of the section identifier. + * \param[in] data Item data. + * \param[in] data_len Length of the item data. + * \param[in] delete_key1 Set to unregister the item from the DB. + * + * \return Error code, KNOT_EOK if success. + */ +int conf_db_unset( + conf_t *conf, + knot_db_txn_t *txn, + const yp_name_t *key0, + const yp_name_t *key1, + const uint8_t *id, + size_t id_len, + const uint8_t *data, + size_t data_len, + bool delete_key1 +); + +/*! + * Gets the item data from the configuration DB. + * + * \param[in] conf Configuration. + * \param[in] txn Configuration DB transaction. + * \param[in] key0 Section name. + * \param[in] key1 Item name. + * \param[in] id Section identifier. + * \param[in] id_len Length of the section identifier. + * \param[out] data Item data. + * + * \return Error code, KNOT_EOK if success. + */ +int conf_db_get( + conf_t *conf, + knot_db_txn_t *txn, + const yp_name_t *key0, + const yp_name_t *key1, + const uint8_t *id, + size_t id_len, + conf_val_t *data +); + +/*! + * Gets a configuration DB section iterator. + * + * \param[in] conf Configuration. + * \param[in] txn Configuration DB transaction. + * \param[in] key0 Section name. + * \param[out] iter Section iterator. + * + * \return Error code, KNOT_EOK if success. + */ +int conf_db_iter_begin( + conf_t *conf, + knot_db_txn_t *txn, + const yp_name_t *key0, + conf_iter_t *iter +); + +/*! + * Moves the section iterator to the next identifier. + * + * \param[in] conf Configuration. + * \param[in,out] iter Section iterator. + * + * \return Error code, KNOT_EOK if success. + */ +int conf_db_iter_next( + conf_t *conf, + conf_iter_t *iter +); + +/*! + * Gets the current section iterator value (identifier). + * + * \param[in] conf Configuration. + * \param[in] iter Section iterator. + * \param[out] data Identifier. + * \param[out] data_len Length of the identifier. + * + * \return Error code, KNOT_EOK if success. + */ +int conf_db_iter_id( + conf_t *conf, + conf_iter_t *iter, + const uint8_t **data, + size_t *data_len +); + +/*! + * Deletes the current section iterator value (identifier). + * + * \param[in] conf Configuration. + * \param[in,out] iter Section iterator. + * + * \return Error code, KNOT_EOK if success. + */ +int conf_db_iter_del( + conf_t *conf, + conf_iter_t *iter +); + +/*! + * Deletes the section iterator. + * + * \param[in] conf Configuration. + * \param[in,out] iter Section iterator. + */ +void conf_db_iter_finish( + conf_t *conf, + conf_iter_t *iter +); + +/*! + * Dumps the configuration DB in the textual form. + * + * \note This function is intended for debugging. + * + * \param[in] conf Configuration. + * \param[in] txn Configuration DB transaction. + * \param[in] file_name File name to dump to (NULL to dump to stdout). + * + * \return Error code, KNOT_EOK if success. + */ +int conf_db_raw_dump( + conf_t *conf, + knot_db_txn_t *txn, + const char *file_name +); diff --git a/src/knot/conf/confio.c b/src/knot/conf/confio.c new file mode 100644 index 0000000..bd24800 --- /dev/null +++ b/src/knot/conf/confio.c @@ -0,0 +1,1514 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <assert.h> + +#include "knot/common/log.h" +#include "knot/conf/confdb.h" +#include "knot/conf/confio.h" +#include "knot/conf/module.h" +#include "knot/conf/tools.h" + +#define FCN(io) (io->fcn != NULL) ? io->fcn(io) : KNOT_EOK; + +static void io_reset_val( + conf_io_t *io, + const yp_item_t *key0, + const yp_item_t *key1, + const uint8_t *id, + size_t id_len, + bool id_as_data, + conf_val_t *val) +{ + io->key0 = key0; + io->key1 = key1; + io->id = id; + io->id_len = id_len; + io->id_as_data = id_as_data; + io->data.val = val; + io->data.bin = NULL; +} + +static void io_reset_bin( + conf_io_t *io, + const yp_item_t *key0, + const yp_item_t *key1, + const uint8_t *id, + size_t id_len, + const uint8_t *bin, + size_t bin_len) +{ + io_reset_val(io, key0, key1, id, id_len, false, NULL); + io->data.bin = bin; + io->data.bin_len = bin_len; +} + +int conf_io_begin( + bool child) +{ + assert(conf() != NULL); + + if (conf()->io.txn != NULL && !child) { + return KNOT_TXN_EEXISTS; + } else if (conf()->io.txn == NULL && child) { + return KNOT_TXN_ENOTEXISTS; + } + + knot_db_txn_t *parent = conf()->io.txn; + knot_db_txn_t *txn = (parent == NULL) ? conf()->io.txn_stack : parent + 1; + if (txn >= conf()->io.txn_stack + CONF_MAX_TXN_DEPTH) { + return KNOT_TXN_EEXISTS; + } + + // Start the writing transaction. + int ret = knot_db_lmdb_txn_begin(conf()->db, txn, parent, 0); + if (ret != KNOT_EOK) { + return ret; + } + + conf()->io.txn = txn; + + // Reset master transaction flags. + if (!child) { + conf()->io.flags = CONF_IO_FACTIVE; + if (conf()->io.zones != NULL) { + trie_clear(conf()->io.zones); + } + } + + return KNOT_EOK; +} + +int conf_io_commit( + bool child) +{ + assert(conf() != NULL); + + if (conf()->io.txn == NULL || + (child && conf()->io.txn == conf()->io.txn_stack)) { + return KNOT_TXN_ENOTEXISTS; + } + + knot_db_txn_t *txn = child ? conf()->io.txn : conf()->io.txn_stack; + + // Commit the writing transaction. + int ret = conf()->api->txn_commit(txn); + + conf()->io.txn = child ? txn - 1 : NULL; + + return ret; +} + +void conf_io_abort( + bool child) +{ + assert(conf() != NULL); + + if (conf()->io.txn == NULL || + (child && conf()->io.txn == conf()->io.txn_stack)) { + return; + } + + knot_db_txn_t *txn = child ? conf()->io.txn : conf()->io.txn_stack; + + // Abort the writing transaction. + conf()->api->txn_abort(txn); + conf()->io.txn = child ? txn - 1 : NULL; + + // Reset master transaction flags. + if (!child) { + conf()->io.flags = YP_FNONE; + if (conf()->io.zones != NULL) { + trie_clear(conf()->io.zones); + } + } +} + +static int list_section( + const yp_item_t *items, + const yp_item_t **item, + conf_io_t *io) +{ + for (*item = items; (*item)->name != NULL; (*item)++) { + int ret = FCN(io); + if (ret != KNOT_EOK) { + return ret; + } + } + + return KNOT_EOK; +} + +int conf_io_list( + const char *key0, + conf_io_t *io) +{ + if (io == NULL) { + return KNOT_EINVAL; + } + + assert(conf() != NULL); + + // List schema sections by default. + if (key0 == NULL) { + io_reset_val(io, NULL, NULL, NULL, 0, false, NULL); + + return list_section(conf()->schema, &io->key0, io); + } + + yp_check_ctx_t *ctx = yp_schema_check_init(&conf()->schema); + if (ctx == NULL) { + return KNOT_ENOMEM; + } + + // Check the input. + int ret = yp_schema_check_str(ctx, key0, NULL, NULL, NULL); + if (ret != KNOT_EOK) { + goto list_error; + } + + yp_node_t *node = &ctx->nodes[ctx->current]; + + // Check for non-group item. + if (node->item->type != YP_TGRP) { + ret = KNOT_ENOTSUP; + goto list_error; + } + + io_reset_val(io, node->item, NULL, NULL, 0, false, NULL); + + ret = list_section(node->item->sub_items, &io->key1, io); +list_error: + yp_schema_check_deinit(ctx); + + return ret; +} + +static int diff_item( + conf_io_t *io) +{ + // Process an identifier item. + if ((io->key0->flags & YP_FMULTI) != 0 && io->key0->var.g.id == io->key1) { + bool old_id, new_id; + + // Check if a removed identifier. + int ret = conf_db_get(conf(), &conf()->read_txn, io->key0->name, + NULL, io->id, io->id_len, NULL); + switch (ret) { + case KNOT_EOK: + old_id = true; + break; + case KNOT_ENOENT: + case KNOT_YP_EINVAL_ID: + old_id = false; + break; + default: + return ret; + } + + // Check if an added identifier. + ret = conf_db_get(conf(), conf()->io.txn, io->key0->name, NULL, + io->id, io->id_len, NULL); + switch (ret) { + case KNOT_EOK: + new_id = true; + break; + case KNOT_ENOENT: + case KNOT_YP_EINVAL_ID: + new_id = false; + break; + default: + return ret; + } + + // Check if valid identifier. + if (!old_id && !new_id) { + return KNOT_YP_EINVAL_ID; + } + + if (old_id != new_id) { + io->id_as_data = true; + io->type = old_id ? OLD : NEW; + + // Process the callback. + ret = FCN(io); + + // Reset the modified parameters. + io->id_as_data = false; + io->type = NONE; + + if (ret != KNOT_EOK) { + return ret; + } + } + + return KNOT_EOK; + } + + conf_val_t old_val, new_val; + + // Get the old item value. + conf_db_get(conf(), &conf()->read_txn, io->key0->name, io->key1->name, + io->id, io->id_len, &old_val); + switch (old_val.code) { + case KNOT_EOK: + break; + case KNOT_ENOENT: + case KNOT_YP_EINVAL_ID: + break; + default: + return old_val.code; + } + + // Get the new item value. + conf_db_get(conf(), conf()->io.txn, io->key0->name, io->key1->name, + io->id, io->id_len, &new_val); + switch (new_val.code) { + case KNOT_EOK: + break; + case KNOT_ENOENT: + case KNOT_YP_EINVAL_ID: + if (old_val.code != KNOT_EOK) { + return KNOT_EOK; + } + break; + default: + return new_val.code; + } + + // Process the value difference. + if (old_val.code != KNOT_EOK) { + io->data.val = &new_val; + io->type = NEW; + int ret = FCN(io); + if (ret != KNOT_EOK) { + return ret; + } + } else if (new_val.code != KNOT_EOK) { + io->data.val = &old_val; + io->type = OLD; + int ret = FCN(io); + if (ret != KNOT_EOK) { + return ret; + } + } else if (!conf_val_equal(&old_val, &new_val)) { + io->data.val = &old_val; + io->type = OLD; + int ret = FCN(io); + if (ret != KNOT_EOK) { + return ret; + } + + io->data.val = &new_val; + io->type = NEW; + ret = FCN(io); + if (ret != KNOT_EOK) { + return ret; + } + } + + // Reset the modified parameters. + io->data.val = NULL; + io->type = NONE; + + return KNOT_EOK; +} + +static int diff_section( + conf_io_t *io) +{ + // Get the value for the specified item. + if (io->key1 != NULL) { + return diff_item(io); + } + + // Get the values for all items. + for (yp_item_t *i = io->key0->sub_items; i->name != NULL; i++) { + io->key1 = i; + + int ret = diff_item(io); + + // Reset the modified parameters. + io->key1 = NULL; + + if (ret != KNOT_EOK) { + return ret; + } + } + + return KNOT_EOK; +} + +static int diff_iter_section( + conf_io_t *io) +{ + // First compare the section with the old and common identifiers. + conf_iter_t iter; + int ret = conf_db_iter_begin(conf(), &conf()->read_txn, io->key0->name, + &iter); + switch (ret) { + case KNOT_EOK: + break; + case KNOT_ENOENT: + // Continue to the second step. + ret = KNOT_EOF; + break; + default: + return ret; + } + + while (ret == KNOT_EOK) { + ret = conf_db_iter_id(conf(), &iter, &io->id, &io->id_len); + if (ret != KNOT_EOK) { + conf_db_iter_finish(conf(), &iter); + return ret; + } + + ret = diff_section(io); + if (ret != KNOT_EOK) { + conf_db_iter_finish(conf(), &iter); + return ret; + } + + ret = conf_db_iter_next(conf(), &iter); + } + if (ret != KNOT_EOF) { + return ret; + } + + // Second compare the section with the new identifiers. + ret = conf_db_iter_begin(conf(), conf()->io.txn, io->key0->name, &iter); + switch (ret) { + case KNOT_EOK: + break; + case KNOT_ENOENT: + return KNOT_EOK; + default: + return ret; + } + + while (ret == KNOT_EOK) { + ret = conf_db_iter_id(conf(), &iter, &io->id, &io->id_len); + if (ret != KNOT_EOK) { + conf_db_iter_finish(conf(), &iter); + return ret; + } + + // Ignore old and common identifiers. + ret = conf_db_get(conf(), &conf()->read_txn, io->key0->name, + NULL, io->id, io->id_len, NULL); + switch (ret) { + case KNOT_EOK: + ret = conf_db_iter_next(conf(), &iter); + continue; + case KNOT_ENOENT: + case KNOT_YP_EINVAL_ID: + break; + default: + conf_db_iter_finish(conf(), &iter); + return ret; + } + + ret = diff_section(io); + if (ret != KNOT_EOK) { + conf_db_iter_finish(conf(), &iter); + return ret; + } + + ret = conf_db_iter_next(conf(), &iter); + } + if (ret != KNOT_EOF) { + return ret; + } + + return KNOT_EOK; +} + +static int diff_zone_section( + conf_io_t *io) +{ + assert(io->key0->flags & CONF_IO_FZONE); + + if (conf()->io.zones == NULL) { + return KNOT_EOK; + } + + trie_it_t *it = trie_it_begin(conf()->io.zones); + for (; !trie_it_finished(it); trie_it_next(it)) { + io->id = (const uint8_t *)trie_it_key(it, &io->id_len); + + // Get the difference for specific zone. + int ret = diff_section(io); + if (ret != KNOT_EOK) { + trie_it_free(it); + return ret; + } + } + trie_it_free(it); + + return KNOT_EOK; +} + +int conf_io_diff( + const char *key0, + const char *key1, + const char *id, + conf_io_t *io) +{ + if (io == NULL) { + return KNOT_EINVAL; + } + + assert(conf() != NULL); + + if (conf()->io.txn == NULL) { + return KNOT_TXN_ENOTEXISTS; + } + + // Compare all sections by default. + if (key0 == NULL) { + for (yp_item_t *i = conf()->schema; i->name != NULL; i++) { + // Skip non-group item. + if (i->type != YP_TGRP) { + continue; + } + + int ret = conf_io_diff(i->name + 1, key1, NULL, io); + + // Reset parameters after each section. + io_reset_val(io, NULL, NULL, NULL, 0, false, NULL); + + if (ret != KNOT_EOK) { + return ret; + } + } + + return KNOT_EOK; + } + + yp_check_ctx_t *ctx = yp_schema_check_init(&conf()->schema); + if (ctx == NULL) { + return KNOT_ENOMEM; + } + + // Check the input. + int ret = yp_schema_check_str(ctx, key0, key1, id, NULL); + if (ret != KNOT_EOK) { + goto diff_error; + } + + yp_node_t *node = &ctx->nodes[ctx->current]; + yp_node_t *parent = node->parent; + + // Key1 is not a group identifier. + if (parent != NULL) { + io_reset_val(io, parent->item, node->item, parent->id, + parent->id_len, false, NULL); + // Key1 is a group identifier. + } else if (key1 != NULL && strlen(key1) != 0) { + assert(node->item->type == YP_TGRP && + (node->item->flags & YP_FMULTI) != 0); + + io_reset_val(io, node->item, node->item->var.g.id, node->id, + node->id_len, true, NULL); + // No key1 specified. + } else { + io_reset_val(io, node->item, NULL, node->id, node->id_len, + false, NULL); + } + + // Check for a non-group item. + if (io->key0->type != YP_TGRP) { + ret = KNOT_ENOTSUP; + goto diff_error; + } + + // Compare the section with all identifiers by default. + if ((io->key0->flags & YP_FMULTI) != 0 && io->id_len == 0) { + // The zone section has an optimized diff. + if (io->key0->flags & CONF_IO_FZONE) { + // Full diff by default. + if (!(conf()->io.flags & CONF_IO_FACTIVE)) { + ret = diff_iter_section(io); + // Full diff if all zones changed. + } else if (conf()->io.flags & CONF_IO_FDIFF_ZONES) { + ret = diff_iter_section(io); + // Optimized diff for specific zones. + } else { + ret = diff_zone_section(io); + } + } else { + ret = diff_iter_section(io); + } + + goto diff_error; + } + + // Compare the section with a possible identifier. + ret = diff_section(io); +diff_error: + yp_schema_check_deinit(ctx); + + return ret; +} + +static int get_section( + knot_db_txn_t *txn, + conf_io_t *io) +{ + conf_val_t data; + + // Get the value for the specified item. + if (io->key1 != NULL) { + if (!io->id_as_data) { + // Get the item value. + conf_db_get(conf(), txn, io->key0->name, io->key1->name, + io->id, io->id_len, &data); + switch (data.code) { + case KNOT_EOK: + break; + case KNOT_ENOENT: + return KNOT_EOK; + default: + return data.code; + } + + io->data.val = &data; + } + + // Process the callback. + int ret = FCN(io); + + // Reset the modified parameters. + io->data.val = NULL; + + return ret; + } + + // Get the values for all section items by default. + for (yp_item_t *i = io->key0->sub_items; i->name != NULL; i++) { + // Process the (first) identifier item. + if ((io->key0->flags & YP_FMULTI) != 0 && io->key0->var.g.id == i) { + // Check if existing identifier. + conf_db_get(conf(), txn, io->key0->name, NULL, io->id, + io->id_len, &data); + switch (data.code) { + case KNOT_EOK: + break; + case KNOT_ENOENT: + continue; + default: + return data.code; + } + + io->key1 = i; + io->id_as_data = true; + + // Process the callback. + int ret = FCN(io); + + // Reset the modified parameters. + io->key1 = NULL; + io->id_as_data = false; + + if (ret != KNOT_EOK) { + return ret; + } + + continue; + } + + // Get the item value. + conf_db_get(conf(), txn, io->key0->name, i->name, io->id, + io->id_len, &data); + switch (data.code) { + case KNOT_EOK: + break; + case KNOT_ENOENT: + continue; + default: + return data.code; + } + + io->key1 = i; + io->data.val = &data; + + // Process the callback. + int ret = FCN(io); + + // Reset the modified parameters. + io->key1 = NULL; + io->data.val = NULL; + + if (ret != KNOT_EOK) { + return ret; + } + } + + return KNOT_EOK; +} + +int conf_io_get( + const char *key0, + const char *key1, + const char *id, + bool get_current, + conf_io_t *io) +{ + if (io == NULL) { + return KNOT_EINVAL; + } + + assert(conf() != NULL); + + if (conf()->io.txn == NULL && !get_current) { + return KNOT_TXN_ENOTEXISTS; + } + + // List all sections by default. + if (key0 == NULL) { + for (yp_item_t *i = conf()->schema; i->name != NULL; i++) { + // Skip non-group item. + if (i->type != YP_TGRP) { + continue; + } + + int ret = conf_io_get(i->name + 1, key1, NULL, + get_current, io); + // Reset parameters after each section. + io_reset_val(io, NULL, NULL, NULL, 0, false, NULL); + if (ret != KNOT_EOK) { + return ret; + } + } + + return KNOT_EOK; + } + + yp_check_ctx_t *ctx = yp_schema_check_init(&conf()->schema); + if (ctx == NULL) { + return KNOT_ENOMEM; + } + + // Check the input. + int ret = yp_schema_check_str(ctx, key0, key1, id, NULL); + if (ret != KNOT_EOK) { + goto get_error; + } + + yp_node_t *node = &ctx->nodes[ctx->current]; + yp_node_t *parent = node->parent; + + // Key1 is not a group identifier. + if (parent != NULL) { + io_reset_val(io, parent->item, node->item, parent->id, + parent->id_len, false, NULL); + // Key1 is a group identifier. + } else if (key1 != NULL && strlen(key1) != 0) { + assert(node->item->type == YP_TGRP && + (node->item->flags & YP_FMULTI) != 0); + + io_reset_val(io, node->item, node->item->var.g.id, node->id, + node->id_len, true, NULL); + // No key1 specified. + } else { + io_reset_val(io, node->item, NULL, node->id, node->id_len, false, + NULL); + } + + knot_db_txn_t *txn = get_current ? &conf()->read_txn : conf()->io.txn; + + // Check for a non-group item. + if (io->key0->type != YP_TGRP) { + ret = KNOT_ENOTSUP; + goto get_error; + } + + // List the section with all identifiers by default. + if ((io->key0->flags & YP_FMULTI) != 0 && io->id_len == 0) { + conf_iter_t iter; + ret = conf_db_iter_begin(conf(), txn, io->key0->name, &iter); + switch (ret) { + case KNOT_EOK: + break; + case KNOT_ENOENT: + ret = KNOT_EOK; + goto get_error; + default: + goto get_error; + } + + while (ret == KNOT_EOK) { + // Set the section identifier. + ret = conf_db_iter_id(conf(), &iter, &io->id, &io->id_len); + if (ret != KNOT_EOK) { + conf_db_iter_finish(conf(), &iter); + goto get_error; + } + + ret = get_section(txn, io); + if (ret != KNOT_EOK) { + conf_db_iter_finish(conf(), &iter); + goto get_error; + } + + ret = conf_db_iter_next(conf(), &iter); + } + + ret = KNOT_EOK; + goto get_error; + } + + // List the section with a possible identifier. + ret = get_section(txn, io); +get_error: + yp_schema_check_deinit(ctx); + + return ret; +} + +static void upd_changes( + const conf_io_t *io, + conf_io_type_t type, + yp_flag_t flags, + bool any_id) +{ + // Update common flags. + conf()->io.flags |= flags; + + // Return if not important change. + if (type == CONF_IO_TNONE) { + return; + } + + // Update reference item. + if (flags & CONF_IO_FREF) { + // Expected an identifier, which cannot be changed. + assert(type != CONF_IO_TCHANGE); + + // Re-check and reload all zones if a reference has been removed. + if (type == CONF_IO_TUNSET) { + conf()->io.flags |= CONF_IO_FCHECK_ZONES | CONF_IO_FRLD_ZONES; + } + return; + // Return if no specific zone operation. + } else if (!(flags & CONF_IO_FZONE)) { + return; + } + + // Don't process each zone individually, process all instead. + if (any_id) { + // Diff all zone changes. + conf()->io.flags |= CONF_IO_FCHECK_ZONES | CONF_IO_FDIFF_ZONES; + + // Reload just with important changes. + if (flags & CONF_IO_FRLD_ZONE) { + conf()->io.flags |= CONF_IO_FRLD_ZONES; + } + return; + } + + // Prepare zone changes storage if it doesn't exist. + trie_t *zones = conf()->io.zones; + if (zones == NULL) { + zones = trie_create(NULL); + if (zones == NULL) { + return; + } + conf()->io.zones = zones; + } + + // Get zone status or create new. + trie_val_t *val = trie_get_ins(zones, (const char *)io->id, io->id_len); + conf_io_type_t *current = (conf_io_type_t *)val; + + switch (type) { + case CONF_IO_TSET: + // Revert remove zone, but don't remove (probably changed). + if (*current & CONF_IO_TUNSET) { + *current &= ~CONF_IO_TUNSET; + } else { + // Must be a new zone. + assert(*current == CONF_IO_TNONE); + // Mark added zone. + *current = type; + } + break; + case CONF_IO_TUNSET: + if (*current & CONF_IO_TSET) { + // Remove inserted zone -> no change. + trie_del(zones, (const char *)io->id, io->id_len, NULL); + } else { + // Remove existing zone. + *current |= type; + } + break; + case CONF_IO_TCHANGE: + *current |= type; + // Mark zone to reload if required. + if (flags & CONF_IO_FRLD_ZONE) { + *current |= CONF_IO_TRELOAD; + } + break; + case CONF_IO_TRELOAD: + default: + assert(0); + } +} + +static int set_item( + conf_io_t *io) +{ + int ret = conf_db_set(conf(), conf()->io.txn, io->key0->name, + (io->key1 != NULL) ? io->key1->name : NULL, + io->id, io->id_len, io->data.bin, io->data.bin_len); + if (ret != KNOT_EOK) { + return ret; + } + + // Postpone group callbacks to config check. + if (io->key0->type == YP_TGRP && io->id_len == 0) { + return KNOT_EOK; + } + + knotd_conf_check_extra_t extra = { + .conf = conf(), + .txn = conf()->io.txn + }; + knotd_conf_check_args_t args = { + .item = (io->key1 != NULL) ? io->key1 : + ((io->id_len == 0) ? io->key0 : io->key0->var.g.id), + .id = io->id, + .id_len = io->id_len, + .data = io->data.bin, + .data_len = io->data.bin_len, + .extra = &extra + }; + + // Call the item callbacks (include, item check, mod-id check). + ret = conf_exec_callbacks(&args); + if (ret != KNOT_EOK) { + CONF_LOG(LOG_DEBUG, "item '%s' (%s)", args.item->name + 1, + args.err_str != NULL ? args.err_str : knot_strerror(ret)); + } + + return ret; +} + +int conf_io_set( + const char *key0, + const char *key1, + const char *id, + const char *data) +{ + assert(conf() != NULL); + + if (conf()->io.txn == NULL) { + return KNOT_TXN_ENOTEXISTS; + } + + // At least key0 must be specified. + if (key0 == NULL) { + return KNOT_EINVAL; + } + + yp_check_ctx_t *ctx = yp_schema_check_init(&conf()->schema); + if (ctx == NULL) { + return KNOT_ENOMEM; + } + + // Check the input. + int ret = yp_schema_check_str(ctx, key0, key1, id, data); + if (ret != KNOT_EOK) { + goto set_error; + } + + yp_node_t *node = &ctx->nodes[ctx->current]; + yp_node_t *parent = node->parent; + + yp_flag_t upd_flags = node->item->flags; + conf_io_type_t upd_type = CONF_IO_TNONE; + + conf_io_t io = { NULL }; + + // Key1 is not a group identifier. + if (parent != NULL) { + if (node->data_len == 0) { + ret = KNOT_YP_ENODATA; + goto set_error; + } + upd_type = CONF_IO_TCHANGE; + upd_flags |= parent->item->flags; + io_reset_bin(&io, parent->item, node->item, parent->id, + parent->id_len, node->data, node->data_len); + // A group identifier or whole group. + } else if (node->item->type == YP_TGRP) { + upd_type = CONF_IO_TSET; + if ((node->item->flags & YP_FMULTI) != 0) { + if (node->id_len == 0) { + ret = KNOT_YP_ENOID; + goto set_error; + } + upd_flags |= node->item->var.g.id->flags; + } else { + ret = KNOT_ENOTSUP; + goto set_error; + } + assert(node->data_len == 0); + io_reset_bin(&io, node->item, NULL, node->id, node->id_len, + NULL, 0); + // A non-group item with data (include). + } else if (node->data_len > 0) { + io_reset_bin(&io, node->item, NULL, NULL, 0, node->data, + node->data_len); + } else { + ret = KNOT_YP_ENODATA; + goto set_error; + } + + // Set the item for all identifiers by default. + if (io.key0->type == YP_TGRP && io.key1 != NULL && + (io.key0->flags & YP_FMULTI) != 0 && io.id_len == 0) { + conf_iter_t iter; + ret = conf_db_iter_begin(conf(), conf()->io.txn, io.key0->name, + &iter); + switch (ret) { + case KNOT_EOK: + break; + case KNOT_ENOENT: + ret = KNOT_EOK; + goto set_error; + default: + goto set_error; + } + + uint8_t copied_id[YP_MAX_ID_LEN]; + io.id = copied_id; + while (ret == KNOT_EOK) { + // Get the identifier and copy it because of next DB update. + const uint8_t *tmp_id; + ret = conf_db_iter_id(conf(), &iter, &tmp_id, &io.id_len); + if (ret != KNOT_EOK) { + conf_db_iter_finish(conf(), &iter); + goto set_error; + } + memcpy(copied_id, tmp_id, io.id_len); + + // Set the data. + ret = set_item(&io); + if (ret != KNOT_EOK) { + conf_db_iter_finish(conf(), &iter); + goto set_error; + } + + ret = conf_db_iter_next(conf(), &iter); + } + if (ret != KNOT_EOF) { + goto set_error; + } + + upd_changes(&io, upd_type, upd_flags, true); + + ret = KNOT_EOK; + goto set_error; + } + + // Set the item with a possible identifier. + ret = set_item(&io); + + if (ret == KNOT_EOK) { + upd_changes(&io, upd_type, upd_flags, false); + } +set_error: + yp_schema_check_deinit(ctx); + + return ret; +} + +static int unset_section_data( + conf_io_t *io) +{ + // Unset the value for the specified item. + if (io->key1 != NULL) { + return conf_db_unset(conf(), conf()->io.txn, io->key0->name, + io->key1->name, io->id, io->id_len, + io->data.bin, io->data.bin_len, false); + } + + // Unset the whole section by default. + for (yp_item_t *i = io->key0->sub_items; i->name != NULL; i++) { + // Skip the identifier item. + if ((io->key0->flags & YP_FMULTI) != 0 && io->key0->var.g.id == i) { + continue; + } + + int ret = conf_db_unset(conf(), conf()->io.txn, io->key0->name, + i->name, io->id, io->id_len, io->data.bin, + io->data.bin_len, false); + switch (ret) { + case KNOT_EOK: + case KNOT_ENOENT: + continue; + default: + return ret; + } + } + + return KNOT_EOK; +} + +static int unset_section( + const yp_item_t *key0) +{ + // Unset the section items. + for (yp_item_t *i = key0->sub_items; i->name != NULL; i++) { + // Skip the identifier item. + if ((key0->flags & YP_FMULTI) != 0 && key0->var.g.id == i) { + continue; + } + + int ret = conf_db_unset(conf(), conf()->io.txn, key0->name, + i->name, NULL, 0, NULL, 0, true); + switch (ret) { + case KNOT_EOK: + case KNOT_ENOENT: + continue; + default: + return ret; + } + } + + // Unset the section. + int ret = conf_db_unset(conf(), conf()->io.txn, key0->name, NULL, NULL, + 0, NULL, 0, false); + switch (ret) { + case KNOT_EOK: + case KNOT_ENOENT: + return KNOT_EOK; + default: + return ret; + } +} + +int conf_io_unset( + const char *key0, + const char *key1, + const char *id, + const char *data) +{ + assert(conf() != NULL); + + if (conf()->io.txn == NULL) { + return KNOT_TXN_ENOTEXISTS; + } + + // Unset all sections by default. + if (key0 == NULL) { + for (yp_item_t *i = conf()->schema; i->name != NULL; i++) { + // Skip non-group item. + if (i->type != YP_TGRP) { + continue; + } + + int ret = conf_io_unset(i->name + 1, key1, NULL, NULL); + switch (ret) { + case KNOT_EOK: + case KNOT_ENOENT: + break; + default: + return ret; + } + } + + return KNOT_EOK; + } + + yp_check_ctx_t *ctx = yp_schema_check_init(&conf()->schema); + if (ctx == NULL) { + return KNOT_ENOMEM; + } + + // Check the input. + int ret = yp_schema_check_str(ctx, key0, key1, id, data); + if (ret != KNOT_EOK) { + goto unset_error; + } + + yp_node_t *node = &ctx->nodes[ctx->current]; + yp_node_t *parent = node->parent; + + yp_flag_t upd_flags = node->item->flags; + conf_io_type_t upd_type = CONF_IO_TNONE; + + conf_io_t io = { NULL }; + + // Key1 is not a group identifier. + if (parent != NULL) { + upd_type = CONF_IO_TCHANGE; + upd_flags |= parent->item->flags; + io_reset_bin(&io, parent->item, node->item, parent->id, + parent->id_len, node->data, node->data_len); + // A group identifier or whole group. + } else if (node->item->type == YP_TGRP) { + upd_type = CONF_IO_TUNSET; + if ((node->item->flags & YP_FMULTI) != 0) { + upd_flags |= node->item->var.g.id->flags; + } + assert(node->data_len == 0); + io_reset_bin(&io, node->item, NULL, node->id, node->id_len, + NULL, 0); + // A non-group item (include). + } else { + ret = KNOT_ENOTSUP; + goto unset_error; + } + + // Unset the section with all identifiers by default. + if ((io.key0->flags & YP_FMULTI) != 0 && io.id_len == 0) { + conf_iter_t iter; + ret = conf_db_iter_begin(conf(), conf()->io.txn, io.key0->name, + &iter); + switch (ret) { + case KNOT_EOK: + break; + case KNOT_ENOENT: + ret = KNOT_EOK; + goto unset_error; + default: + goto unset_error; + } + + uint8_t copied_id[YP_MAX_ID_LEN]; + io.id = copied_id; + while (ret == KNOT_EOK) { + // Get the identifier and copy it because of next DB update. + const uint8_t *tmp_id; + ret = conf_db_iter_id(conf(), &iter, &tmp_id, &io.id_len); + if (ret != KNOT_EOK) { + conf_db_iter_finish(conf(), &iter); + goto unset_error; + } + memcpy(copied_id, tmp_id, io.id_len); + + // Unset the section data. + ret = unset_section_data(&io); + switch (ret) { + case KNOT_EOK: + case KNOT_ENOENT: + break; + default: + conf_db_iter_finish(conf(), &iter); + goto unset_error; + } + + ret = conf_db_iter_next(conf(), &iter); + } + if (ret != KNOT_EOF) { + goto unset_error; + } + + if (io.key1 == NULL) { + // Unset all identifiers. + ret = conf_db_iter_begin(conf(), conf()->io.txn, + io.key0->name, &iter); + switch (ret) { + case KNOT_EOK: + break; + case KNOT_ENOENT: + ret = KNOT_EOK; + goto unset_error; + default: + goto unset_error; + } + + while (ret == KNOT_EOK) { + ret = conf_db_iter_del(conf(), &iter); + if (ret != KNOT_EOK) { + conf_db_iter_finish(conf(), &iter); + goto unset_error; + } + + ret = conf_db_iter_next(conf(), &iter); + } + if (ret != KNOT_EOF) { + goto unset_error; + } + + // Unset the section. + ret = unset_section(io.key0); + if (ret != KNOT_EOK) { + goto unset_error; + } + } + + upd_changes(&io, upd_type, upd_flags, true); + + ret = KNOT_EOK; + goto unset_error; + } + + // Unset the section data. + ret = unset_section_data(&io); + if (ret != KNOT_EOK) { + goto unset_error; + } + + if (io.key1 == NULL) { + // Unset the identifier. + if (io.id_len != 0) { + ret = conf_db_unset(conf(), conf()->io.txn, io.key0->name, + NULL, io.id, io.id_len, NULL, 0, false); + if (ret != KNOT_EOK) { + goto unset_error; + } + // Unset the section. + } else { + ret = unset_section(io.key0); + if (ret != KNOT_EOK) { + goto unset_error; + } + } + } + + if (ret == KNOT_EOK) { + upd_changes(&io, upd_type, upd_flags, false); + } +unset_error: + yp_schema_check_deinit(ctx); + + return ret; +} + +static int check_section( + const yp_item_t *group, + const uint8_t *id, + size_t id_len, + conf_io_t *io) +{ + knotd_conf_check_extra_t extra = { + .conf = conf(), + .txn = conf()->io.txn, + .check = true + }; + knotd_conf_check_args_t args = { + .id = id, + .id_len = id_len, + .extra = &extra + }; + + bool non_empty = false; + + for (yp_item_t *item = group->sub_items; item->name != NULL; item++) { + args.item = item; + + // Check the identifier. + if ((group->flags & YP_FMULTI) != 0 && group->var.g.id == item) { + io->error.code = conf_exec_callbacks(&args); + if (io->error.code != KNOT_EOK) { + io_reset_val(io, group, item, NULL, 0, false, NULL); + goto check_section_error; + } + continue; + } + + // Get the item value. + conf_val_t bin; + conf_db_get(conf(), conf()->io.txn, group->name, item->name, id, + id_len, &bin); + if (bin.code == KNOT_ENOENT) { + continue; + } else if (bin.code != KNOT_EOK) { + return bin.code; + } + + non_empty = true; + + // Check the item value(s). + size_t values = conf_val_count(&bin); + for (size_t i = 1; i <= values; i++) { + conf_val(&bin); + args.data = bin.data; + args.data_len = bin.len; + + io->error.code = conf_exec_callbacks(&args); + if (io->error.code != KNOT_EOK) { + io_reset_val(io, group, item, id, id_len, false, + &bin); + io->data.index = i; + goto check_section_error; + } + + if (values > 1) { + conf_val_next(&bin); + } + } + } + + // Check the whole section if not empty. + if (id != NULL || non_empty) { + args.item = group; + args.data = NULL; + args.data_len = 0; + + io->error.code = conf_exec_callbacks(&args); + if (io->error.code != KNOT_EOK) { + io_reset_val(io, group, NULL, id, id_len, false, NULL); + goto check_section_error; + } + } + + return KNOT_EOK; + +check_section_error: + io->error.str = args.err_str; + int ret = FCN(io); + if (ret == KNOT_EOK) { + return io->error.code; + } + return ret; +} + +static int check_iter_section( + const yp_item_t *item, + conf_io_t *io) +{ + // Iterate over all identifiers. + conf_iter_t iter; + int ret = conf_db_iter_begin(conf(), conf()->io.txn, item->name, &iter); + switch (ret) { + case KNOT_EOK: + break; + case KNOT_ENOENT: + return KNOT_EOK; + default: + return ret; + } + + while (ret == KNOT_EOK) { + size_t id_len; + const uint8_t *id; + ret = conf_db_iter_id(conf(), &iter, &id, &id_len); + if (ret != KNOT_EOK) { + conf_db_iter_finish(conf(), &iter); + return ret; + } + + // Check specific section item. + ret = check_section(item, id, id_len, io); + if (ret != KNOT_EOK) { + conf_db_iter_finish(conf(), &iter); + return ret; + } + + ret = conf_db_iter_next(conf(), &iter); + } + if (ret != KNOT_EOF) { + return ret; + } + + return KNOT_EOK; +} + +static int check_zone_section( + const yp_item_t *item, + conf_io_t *io) +{ + assert(item->flags & CONF_IO_FZONE); + + if (conf()->io.zones == NULL) { + return KNOT_EOK; + } + + trie_it_t *it = trie_it_begin(conf()->io.zones); + for (; !trie_it_finished(it); trie_it_next(it)) { + size_t id_len; + const uint8_t *id = (const uint8_t *)trie_it_key(it, &id_len); + + conf_io_type_t type = (conf_io_type_t)(*trie_it_val(it)); + if (type == CONF_IO_TUNSET) { + // Nothing to check. + continue; + } + + // Check specific zone. + int ret = check_section(item, id, id_len, io); + if (ret != KNOT_EOK) { + trie_it_free(it); + return ret; + } + } + trie_it_free(it); + + return KNOT_EOK; +} + +int conf_io_check( + conf_io_t *io) +{ + if (io == NULL) { + return KNOT_EINVAL; + } + + assert(conf() != NULL); + + if (conf()->io.txn == NULL) { + return KNOT_TXN_ENOTEXISTS; + } + + int ret; + + // Iterate over the schema. + for (yp_item_t *item = conf()->schema; item->name != NULL; item++) { + // Skip non-group items (include). + if (item->type != YP_TGRP) { + continue; + } + + // Check simple group without identifiers. + if ((item->flags & YP_FMULTI) == 0) { + ret = check_section(item, NULL, 0, io); + if (ret != KNOT_EOK) { + goto check_error; + } + continue; + } + + // The zone section has an optimized check. + if (item->flags & CONF_IO_FZONE) { + // Full check by default. + if (!(conf()->io.flags & CONF_IO_FACTIVE)) { + ret = check_iter_section(item, io); + // Full check if all zones changed. + } else if (conf()->io.flags & CONF_IO_FCHECK_ZONES) { + ret = check_iter_section(item, io); + // Optimized check for specific zones. + } else { + ret = check_zone_section(item, io); + } + } else { + ret = check_iter_section(item, io); + } + if (ret != KNOT_EOK) { + goto check_error; + } + } + + ret = KNOT_EOK; +check_error: + conf_mod_load_purge(conf(), true); + + return ret; +} diff --git a/src/knot/conf/confio.h b/src/knot/conf/confio.h new file mode 100644 index 0000000..460bfbd --- /dev/null +++ b/src/knot/conf/confio.h @@ -0,0 +1,218 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include "knot/conf/conf.h" + +/*! Configuration schema additional flags for dynamic changes. */ +#define CONF_IO_FACTIVE YP_FUSR1 /*!< Active confio transaction indicator. */ +#define CONF_IO_FZONE YP_FUSR2 /*!< Zone section indicator. */ +#define CONF_IO_FREF YP_FUSR3 /*!< Possibly referenced id from a zone. */ +#define CONF_IO_FDIFF_ZONES YP_FUSR4 /*!< All zones config has changed. */ +#define CONF_IO_FCHECK_ZONES YP_FUSR5 /*!< All zones config needs to check. */ +#define CONF_IO_FRLD_SRV YP_FUSR6 /*!< Reload server. */ +#define CONF_IO_FRLD_LOG YP_FUSR7 /*!< Reload logging. */ +#define CONF_IO_FRLD_MOD YP_FUSR8 /*!< Reload global modules. */ +#define CONF_IO_FRLD_ZONE YP_FUSR9 /*!< Reload a specific zone. */ +#define CONF_IO_FRLD_ZONES YP_FUSR10 /*!< Reload all zones. */ +#define CONF_IO_FRLD_ALL (CONF_IO_FRLD_SRV | CONF_IO_FRLD_LOG | \ + CONF_IO_FRLD_MOD | CONF_IO_FRLD_ZONES) + +/*! Zone configuration change type. */ +typedef enum { + CONF_IO_TNONE = 0, /*!< Unspecified. */ + CONF_IO_TSET = 1 << 0, /*!< Zone added. */ + CONF_IO_TUNSET = 1 << 1, /*!< Zone removed. */ + CONF_IO_TCHANGE = 1 << 2, /*!< Zone has changed configuration. */ + CONF_IO_TRELOAD = 1 << 3, /*!< Zone must be reloaded. */ +} conf_io_type_t; + +/*! Configuration interface output. */ +typedef struct conf_io conf_io_t; +struct conf_io { + /*! Section. */ + const yp_item_t *key0; + /*! Section item. */ + const yp_item_t *key1; + /*! Section identifier. */ + const uint8_t *id; + /*! Section identifier length. */ + size_t id_len; + /*! Consider item identifier as item data. */ + bool id_as_data; + + enum { + /*! Default item state. */ + NONE, + /*! New item indicator. */ + NEW, + /*! Old item indicator. */ + OLD + } type; + + struct { + /*! Section item data (NULL if not used). */ + conf_val_t *val; + /*! Index of data value to format (counted from 1, 0 means all). */ + size_t index; + /*! Binary data value (NULL if not used). */ + const uint8_t *bin; + /*! Length of the binary data value. */ + size_t bin_len; + } data; + + struct { + /*! Edit operation return code. */ + int code; + /*! Edit operation return error message. */ + const char *str; + } error; + + /*! Optional processing callback. */ + int (*fcn)(conf_io_t *); + /*! Miscellaneous data useful for the callback. */ + void *misc; +}; + +/*! + * Starts new writing transaction. + * + * \param[in] child Nested transaction indicator. + * + * \return Error code, KNOT_EOK if success. + */ +int conf_io_begin( + bool child +); + +/*! + * Commits the current writing transaction. + * + * \note Remember to call conf_refresh to publish the changes into the common + * configuration. + * + * \param[in] child Nested transaction indicator. + * + * \return Error code, KNOT_EOK if success. + */ +int conf_io_commit( + bool child +); + +/*! + * Aborts the current writing transaction. + * + * \param[in] child Nested transaction indicator. + */ +void conf_io_abort( + bool child +); + +/*! + * Gets the configuration sections list or section items list. + * + * \param[in] key0 Section name (NULL to get section list). + * \param[out] io Operation output. + * + * \return Error code, KNOT_EOK if success. + */ +int conf_io_list( + const char *key0, + conf_io_t *io +); + +/*! + * Gets the configuration difference between the current configuration and + * the active transaction. + * + * \param[in] key0 Section name (NULL to diff all sections). + * \param[in] key1 Item name (NULL to diff all section items). + * \param[in] id Section identifier name (NULL to consider all section identifiers). + * \param[out] io Operation output. + * + * \return Error code, KNOT_EOK if success. + */ +int conf_io_diff( + const char *key0, + const char *key1, + const char *id, + conf_io_t *io +); + +/*! + * Gets the configuration item(s) value(s). + * + * \param[in] key0 Section name (NULL to get all sections). + * \param[in] key1 Item name (NULL to get all section items). + * \param[in] id Section identifier name (NULL to consider all section identifiers). + * \param[in] get_current The current configuration or the active transaction switch. + * \param[out] io Operation output. + * + * \return Error code, KNOT_EOK if success. + */ +int conf_io_get( + const char *key0, + const char *key1, + const char *id, + bool get_current, + conf_io_t *io +); + +/*! + * Sets the configuration item(s) value. + * + * \param[in] key0 Section name. + * \param[in] key1 Item name (NULL to add identifier only). + * \param[in] id Section identifier name (NULL to consider all section identifiers). + * \param[in] data Item data to set/add. + * + * \return Error code, KNOT_EOK if success. + */ +int conf_io_set( + const char *key0, + const char *key1, + const char *id, + const char *data +); + +/*! + * Unsets the configuration item(s) value(s). + * + * \param[in] key0 Section name (NULL to unset all sections). + * \param[in] key1 Item name (NULL to unset the whole section). + * \param[in] id Section identifier name (NULL to consider all section identifiers). + * \param[in] data Item data (NULL to unset all data). + * + * \return Error code, KNOT_EOK if success. + */ +int conf_io_unset( + const char *key0, + const char *key1, + const char *id, + const char *data +); + +/*! + * Checks the configuration database semantics in the current writing transaction. + * + * \param[out] io Operation output. + * + * \return Error code, KNOT_EOK if success. + */ +int conf_io_check( + conf_io_t *io +); diff --git a/src/knot/conf/migration.c b/src/knot/conf/migration.c new file mode 100644 index 0000000..a15edae --- /dev/null +++ b/src/knot/conf/migration.c @@ -0,0 +1,81 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "knot/common/log.h" +#include "knot/conf/migration.h" +#include "knot/conf/confdb.h" + +/* +static void try_unset(conf_t *conf, knot_db_txn_t *txn, yp_name_t *key0, yp_name_t *key1) +{ + int ret = conf_db_unset(conf, txn, key0, key1, NULL, 0, NULL, 0, true); + if (ret != KNOT_EOK && ret != KNOT_ENOENT) { + log_warning("conf, migration, failed to unset '%s%s%s' (%s)", + key0 + 1, + (key1 != NULL) ? "/" : "", + (key1 != NULL) ? key1 + 1 : "", + knot_strerror(ret)); + } +} + +#define check_set(conf, txn, key0, key1, id, id_len, data, data_len) \ + ret = conf_db_set(conf, txn, key0, key1, id, id_len, data, data_len); \ + if (ret != KNOT_EOK && ret != KNOT_CONF_EREDEFINE) { \ + log_error("conf, migration, failed to set '%s%s%s' (%s)", \ + key0 + 1, \ + (key1 != NULL) ? "/" : "", \ + (key1 != NULL) ? key1 + 1 : "", \ + knot_strerror(ret)); \ + return ret; \ + } + +static int migrate_( + conf_t *conf, + knot_db_txn_t *txn) +{ + return KNOT_EOK; +} +*/ + +int conf_migrate( + conf_t *conf) +{ + return KNOT_EOK; + /* + if (conf == NULL) { + return KNOT_EINVAL; + } + + knot_db_txn_t txn; + int ret = conf->api->txn_begin(conf->db, &txn, 0); + if (ret != KNOT_EOK) { + return ret; + } + + ret = migrate_(conf, &txn); + if (ret != KNOT_EOK) { + conf->api->txn_abort(&txn); + return ret; + } + + ret = conf->api->txn_commit(&txn); + if (ret != KNOT_EOK) { + return ret; + } + + return conf_refresh_txn(conf); + */ +} diff --git a/src/knot/conf/migration.h b/src/knot/conf/migration.h new file mode 100644 index 0000000..c885ff4 --- /dev/null +++ b/src/knot/conf/migration.h @@ -0,0 +1,30 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include "knot/conf/base.h" + +/*! + * Migrates from an old configuration schema. + * + * \param[in] conf Configuration. + * + * \return Error code, KNOT_EOK if success. + */ +int conf_migrate( + conf_t *conf +); diff --git a/src/knot/conf/module.c b/src/knot/conf/module.c new file mode 100644 index 0000000..6bb013f --- /dev/null +++ b/src/knot/conf/module.c @@ -0,0 +1,460 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <dlfcn.h> +#include <fcntl.h> +#include <glob.h> +#include <sys/stat.h> +#include <unistd.h> +#include <urcu.h> + +#include "knot/conf/conf.h" +#include "knot/conf/confio.h" +#include "knot/conf/module.h" +#include "knot/common/log.h" +#include "knot/modules/static_modules.h" +#include "knot/nameserver/query_module.h" +#include "contrib/openbsd/strlcat.h" +#include "contrib/string.h" + +#define LIB_EXTENSION ".so" + +dynarray_define(mod, module_t *, DYNARRAY_VISIBILITY_PUBLIC) +dynarray_define(old_schema, yp_item_t *, DYNARRAY_VISIBILITY_PUBLIC) + +static module_t STATIC_MODULES[] = { + STATIC_MODULES_INIT + { NULL } +}; + +module_t *conf_mod_find( + conf_t *conf, + const char *name, + size_t len, + bool temporary) +{ + if (conf == NULL || name == NULL) { + return NULL; + } + + // First, search in static modules. + for (module_t *mod = STATIC_MODULES; mod->api != NULL; mod++) { + if (strncmp(name, mod->api->name, len) == 0) { + return mod; + } + } + + // Second, search in dynamic modules. + dynarray_foreach(mod, module_t *, module, conf->modules) { + if ((*module) != NULL && (*module)->temporary == temporary && + strncmp(name, (*module)->api->name, len) == 0) { + return (*module); + } + } + + return NULL; +} + +static int mod_load( + conf_t *conf, + module_t *mod) +{ + static const yp_item_t module_common[] = { + { C_ID, YP_TSTR, YP_VNONE, CONF_IO_FREF }, + { C_COMMENT, YP_TSTR, YP_VNONE }, + { NULL } + }; + + yp_item_t *sub_items = NULL; + + int ret; + if (mod->api->config != NULL) { + ret = yp_schema_merge(&sub_items, module_common, mod->api->config); + } else { + ret = yp_schema_copy(&sub_items, module_common); + } + if (ret != KNOT_EOK) { + return ret; + } + + /* Synthesise module config section name. */ + const size_t name_len = strlen(mod->api->name); + if (name_len > YP_MAX_ITEM_NAME_LEN) { + return KNOT_YP_EINVAL_ITEM; + } + char name[1 + YP_MAX_ITEM_NAME_LEN + 1]; + name[0] = name_len; + memcpy(name + 1, mod->api->name, name_len + 1); + + const yp_item_t schema[] = { + { name, YP_TGRP, YP_VGRP = { sub_items }, + YP_FALLOC | YP_FMULTI | CONF_IO_FRLD_MOD | CONF_IO_FRLD_ZONES, + { mod->api->config_check } }, + { NULL } + }; + + yp_item_t *merged = NULL; + ret = yp_schema_merge(&merged, conf->schema, schema); + yp_schema_free(sub_items); + if (ret != KNOT_EOK) { + return ret; + } + + // Update configuration schema (with lazy free). + yp_item_t **current_schema = &conf->schema; + yp_item_t *old_schema = rcu_xchg_pointer(current_schema, merged); + synchronize_rcu(); + old_schema_dynarray_add(&conf->old_schemas, &old_schema); + + return KNOT_EOK; +} + +int conf_mod_load_common( + conf_t *conf) +{ + if (conf == NULL) { + return KNOT_EINVAL; + } + + int ret = KNOT_EOK; + + // First, load static modules. + for (module_t *mod = STATIC_MODULES; mod->api != NULL; mod++) { + ret = mod_load(conf, mod); + if (ret != KNOT_EOK) { + break; + } + + log_debug("module '%s', loaded static", mod->api->name); + } + + // Second, try to load implicit shared modules if configured. + if (strlen(MODULE_DIR) > 0) { + struct stat path_stat; + glob_t glob_buf = { 0 }; + + char *path = sprintf_alloc("%s/*%s", MODULE_DIR, LIB_EXTENSION); + if (path == NULL) { + ret = KNOT_ENOMEM; + } else if (stat(MODULE_DIR, &path_stat) != 0 || + !S_ISDIR(path_stat.st_mode)) { + if (errno == ENOENT) { + // Module directory doesn't exist. + ret = KNOT_EOK; + } else { + log_error("module, invalid directory '%s'", + MODULE_DIR); + ret = KNOT_EINVAL; + } + } else if (access(MODULE_DIR, F_OK | R_OK) != 0) { + log_error("module, failed to access directory '%s'", + MODULE_DIR); + ret = KNOT_EACCES; + } else { + ret = glob(path, 0, NULL, &glob_buf); + if (ret != 0 && ret != GLOB_NOMATCH) { + log_error("module, failed to read directory '%s'", + MODULE_DIR); + ret = KNOT_EACCES; + } else { + ret = KNOT_EOK; + } + } + + // Process each module in the directory. + for (size_t i = 0; i < glob_buf.gl_pathc; i++) { + (void)conf_mod_load_extra(conf, NULL, glob_buf.gl_pathv[i], + false); + } + + globfree(&glob_buf); + free(path); + } + + conf_mod_load_purge(conf, false); + + return ret; +} + +int conf_mod_load_extra( + conf_t *conf, + const char *mod_name, + const char *file_name, + bool temporary) +{ + if (conf == NULL || (mod_name == NULL && file_name == NULL)) { + return KNOT_EINVAL; + } + + // Synthesize module file name if not specified. + char *tmp_name = NULL; + if (file_name == NULL) { + tmp_name = sprintf_alloc("%s/%s%s", MODULE_INSTDIR, + mod_name + strlen(KNOTD_MOD_NAME_PREFIX), + LIB_EXTENSION); + if (tmp_name == NULL) { + return KNOT_ENOMEM; + } + file_name = tmp_name; + } + + void *handle = dlopen(file_name, RTLD_NOW | RTLD_LOCAL); + if (handle == NULL) { + log_error("module, failed to open '%s' (%s)", file_name, dlerror()); + free(tmp_name); + return KNOT_ENOENT; + } + (void)dlerror(); + + knotd_mod_api_t *api = dlsym(handle, "knotd_mod_api"); + if (api == NULL) { + char *err = dlerror(); + if (err == NULL) { + err = "empty symbol"; + } + log_error("module, invalid library '%s' (%s)", file_name, err); + dlclose(handle); + free(tmp_name); + return KNOT_ENOENT; + } + free(tmp_name); + + if (api->version != KNOTD_MOD_ABI_VERSION) { + log_error("module '%s', incompatible version", api->name); + dlclose(handle); + return KNOT_ENOTSUP; + } + + if (api->name == NULL || (mod_name != NULL && strcmp(api->name, mod_name) != 0)) { + log_error("module '%s', module name mismatch", api->name); + dlclose(handle); + return KNOT_ENOTSUP; + } + + // Check if the module is already loaded. + module_t *found = conf_mod_find(conf, api->name, strlen(api->name), temporary); + if (found != NULL) { + log_error("module '%s', duplicate module", api->name); + dlclose(handle); + return KNOT_EEXIST; + } + + module_t *mod = calloc(1, sizeof(*mod)); + if (mod == NULL) { + dlclose(handle); + return KNOT_ENOMEM; + } + mod->api = api; + mod->lib_handle = handle; + mod->temporary = temporary; + + int ret = mod_load(conf, mod); + if (ret != KNOT_EOK) { + log_error("module '%s', failed to load (%s)", api->name, + knot_strerror(ret)); + dlclose(handle); + free(mod); + return ret; + } + + mod_dynarray_add(&conf->modules, &mod); + + log_debug("module '%s', loaded shared", api->name); + + return KNOT_EOK; +} + +static void unload_shared( + module_t *mod) +{ + if (mod != NULL) { + assert(mod->lib_handle); + (void)dlclose(mod->lib_handle); + free(mod); + } +} + +void conf_mod_load_purge( + conf_t *conf, + bool temporary) +{ + if (conf == NULL) { + return; + } + + // Switch the current temporary schema with the initial one. + if (temporary && conf->old_schemas.size > 0) { + yp_item_t **current_schema = &conf->schema; + yp_item_t **initial = &(conf->old_schemas.arr(&conf->old_schemas))[0]; + + yp_item_t *old_schema = rcu_xchg_pointer(current_schema, *initial); + synchronize_rcu(); + *initial = old_schema; + } + + dynarray_foreach(old_schema, yp_item_t *, schema, conf->old_schemas) { + yp_schema_free(*schema); + } + old_schema_dynarray_free(&conf->old_schemas); + + dynarray_foreach(mod, module_t *, module, conf->modules) { + if ((*module) != NULL && (*module)->temporary) { + unload_shared((*module)); + *module = NULL; // Cannot remove from dynarray. + } + } +} + +void conf_mod_unload_shared( + conf_t *conf) +{ + if (conf == NULL) { + return; + } + + dynarray_foreach(mod, module_t *, module, conf->modules) { + unload_shared((*module)); + } + mod_dynarray_free(&conf->modules); +} + +#define LOG_ARGS(mod_id, msg) "module '%s%s%.*s', " msg, \ + mod_id->name + 1, (mod_id->len > 0) ? "/" : "", (int)mod_id->len, \ + mod_id->data + +#define MOD_ID_LOG(zone, level, mod_id, msg, ...) \ + if (zone != NULL) \ + log_zone_##level(zone, LOG_ARGS(mod_id, msg), ##__VA_ARGS__); \ + else \ + log_##level(LOG_ARGS(mod_id, msg), ##__VA_ARGS__); + +void conf_activate_modules( + conf_t *conf, + const knot_dname_t *zone_name, + list_t *query_modules, + struct query_plan **query_plan) +{ + int ret = KNOT_EOK; + + if (conf == NULL || query_modules == NULL || query_plan == NULL) { + ret = KNOT_EINVAL; + goto activate_error; + } + + conf_val_t val; + + // Get list of associated modules. + if (zone_name != NULL) { + val = conf_zone_get(conf, C_MODULE, zone_name); + } else { + val = conf_default_get(conf, C_GLOBAL_MODULE); + } + + switch (val.code) { + case KNOT_EOK: + break; + case KNOT_ENOENT: // Check if a module is configured at all. + case KNOT_YP_EINVAL_ID: + return; + default: + ret = val.code; + goto activate_error; + } + + // Create query plan. + *query_plan = query_plan_create(); + if (*query_plan == NULL) { + ret = KNOT_ENOMEM; + goto activate_error; + } + + // Initialize query modules list. + init_list(query_modules); + + // Open the modules. + while (val.code == KNOT_EOK) { + conf_mod_id_t *mod_id = conf_mod_id(&val); + if (mod_id == NULL) { + ret = KNOT_ENOMEM; + goto activate_error; + } + + // Open the module. + knotd_mod_t *mod = query_module_open(conf, mod_id, *query_plan, + zone_name); + if (mod == NULL) { + MOD_ID_LOG(zone_name, error, mod_id, "failed to open"); + conf_free_mod_id(mod_id); + goto skip_module; + } + + // Check the module scope. + if ((zone_name == NULL && !(mod->api->flags & KNOTD_MOD_FLAG_SCOPE_GLOBAL)) || + (zone_name != NULL && !(mod->api->flags & KNOTD_MOD_FLAG_SCOPE_ZONE))) { + MOD_ID_LOG(zone_name, error, mod_id, "out of scope"); + query_module_close(mod); + goto skip_module; + } + + // Check if the module is loadable. + if (mod->api->load == NULL) { + MOD_ID_LOG(zone_name, debug, mod_id, "empty module, not loaded"); + query_module_close(mod); + goto skip_module; + } + + // Load the module. + ret = mod->api->load(mod); + if (ret != KNOT_EOK) { + MOD_ID_LOG(zone_name, error, mod_id, "failed to load (%s)", + knot_strerror(ret)); + query_module_close(mod); + goto skip_module; + } + mod->config = NULL; // Invalidate the current config. + + add_tail(query_modules, &mod->node); +skip_module: + conf_val_next(&val); + } + + return; +activate_error: + CONF_LOG(LOG_ERR, "failed to activate modules (%s)", knot_strerror(ret)); +} + +void conf_deactivate_modules( + list_t *query_modules, + struct query_plan **query_plan) +{ + if (query_modules == NULL || query_plan == NULL) { + return; + } + + // Free query plan. + query_plan_free(*query_plan); + *query_plan = NULL; + + // Free query modules list. + knotd_mod_t *mod = NULL, *next = NULL; + WALK_LIST_DELSAFE(mod, next, *query_modules) { + if (mod->api->unload != NULL) { + mod->api->unload(mod); + } + query_module_close(mod); + } + init_list(query_modules); +} diff --git a/src/knot/conf/module.h b/src/knot/conf/module.h new file mode 100644 index 0000000..2dc353a --- /dev/null +++ b/src/knot/conf/module.h @@ -0,0 +1,110 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include "knot/conf/base.h" + +/*! + * Finds specific module in static or dynamic modules. + * + * \param[in] conf Configuration. + * \param[in] name Module name. + * \param[in] len Module name length. + * \param[in] temporary Find only a temporary module indication. + * + * \return Module, NULL if not found. + */ +module_t *conf_mod_find( + conf_t *conf, + const char *name, + size_t len, + bool temporary +); + +/*! + * Loads common static and shared modules. + * + * \param[in] conf Configuration. + * + * \return Error code, KNOT_EOK if success. + */ +int conf_mod_load_common( + conf_t *conf +); + +/*! + * Loads extra shared module. + * + * \param[in] conf Configuration. + * \param[in] mod_name Module name. + * \param[in] file_name Shared library file name. + * \param[in] temporary Mark module as temporary. + * + * \return Error code, KNOT_EOK if success. + */ +int conf_mod_load_extra( + conf_t *conf, + const char *mod_name, + const char *file_name, + bool temporary +); + +/*! + * Purges temporary schemas and modules after all modules loading. + * + * \param[in] conf Configuration. + * \param[in] temporary Purge only temporary modules indication. + */ +void conf_mod_load_purge( + conf_t *conf, + bool temporary +); + +/*! + * Unloads all shared modules. + * + * \param[in] conf Configuration. + */ +void conf_mod_unload_shared( + conf_t *conf +); + +/*! + * Activates configured query modules for the specified zone or for all zones. + * + * \param[in] conf Configuration. + * \param[in] zone_name Zone name, NULL for all zones. + * \param[in] query_modules Destination query modules list. + * \param[in] query_plan Destination query plan. + */ +void conf_activate_modules( + conf_t *conf, + const knot_dname_t *zone_name, + list_t *query_modules, + struct query_plan **query_plan +); + +/*! + * Deactivates query modules list. + * + * \param[in] query_modules Destination query modules list. + * \param[in] query_plan Destination query plan. + */ +void conf_deactivate_modules( + list_t *query_modules, + struct query_plan **query_plan +); diff --git a/src/knot/conf/schema.c b/src/knot/conf/schema.c new file mode 100644 index 0000000..560f005 --- /dev/null +++ b/src/knot/conf/schema.c @@ -0,0 +1,344 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <limits.h> +#include <stdbool.h> +#include <stdlib.h> +#include <stdint.h> + +#include "knot/conf/schema.h" +#include "knot/conf/confio.h" +#include "knot/conf/tools.h" +#include "knot/common/log.h" +#include "knot/journal/journal.h" +#include "knot/updates/acl.h" +#include "libknot/rrtype/opt.h" +#include "libdnssec/tsig.h" +#include "libdnssec/key.h" + +#define HOURS(x) ((x) * 3600) +#define DAYS(x) ((x) * HOURS(24)) + +#define KILO(x) (1024LLU * (x)) +#define MEGA(x) (KILO(1024) * (x)) +#define GIGA(x) (MEGA(1024) * (x)) +#define TERA(x) (GIGA(1024) * (x)) + +#define VIRT_MEM_TOP_32BIT GIGA(1) +#define VIRT_MEM_LIMIT(x) (((sizeof(void *) < 8) && ((x) > VIRT_MEM_TOP_32BIT)) \ + ? VIRT_MEM_TOP_32BIT : (x)) + +static const knot_lookup_t keystore_backends[] = { + { KEYSTORE_BACKEND_PEM, "pem" }, + { KEYSTORE_BACKEND_PKCS11, "pkcs11" }, + { 0, NULL } +}; + +static const knot_lookup_t tsig_key_algs[] = { + { DNSSEC_TSIG_HMAC_MD5, "hmac-md5" }, + { DNSSEC_TSIG_HMAC_SHA1, "hmac-sha1" }, + { DNSSEC_TSIG_HMAC_SHA224, "hmac-sha224" }, + { DNSSEC_TSIG_HMAC_SHA256, "hmac-sha256" }, + { DNSSEC_TSIG_HMAC_SHA384, "hmac-sha384" }, + { DNSSEC_TSIG_HMAC_SHA512, "hmac-sha512" }, + { 0, NULL } +}; + +static const knot_lookup_t dnssec_key_algs[] = { + { DNSSEC_KEY_ALGORITHM_RSA_SHA1, "rsasha1" }, + { DNSSEC_KEY_ALGORITHM_RSA_SHA1_NSEC3, "rsasha1-nsec3-sha1" }, + { DNSSEC_KEY_ALGORITHM_RSA_SHA256, "rsasha256" }, + { DNSSEC_KEY_ALGORITHM_RSA_SHA512, "rsasha512" }, + { DNSSEC_KEY_ALGORITHM_ECDSA_P256_SHA256, "ecdsap256sha256" }, + { DNSSEC_KEY_ALGORITHM_ECDSA_P384_SHA384, "ecdsap384sha384" }, + { DNSSEC_KEY_ALGORITHM_ED25519, "ed25519" }, + /* Obsolete items. */ + { 3, "dsa" }, + { 6, "dsa-nsec3-sha1" }, + { 0, NULL } +}; + +const knot_lookup_t child_record[] = { + { CHILD_RECORDS_NONE, "none" }, + { CHILD_RECORDS_EMPTY, "delete-dnssec" }, + { CHILD_RECORDS_ROLLOVER, "rollover" }, + { CHILD_RECORDS_ALWAYS, "always" }, + { 0, NULL } +}; + +const knot_lookup_t acl_actions[] = { + { ACL_ACTION_NOTIFY, "notify" }, + { ACL_ACTION_TRANSFER, "transfer" }, + { ACL_ACTION_UPDATE, "update" }, + { 0, NULL } +}; + +static const knot_lookup_t serial_policies[] = { + { SERIAL_POLICY_INCREMENT, "increment" }, + { SERIAL_POLICY_UNIXTIME, "unixtime" }, + { SERIAL_POLICY_DATESERIAL, "dateserial" }, + { 0, NULL } +}; + +static const knot_lookup_t journal_content[] = { + { JOURNAL_CONTENT_NONE, "none" }, + { JOURNAL_CONTENT_CHANGES, "changes" }, + { JOURNAL_CONTENT_ALL, "all" }, + { 0, NULL } +}; + +static const knot_lookup_t zonefile_load[] = { + { ZONEFILE_LOAD_NONE, "none" }, + { ZONEFILE_LOAD_DIFF, "difference" }, + { ZONEFILE_LOAD_DIFSE, "difference-no-serial" }, + { ZONEFILE_LOAD_WHOLE, "whole" }, + { 0, NULL } +}; + +static const knot_lookup_t log_severities[] = { + { LOG_UPTO(LOG_CRIT), "critical" }, + { LOG_UPTO(LOG_ERR), "error" }, + { LOG_UPTO(LOG_WARNING), "warning" }, + { LOG_UPTO(LOG_NOTICE), "notice" }, + { LOG_UPTO(LOG_INFO), "info" }, + { LOG_UPTO(LOG_DEBUG), "debug" }, + { 0, NULL } +}; + +static const knot_lookup_t journal_modes[] = { + { JOURNAL_MODE_ROBUST, "robust" }, + { JOURNAL_MODE_ASYNC, "asynchronous" }, + { 0, NULL } +}; + +static const yp_item_t desc_module[] = { + { C_ID, YP_TSTR, YP_VNONE, YP_FNONE, { check_module_id } }, + { C_FILE, YP_TSTR, YP_VNONE }, + { C_COMMENT, YP_TSTR, YP_VNONE }, + { NULL } +}; + +static const yp_item_t desc_server[] = { + { C_IDENT, YP_TSTR, YP_VNONE }, + { C_VERSION, YP_TSTR, YP_VNONE }, + { C_NSID, YP_THEX, YP_VNONE }, + { C_RUNDIR, YP_TSTR, YP_VSTR = { RUN_DIR } }, + { C_USER, YP_TSTR, YP_VNONE }, + { C_PIDFILE, YP_TSTR, YP_VSTR = { "knot.pid" } }, + { C_UDP_WORKERS, YP_TINT, YP_VINT = { 1, 255, YP_NIL } }, + { C_TCP_WORKERS, YP_TINT, YP_VINT = { 1, 255, YP_NIL } }, + { C_BG_WORKERS, YP_TINT, YP_VINT = { 1, 255, YP_NIL } }, + { C_ASYNC_START, YP_TBOOL, YP_VNONE }, + { C_TCP_HSHAKE_TIMEOUT, YP_TINT, YP_VINT = { 0, INT32_MAX, 5, YP_STIME } }, + { C_TCP_IDLE_TIMEOUT, YP_TINT, YP_VINT = { 0, INT32_MAX, 20, YP_STIME } }, + { C_TCP_REPLY_TIMEOUT, YP_TINT, YP_VINT = { 0, INT32_MAX, 10, YP_STIME } }, + { C_MAX_TCP_CLIENTS, YP_TINT, YP_VINT = { 0, INT32_MAX, 100 } }, + { C_MAX_UDP_PAYLOAD, YP_TINT, YP_VINT = { KNOT_EDNS_MIN_DNSSEC_PAYLOAD, + KNOT_EDNS_MAX_UDP_PAYLOAD, + KNOT_EDNS_MAX_UDP_PAYLOAD, YP_SSIZE } }, + { C_MAX_IPV4_UDP_PAYLOAD, YP_TINT, YP_VINT = { KNOT_EDNS_MIN_DNSSEC_PAYLOAD, + KNOT_EDNS_MAX_UDP_PAYLOAD, + KNOT_EDNS_MAX_UDP_PAYLOAD, YP_SSIZE } }, + { C_MAX_IPV6_UDP_PAYLOAD, YP_TINT, YP_VINT = { KNOT_EDNS_MIN_DNSSEC_PAYLOAD, + KNOT_EDNS_MAX_UDP_PAYLOAD, + KNOT_EDNS_MAX_UDP_PAYLOAD, YP_SSIZE } }, + { C_LISTEN, YP_TADDR, YP_VADDR = { 53 }, YP_FMULTI }, + { C_COMMENT, YP_TSTR, YP_VNONE }, + { C_ECS, YP_TBOOL, YP_VNONE }, + { C_ANS_ROTATION, YP_TBOOL, YP_VNONE }, + { NULL } +}; + +static const yp_item_t desc_control[] = { + { C_LISTEN, YP_TSTR, YP_VSTR = { "knot.sock" } }, + { C_TIMEOUT, YP_TINT, YP_VINT = { 0, INT32_MAX / 1000, 5, YP_STIME } }, + { C_COMMENT, YP_TSTR, YP_VNONE }, + { NULL } +}; + +static const yp_item_t desc_log[] = { + { C_TARGET, YP_TSTR, YP_VNONE }, + { C_SERVER, YP_TOPT, YP_VOPT = { log_severities, 0 } }, + { C_CTL, YP_TOPT, YP_VOPT = { log_severities, 0 } }, + { C_ZONE, YP_TOPT, YP_VOPT = { log_severities, 0 } }, + { C_ANY, YP_TOPT, YP_VOPT = { log_severities, 0 } }, + { C_COMMENT, YP_TSTR, YP_VNONE }, + { NULL } +}; + +static const yp_item_t desc_stats[] = { + { C_TIMER, YP_TINT, YP_VINT = { 1, UINT32_MAX, 0, YP_STIME } }, + { C_FILE, YP_TSTR, YP_VSTR = { "stats.yaml" } }, + { C_APPEND, YP_TBOOL, YP_VNONE }, + { NULL } +}; + +static const yp_item_t desc_keystore[] = { + { C_ID, YP_TSTR, YP_VNONE }, + { C_BACKEND, YP_TOPT, YP_VOPT = { keystore_backends, KEYSTORE_BACKEND_PEM }, + CONF_IO_FRLD_ZONES }, + { C_CONFIG, YP_TSTR, YP_VSTR = { "keys" }, CONF_IO_FRLD_ZONES }, + { C_COMMENT, YP_TSTR, YP_VNONE }, + { NULL } +}; + +static const yp_item_t desc_key[] = { + { C_ID, YP_TDNAME, YP_VNONE }, + { C_ALG, YP_TOPT, YP_VOPT = { tsig_key_algs, DNSSEC_TSIG_UNKNOWN } }, + { C_SECRET, YP_TB64, YP_VNONE }, + { C_COMMENT, YP_TSTR, YP_VNONE }, + { NULL } +}; + +static const yp_item_t desc_acl[] = { + { C_ID, YP_TSTR, YP_VNONE, CONF_IO_FREF }, + { C_ADDR, YP_TNET, YP_VNONE, YP_FMULTI }, + { C_KEY, YP_TREF, YP_VREF = { C_KEY }, YP_FMULTI, { check_ref } }, + { C_ACTION, YP_TOPT, YP_VOPT = { acl_actions, ACL_ACTION_NONE }, YP_FMULTI }, + { C_DENY, YP_TBOOL, YP_VNONE }, + { C_COMMENT, YP_TSTR, YP_VNONE }, + { NULL } +}; + +static const yp_item_t desc_remote[] = { + { C_ID, YP_TSTR, YP_VNONE, CONF_IO_FREF }, + { C_ADDR, YP_TADDR, YP_VADDR = { 53 }, YP_FMULTI }, + { C_VIA, YP_TADDR, YP_VNONE, YP_FMULTI }, + { C_KEY, YP_TREF, YP_VREF = { C_KEY }, YP_FNONE, { check_ref } }, + { C_COMMENT, YP_TSTR, YP_VNONE }, + { NULL } +}; + +static const yp_item_t desc_submission[] = { + { C_ID, YP_TSTR, YP_VNONE }, + { C_PARENT, YP_TREF, YP_VREF = { C_RMT }, YP_FMULTI | CONF_IO_FRLD_ZONES, + { check_ref } }, + { C_CHK_INTERVAL, YP_TINT, YP_VINT = { 1, UINT32_MAX, HOURS(1), YP_STIME }, + CONF_IO_FRLD_ZONES }, + { C_TIMEOUT, YP_TINT, YP_VINT = { 1, UINT32_MAX, 0, YP_STIME }, + CONF_IO_FRLD_ZONES }, + { NULL } +}; + +static const yp_item_t desc_policy[] = { + { C_ID, YP_TSTR, YP_VNONE, CONF_IO_FREF }, + { C_KEYSTORE, YP_TREF, YP_VREF = { C_KEYSTORE }, CONF_IO_FRLD_ZONES, + { check_ref_dflt } }, + { C_MANUAL, YP_TBOOL, YP_VNONE, CONF_IO_FRLD_ZONES }, + { C_KSK_SHARED, YP_TBOOL, YP_VNONE, CONF_IO_FRLD_ZONES }, + { C_SINGLE_TYPE_SIGNING, YP_TBOOL, YP_VNONE, CONF_IO_FRLD_ZONES }, + { C_ALG, YP_TOPT, YP_VOPT = { dnssec_key_algs, + DNSSEC_KEY_ALGORITHM_ECDSA_P256_SHA256 }, + CONF_IO_FRLD_ZONES }, + { C_KSK_SIZE, YP_TINT, YP_VINT = { 0, UINT16_MAX, YP_NIL, YP_SSIZE }, + CONF_IO_FRLD_ZONES }, + { C_ZSK_SIZE, YP_TINT, YP_VINT = { 0, UINT16_MAX, YP_NIL, YP_SSIZE }, + CONF_IO_FRLD_ZONES }, + { C_DNSKEY_TTL, YP_TINT, YP_VINT = { 0, UINT32_MAX, YP_NIL, YP_STIME }, + CONF_IO_FRLD_ZONES }, + { C_ZSK_LIFETIME, YP_TINT, YP_VINT = { 0, UINT32_MAX, DAYS(30), YP_STIME }, + CONF_IO_FRLD_ZONES }, + { C_KSK_LIFETIME, YP_TINT, YP_VINT = { 0, UINT32_MAX, 0, YP_STIME }, + CONF_IO_FRLD_ZONES }, + { C_PROPAG_DELAY, YP_TINT, YP_VINT = { 0, UINT32_MAX, HOURS(1), YP_STIME }, + CONF_IO_FRLD_ZONES }, + { C_RRSIG_LIFETIME, YP_TINT, YP_VINT = { 1, UINT32_MAX, DAYS(14), YP_STIME }, + CONF_IO_FRLD_ZONES }, + { C_RRSIG_REFRESH, YP_TINT, YP_VINT = { 1, UINT32_MAX, DAYS(7), YP_STIME }, + CONF_IO_FRLD_ZONES }, + { C_NSEC3, YP_TBOOL, YP_VNONE, CONF_IO_FRLD_ZONES }, + { C_NSEC3_ITER, YP_TINT, YP_VINT = { 0, UINT16_MAX, 10 }, CONF_IO_FRLD_ZONES }, + { C_NSEC3_OPT_OUT, YP_TBOOL, YP_VNONE, CONF_IO_FRLD_ZONES }, + { C_NSEC3_SALT_LEN, YP_TINT, YP_VINT = { 0, UINT8_MAX, 8 }, CONF_IO_FRLD_ZONES }, + { C_NSEC3_SALT_LIFETIME, YP_TINT, YP_VINT = { 1, UINT32_MAX, DAYS(30), YP_STIME }, + CONF_IO_FRLD_ZONES }, + { C_KSK_SBM, YP_TREF, YP_VREF = { C_SBM }, CONF_IO_FRLD_ZONES, + { check_ref } }, + { C_CHILD_RECORDS, YP_TOPT, YP_VOPT = { child_record, CHILD_RECORDS_ALWAYS } }, + { C_COMMENT, YP_TSTR, YP_VNONE }, + { NULL } +}; + +#define ZONE_ITEMS(FLAGS) \ + { C_STORAGE, YP_TSTR, YP_VSTR = { STORAGE_DIR }, FLAGS }, \ + { C_FILE, YP_TSTR, YP_VNONE, FLAGS }, \ + { C_MASTER, YP_TREF, YP_VREF = { C_RMT }, YP_FMULTI, { check_ref } }, \ + { C_DDNS_MASTER, YP_TREF, YP_VREF = { C_RMT }, YP_FNONE, { check_ref } }, \ + { C_NOTIFY, YP_TREF, YP_VREF = { C_RMT }, YP_FMULTI, { check_ref } }, \ + { C_ACL, YP_TREF, YP_VREF = { C_ACL }, YP_FMULTI, { check_ref } }, \ + { C_SEM_CHECKS, YP_TBOOL, YP_VNONE, FLAGS }, \ + { C_DISABLE_ANY, YP_TBOOL, YP_VNONE }, \ + { C_ZONEFILE_SYNC, YP_TINT, YP_VINT = { -1, INT32_MAX, 0, YP_STIME } }, \ + { C_JOURNAL_CONTENT, YP_TOPT, YP_VOPT = { journal_content, JOURNAL_CONTENT_CHANGES } }, \ + { C_ZONEFILE_LOAD, YP_TOPT, YP_VOPT = { zonefile_load, ZONEFILE_LOAD_WHOLE } }, \ + { C_MAX_ZONE_SIZE, YP_TINT, YP_VINT = { 0, SSIZE_MAX, SSIZE_MAX, YP_SSIZE }, FLAGS }, \ + { C_MAX_JOURNAL_USAGE, YP_TINT, YP_VINT = { KILO(40), SSIZE_MAX, MEGA(100), YP_SSIZE } }, \ + { C_MAX_JOURNAL_DEPTH, YP_TINT, YP_VINT = { 2, SSIZE_MAX, SSIZE_MAX } }, \ + { C_DNSSEC_SIGNING, YP_TBOOL, YP_VNONE, FLAGS }, \ + { C_DNSSEC_POLICY, YP_TREF, YP_VREF = { C_POLICY }, FLAGS, { check_ref_dflt } }, \ + { C_SERIAL_POLICY, YP_TOPT, YP_VOPT = { serial_policies, SERIAL_POLICY_INCREMENT } }, \ + { C_REQUEST_EDNS_OPTION, YP_TDATA, YP_VDATA = { 0, NULL, edns_opt_to_bin, edns_opt_to_txt } }, \ + { C_MAX_REFRESH_INTERVAL,YP_TINT, YP_VINT = { 2, UINT32_MAX, UINT32_MAX, YP_STIME } }, \ + { C_MIN_REFRESH_INTERVAL,YP_TINT, YP_VINT = { 2, UINT32_MAX, 2, YP_STIME } }, \ + { C_MODULE, YP_TDATA, YP_VDATA = { 0, NULL, mod_id_to_bin, mod_id_to_txt }, \ + YP_FMULTI | FLAGS, { check_modref } }, \ + { C_COMMENT, YP_TSTR, YP_VNONE }, \ + +static const yp_item_t desc_template[] = { + { C_ID, YP_TSTR, YP_VNONE, CONF_IO_FREF }, + ZONE_ITEMS(CONF_IO_FRLD_ZONES) + { C_GLOBAL_MODULE, YP_TDATA, YP_VDATA = { 0, NULL, mod_id_to_bin, mod_id_to_txt }, + YP_FMULTI | CONF_IO_FRLD_MOD, { check_modref } }, + { C_TIMER_DB, YP_TSTR, YP_VSTR = { "timers" }, CONF_IO_FRLD_ZONES }, + { C_MAX_TIMER_DB_SIZE, YP_TINT, YP_VINT = { MEGA(1), VIRT_MEM_LIMIT(GIGA(100)), + MEGA(100), YP_SSIZE }, CONF_IO_FRLD_ZONES }, + { C_JOURNAL_DB, YP_TSTR, YP_VSTR = { "journal" }, CONF_IO_FRLD_SRV }, + { C_JOURNAL_DB_MODE, YP_TOPT, YP_VOPT = { journal_modes, JOURNAL_MODE_ROBUST }, + CONF_IO_FRLD_SRV }, + { C_MAX_JOURNAL_DB_SIZE, YP_TINT, YP_VINT = { JOURNAL_MIN_FSLIMIT, VIRT_MEM_LIMIT(TERA(100)), + VIRT_MEM_LIMIT(GIGA(20)), YP_SSIZE }, + CONF_IO_FRLD_SRV }, + { C_KASP_DB, YP_TSTR, YP_VSTR = { "keys" }, CONF_IO_FRLD_SRV }, + { C_MAX_KASP_DB_SIZE, YP_TINT, YP_VINT = { MEGA(5), VIRT_MEM_LIMIT(GIGA(100)), + MEGA(500), YP_SSIZE }, CONF_IO_FRLD_SRV }, + { NULL } +}; + +static const yp_item_t desc_zone[] = { + { C_DOMAIN, YP_TDNAME, YP_VNONE, CONF_IO_FRLD_ZONE }, + { C_TPL, YP_TREF, YP_VREF = { C_TPL }, CONF_IO_FRLD_ZONE, { check_ref } }, + ZONE_ITEMS(CONF_IO_FRLD_ZONE) + { NULL } +}; + +const yp_item_t conf_schema[] = { + { C_MODULE, YP_TGRP, YP_VGRP = { desc_module }, YP_FMULTI | CONF_IO_FRLD_ALL | + CONF_IO_FCHECK_ZONES, { load_module } }, + { C_SRV, YP_TGRP, YP_VGRP = { desc_server }, CONF_IO_FRLD_SRV, { check_server } }, + { C_CTL, YP_TGRP, YP_VGRP = { desc_control } }, + { C_LOG, YP_TGRP, YP_VGRP = { desc_log }, YP_FMULTI | CONF_IO_FRLD_LOG }, + { C_STATS, YP_TGRP, YP_VGRP = { desc_stats }, CONF_IO_FRLD_SRV }, + { C_KEYSTORE, YP_TGRP, YP_VGRP = { desc_keystore }, YP_FMULTI, { check_keystore } }, + { C_KEY, YP_TGRP, YP_VGRP = { desc_key }, YP_FMULTI, { check_key } }, + { C_ACL, YP_TGRP, YP_VGRP = { desc_acl }, YP_FMULTI, { check_acl } }, + { C_RMT, YP_TGRP, YP_VGRP = { desc_remote }, YP_FMULTI, { check_remote } }, + { C_SBM, YP_TGRP, YP_VGRP = { desc_submission }, YP_FMULTI }, + { C_POLICY, YP_TGRP, YP_VGRP = { desc_policy }, YP_FMULTI, { check_policy } }, + { C_TPL, YP_TGRP, YP_VGRP = { desc_template }, YP_FMULTI, { check_template } }, + { C_ZONE, YP_TGRP, YP_VGRP = { desc_zone }, YP_FMULTI | CONF_IO_FZONE, { check_zone } }, + { C_INCL, YP_TSTR, YP_VNONE, CONF_IO_FDIFF_ZONES | CONF_IO_FRLD_ALL, { include_file } }, + { NULL } +}; diff --git a/src/knot/conf/schema.h b/src/knot/conf/schema.h new file mode 100644 index 0000000..8ba6941 --- /dev/null +++ b/src/knot/conf/schema.h @@ -0,0 +1,154 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include "libknot/lookup.h" +#include "libknot/yparser/ypschema.h" + +#define C_ACL "\x03""acl" +#define C_ACTION "\x06""action" +#define C_ADDR "\x07""address" +#define C_ALG "\x09""algorithm" +#define C_ANS_ROTATION "\x0F""answer-rotation" +#define C_ANY "\x03""any" +#define C_APPEND "\x06""append" +#define C_ASYNC_START "\x0B""async-start" +#define C_BACKEND "\x07""backend" +#define C_BG_WORKERS "\x12""background-workers" +#define C_CHILD_RECORDS "\x13""cds-cdnskey-publish" +#define C_CHK_INTERVAL "\x0E""check-interval" +#define C_COMMENT "\x07""comment" +#define C_CONFIG "\x06""config" +#define C_CTL "\x07""control" +#define C_DDNS_MASTER "\x0B""ddns-master" +#define C_DENY "\x04""deny" +#define C_DISABLE_ANY "\x0B""disable-any" +#define C_DNSKEY_TTL "\x0A""dnskey-ttl" +#define C_DNSSEC_POLICY "\x0D""dnssec-policy" +#define C_DNSSEC_SIGNING "\x0E""dnssec-signing" +#define C_DOMAIN "\x06""domain" +#define C_ECS "\x12""edns-client-subnet" +#define C_FILE "\x04""file" +#define C_GLOBAL_MODULE "\x0D""global-module" +#define C_ID "\x02""id" +#define C_IDENT "\x08""identity" +#define C_INCL "\x07""include" +#define C_JOURNAL_CONTENT "\x0F""journal-content" +#define C_JOURNAL_DB "\x0A""journal-db" +#define C_JOURNAL_DB_MODE "\x0F""journal-db-mode" +#define C_KASP_DB "\x07""kasp-db" +#define C_KEY "\x03""key" +#define C_KEYSTORE "\x08""keystore" +#define C_KSK_LIFETIME "\x0C""ksk-lifetime" +#define C_KSK_SBM "\x0E""ksk-submission" +#define C_KSK_SHARED "\x0a""ksk-shared" +#define C_KSK_SIZE "\x08""ksk-size" +#define C_LISTEN "\x06""listen" +#define C_LOG "\x03""log" +#define C_MANUAL "\x06""manual" +#define C_MASTER "\x06""master" +#define C_MAX_IPV4_UDP_PAYLOAD "\x14""max-ipv4-udp-payload" +#define C_MAX_IPV6_UDP_PAYLOAD "\x14""max-ipv6-udp-payload" +#define C_MAX_JOURNAL_DB_SIZE "\x13""max-journal-db-size" +#define C_MAX_JOURNAL_DEPTH "\x11""max-journal-depth" +#define C_MAX_JOURNAL_USAGE "\x11""max-journal-usage" +#define C_MAX_KASP_DB_SIZE "\x10""max-kasp-db-size" +#define C_MAX_REFRESH_INTERVAL "\x14""max-refresh-interval" +#define C_MAX_TCP_CLIENTS "\x0F""max-tcp-clients" +#define C_MAX_TIMER_DB_SIZE "\x11""max-timer-db-size" +#define C_MAX_UDP_PAYLOAD "\x0F""max-udp-payload" +#define C_MAX_ZONE_SIZE "\x0D""max-zone-size" +#define C_MIN_REFRESH_INTERVAL "\x14""min-refresh-interval" +#define C_MODULE "\x06""module" +#define C_NOTIFY "\x06""notify" +#define C_NSEC3 "\x05""nsec3" +#define C_NSEC3_ITER "\x10""nsec3-iterations" +#define C_NSEC3_OPT_OUT "\x0D""nsec3-opt-out" +#define C_NSEC3_SALT_LEN "\x11""nsec3-salt-length" +#define C_NSEC3_SALT_LIFETIME "\x13""nsec3-salt-lifetime" +#define C_NSID "\x04""nsid" +#define C_PARENT "\x06""parent" +#define C_PIDFILE "\x07""pidfile" +#define C_POLICY "\x06""policy" +#define C_PROPAG_DELAY "\x11""propagation-delay" +#define C_REQUEST_EDNS_OPTION "\x13""request-edns-option" +#define C_RMT "\x06""remote" +#define C_RRSIG_LIFETIME "\x0E""rrsig-lifetime" +#define C_RRSIG_REFRESH "\x0D""rrsig-refresh" +#define C_RUNDIR "\x06""rundir" +#define C_SBM "\x0A""submission" +#define C_SECRET "\x06""secret" +#define C_SEM_CHECKS "\x0F""semantic-checks" +#define C_SERIAL_POLICY "\x0D""serial-policy" +#define C_SERVER "\x06""server" +#define C_SINGLE_TYPE_SIGNING "\x13""single-type-signing" +#define C_SRV "\x06""server" +#define C_STATS "\x0A""statistics" +#define C_STORAGE "\x07""storage" +#define C_TARGET "\x06""target" +#define C_TCP_HSHAKE_TIMEOUT "\x15""tcp-handshake-timeout" +#define C_TCP_IDLE_TIMEOUT "\x10""tcp-idle-timeout" +#define C_TCP_REPLY_TIMEOUT "\x11""tcp-reply-timeout" +#define C_TCP_WORKERS "\x0B""tcp-workers" +#define C_TIMEOUT "\x07""timeout" +#define C_TIMER "\x05""timer" +#define C_TIMER_DB "\x08""timer-db" +#define C_TPL "\x08""template" +#define C_UDP_WORKERS "\x0B""udp-workers" +#define C_USER "\x04""user" +#define C_VERSION "\x07""version" +#define C_VIA "\x03""via" +#define C_ZONE "\x04""zone" +#define C_ZONEFILE_LOAD "\x0D""zonefile-load" +#define C_ZONEFILE_SYNC "\x0D""zonefile-sync" +#define C_ZSK_LIFETIME "\x0C""zsk-lifetime" +#define C_ZSK_SIZE "\x08""zsk-size" + +enum { + KEYSTORE_BACKEND_PEM = 1, + KEYSTORE_BACKEND_PKCS11 = 2, +}; + +enum { + CHILD_RECORDS_NONE = 0, + CHILD_RECORDS_EMPTY = 1, + CHILD_RECORDS_ROLLOVER = 2, + CHILD_RECORDS_ALWAYS = 3, +}; + +enum { + SERIAL_POLICY_INCREMENT = 1, + SERIAL_POLICY_UNIXTIME = 2, + SERIAL_POLICY_DATESERIAL = 3, +}; + +enum { + JOURNAL_CONTENT_NONE = 0, + JOURNAL_CONTENT_CHANGES = 1, + JOURNAL_CONTENT_ALL = 2, +}; + +enum { + ZONEFILE_LOAD_NONE = 0, + ZONEFILE_LOAD_DIFF = 1, + ZONEFILE_LOAD_WHOLE = 2, + ZONEFILE_LOAD_DIFSE = 3, +}; + +extern const knot_lookup_t acl_actions[]; + +extern const yp_item_t conf_schema[]; diff --git a/src/knot/conf/tools.c b/src/knot/conf/tools.c new file mode 100644 index 0000000..3ab365e --- /dev/null +++ b/src/knot/conf/tools.c @@ -0,0 +1,551 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <glob.h> +#include <inttypes.h> +#include <libgen.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <unistd.h> + +#include "libdnssec/key.h" +#include "knot/conf/tools.h" +#include "knot/conf/conf.h" +#include "knot/conf/module.h" +#include "knot/conf/schema.h" +#include "knot/common/log.h" +#include "libknot/errcode.h" +#include "libknot/yparser/yptrafo.h" +#include "contrib/string.h" +#include "contrib/wire_ctx.h" + +#define MAX_INCLUDE_DEPTH 5 + +static bool is_default_id( + const uint8_t *id, + size_t id_len) +{ + return id_len == CONF_DEFAULT_ID[0] && + memcmp(id, CONF_DEFAULT_ID + 1, id_len) == 0; +} + +int conf_exec_callbacks( + knotd_conf_check_args_t *args) +{ + if (args == NULL) { + return KNOT_EINVAL; + } + + for (size_t i = 0; i < YP_MAX_MISC_COUNT; i++) { + int (*fcn)(knotd_conf_check_args_t *) = args->item->misc[i]; + if (fcn == NULL) { + break; + } + + int ret = fcn(args); + if (ret != KNOT_EOK) { + return ret; + } + } + + return KNOT_EOK; +} + +int mod_id_to_bin( + YP_TXT_BIN_PARAMS) +{ + YP_CHECK_PARAMS_BIN; + + // Check for "mod_name/mod_id" format. + const uint8_t *pos = (uint8_t *)strchr((char *)in->position, '/'); + if (pos == in->position) { + // Missing module name. + return KNOT_EINVAL; + } else if (pos >= stop - 1) { + // Missing module identifier after slash. + return KNOT_EINVAL; + } + + // Write mod_name in the yp_name_t format. + uint8_t name_len = (pos != NULL) ? (pos - in->position) : + wire_ctx_available(in); + wire_ctx_write_u8(out, name_len); + wire_ctx_write(out, in->position, name_len); + wire_ctx_skip(in, name_len); + + // Check for mod_id. + if (pos != NULL) { + // Skip the separator. + wire_ctx_skip(in, sizeof(uint8_t)); + + // Write mod_id as a zero terminated string. + int ret = yp_str_to_bin(in, out, stop); + if (ret != KNOT_EOK) { + return ret; + } + } + + YP_CHECK_RET; +} + +int mod_id_to_txt( + YP_BIN_TXT_PARAMS) +{ + YP_CHECK_PARAMS_TXT; + + // Write mod_name. + uint8_t name_len = wire_ctx_read_u8(in); + wire_ctx_write(out, in->position, name_len); + wire_ctx_skip(in, name_len); + + // Check for mod_id. + if (wire_ctx_available(in) > 0) { + // Write the separator. + wire_ctx_write_u8(out, '/'); + + // Write mod_id. + int ret = yp_str_to_txt(in, out); + if (ret != KNOT_EOK) { + return ret; + } + } + + YP_CHECK_RET; +} + +int edns_opt_to_bin( + YP_TXT_BIN_PARAMS) +{ + YP_CHECK_PARAMS_BIN; + + // Check for "code:[value]" format. + const uint8_t *pos = (uint8_t *)strchr((char *)in->position, ':'); + if (pos == NULL || pos >= stop) { + return KNOT_EINVAL; + } + + // Write option code. + int ret = yp_int_to_bin(in, out, pos, 0, UINT16_MAX, YP_SNONE); + if (ret != KNOT_EOK) { + return ret; + } + + // Skip the separator. + wire_ctx_skip(in, sizeof(uint8_t)); + + // Write option data. + ret = yp_hex_to_bin(in, out, stop); + if (ret != KNOT_EOK) { + return ret; + } + + YP_CHECK_RET; +} + +int edns_opt_to_txt( + YP_BIN_TXT_PARAMS) +{ + YP_CHECK_PARAMS_TXT; + + // Write option code. + int ret = yp_int_to_txt(in, out, YP_SNONE); + if (ret != KNOT_EOK) { + return ret; + } + + // Write the separator. + wire_ctx_write_u8(out, ':'); + + // Write option data. + ret = yp_hex_to_txt(in, out); + if (ret != KNOT_EOK) { + return ret; + } + + YP_CHECK_RET; +} + +int check_ref( + knotd_conf_check_args_t *args) +{ + const yp_item_t *ref = args->item->var.r.ref; + + // Try to find a referenced block with the id. + if (!conf_rawid_exists_txn(args->extra->conf, args->extra->txn, ref->name, + args->data, args->data_len)) { + args->err_str = "invalid reference"; + return KNOT_ENOENT; + } + + return KNOT_EOK; +} + +int check_ref_dflt( + knotd_conf_check_args_t *args) +{ + if (check_ref(args) != KNOT_EOK && !is_default_id(args->data, args->data_len)) { + args->err_str = "invalid reference"; + return KNOT_ENOENT; + } + + return KNOT_EOK; +} + +int check_modref( + knotd_conf_check_args_t *args) +{ + const yp_name_t *mod_name = (const yp_name_t *)args->data; + const uint8_t *id = args->data + 1 + args->data[0]; + size_t id_len = args->data_len - 1 - args->data[0]; + + // Check if the module is ever available. + const module_t *mod = conf_mod_find(args->extra->conf, mod_name + 1, + mod_name[0], args->extra->check); + if (mod == NULL) { + args->err_str = "unknown module"; + return KNOT_EINVAL; + } + + // Check if the module requires some configuration. + if (id_len == 0) { + if (mod->api->flags & KNOTD_MOD_FLAG_OPT_CONF) { + return KNOT_EOK; + } else { + args->err_str = "missing module configuration"; + return KNOT_YP_ENOID; + } + } + + // Try to find a module with the id. + if (!conf_rawid_exists_txn(args->extra->conf, args->extra->txn, mod_name, + id, id_len)) { + args->err_str = "invalid module reference"; + return KNOT_ENOENT; + } + + return KNOT_EOK; +} + +int check_module_id( + knotd_conf_check_args_t *args) +{ + const size_t len = strlen(KNOTD_MOD_NAME_PREFIX); + + if (strncmp((const char *)args->id, KNOTD_MOD_NAME_PREFIX, len) != 0) { + args->err_str = "required 'mod-' prefix"; + return KNOT_EINVAL; + } + + return KNOT_EOK; +} + +int check_server( + knotd_conf_check_args_t *args) +{ + return KNOT_EOK; +} + +int check_keystore( + knotd_conf_check_args_t *args) +{ + conf_val_t backend = conf_rawid_get_txn(args->extra->conf, args->extra->txn, C_KEYSTORE, + C_BACKEND, args->id, args->id_len); + conf_val_t config = conf_rawid_get_txn(args->extra->conf, args->extra->txn, C_KEYSTORE, + C_CONFIG, args->id, args->id_len); + + if (conf_opt(&backend) == KEYSTORE_BACKEND_PKCS11 && conf_str(&config) == NULL) { + args->err_str = "no PKCS #11 configuration defined"; + return KNOT_EINVAL; + } + + return KNOT_EOK; +} + +int check_policy( + knotd_conf_check_args_t *args) +{ + conf_val_t alg = conf_rawid_get_txn(args->extra->conf, args->extra->txn, C_POLICY, + C_ALG, args->id, args->id_len); + conf_val_t ksk = conf_rawid_get_txn(args->extra->conf, args->extra->txn, C_POLICY, + C_KSK_SIZE, args->id, args->id_len); + conf_val_t zsk = conf_rawid_get_txn(args->extra->conf, args->extra->txn, C_POLICY, + C_ZSK_SIZE, args->id, args->id_len); + conf_val_t lifetime = conf_rawid_get_txn(args->extra->conf, args->extra->txn, C_POLICY, + C_RRSIG_LIFETIME, args->id, args->id_len); + conf_val_t refresh = conf_rawid_get_txn(args->extra->conf, args->extra->txn, C_POLICY, + C_RRSIG_REFRESH, args->id, args->id_len); + + conf_val_t prop_del = conf_rawid_get_txn(args->extra->conf, args->extra->txn, C_POLICY, + C_PROPAG_DELAY, args->id, args->id_len); + conf_val_t zsk_life = conf_rawid_get_txn(args->extra->conf, args->extra->txn, C_POLICY, + C_ZSK_LIFETIME, args->id, args->id_len); + conf_val_t ksk_life = conf_rawid_get_txn(args->extra->conf, args->extra->txn, C_POLICY, + C_KSK_LIFETIME, args->id, args->id_len); + conf_val_t dnskey_ttl = conf_rawid_get_txn(args->extra->conf, args->extra->txn, C_POLICY, + C_DNSKEY_TTL, args->id, args->id_len); + + unsigned algorithm = conf_opt(&alg); + if (algorithm == 3 || algorithm == 6) { + args->err_str = "DSA algorithm no longer supported"; + return KNOT_EINVAL; + } + + int64_t ksk_size = conf_int(&ksk); + if (ksk_size != YP_NIL && !dnssec_algorithm_key_size_check(algorithm, ksk_size)) { + args->err_str = "KSK key size not compatible with the algorithm"; + return KNOT_EINVAL; + } + + int64_t zsk_size = conf_int(&zsk); + if (zsk_size != YP_NIL && !dnssec_algorithm_key_size_check(algorithm, zsk_size)) { + args->err_str = "ZSK key size not compatible with the algorithm"; + return KNOT_EINVAL; + } + + int64_t lifetime_val = conf_int(&lifetime); + int64_t refresh_val = conf_int(&refresh); + if (lifetime_val <= refresh_val) { + args->err_str = "RRSIG refresh has to be lower than RRSIG lifetime"; + return KNOT_EINVAL; + } + + int64_t prop_del_val = conf_int(&prop_del); + int64_t zsk_life_val = conf_int(&zsk_life); + int64_t ksk_life_val = conf_int(&ksk_life); + int64_t dnskey_ttl_val = conf_int(&dnskey_ttl); + if (dnskey_ttl_val == YP_NIL) { + dnskey_ttl_val = 0; + } + + if (zsk_life_val != 0 && zsk_life_val < 2 * prop_del_val + dnskey_ttl_val) { + args->err_str = "ZSK lifetime too low according to propagation delay and DNSKEY TTL"; + return KNOT_EINVAL; + } + if (ksk_life_val != 0 && ksk_life_val < 2 * prop_del_val + 2 * dnskey_ttl_val) { + args->err_str = "KSK lifetime too low according to propagation delay and DNSKEY TTL"; + return KNOT_EINVAL; + } + + return KNOT_EOK; +} + +int check_key( + knotd_conf_check_args_t *args) +{ + conf_val_t alg = conf_rawid_get_txn(args->extra->conf, args->extra->txn, C_KEY, + C_ALG, args->id, args->id_len); + if (conf_val_count(&alg) == 0) { + args->err_str = "no key algorithm defined"; + return KNOT_EINVAL; + } + + conf_val_t secret = conf_rawid_get_txn(args->extra->conf, args->extra->txn, C_KEY, + C_SECRET, args->id, args->id_len); + if (conf_val_count(&secret) == 0) { + args->err_str = "no key secret defined"; + return KNOT_EINVAL; + } + + return KNOT_EOK; +} + +int check_acl( + knotd_conf_check_args_t *args) +{ + conf_val_t action = conf_rawid_get_txn(args->extra->conf, args->extra->txn, C_ACL, + C_ACTION, args->id, args->id_len); + conf_val_t deny = conf_rawid_get_txn(args->extra->conf, args->extra->txn, C_ACL, + C_DENY, args->id, args->id_len); + if (conf_val_count(&action) == 0 && conf_val_count(&deny) == 0) { + args->err_str = "no ACL action defined"; + return KNOT_EINVAL; + } + + return KNOT_EOK; +} + +int check_remote( + knotd_conf_check_args_t *args) +{ + conf_val_t addr = conf_rawid_get_txn(args->extra->conf, args->extra->txn, C_RMT, + C_ADDR, args->id, args->id_len); + if (conf_val_count(&addr) == 0) { + args->err_str = "no remote address defined"; + return KNOT_EINVAL; + } + + return KNOT_EOK; +} + +int check_template( + knotd_conf_check_args_t *args) +{ + // Stop if the default template. + if (is_default_id(args->id, args->id_len)) { + return KNOT_EOK; + } + + conf_val_t val; + #define CHECK_DFLT(item, name) \ + val = conf_rawid_get_txn(args->extra->conf, args->extra->txn, C_TPL, \ + item, args->id, args->id_len); \ + if (val.code == KNOT_EOK) { \ + args->err_str = name " in non-default template"; \ + return KNOT_EINVAL; \ + } + + CHECK_DFLT(C_GLOBAL_MODULE, "global module"); + CHECK_DFLT(C_TIMER_DB, "timer database path"); + CHECK_DFLT(C_MAX_TIMER_DB_SIZE, "timer database maximum size"); + CHECK_DFLT(C_JOURNAL_DB, "journal database path"); + CHECK_DFLT(C_JOURNAL_DB_MODE, "journal database mode"); + CHECK_DFLT(C_MAX_JOURNAL_DB_SIZE, "journal database maximum size"); + CHECK_DFLT(C_KASP_DB, "KASP database path"); + CHECK_DFLT(C_MAX_KASP_DB_SIZE, "KASP database maximum size"); + + return KNOT_EOK; +} + +int check_zone( + knotd_conf_check_args_t *args) +{ + return KNOT_EOK; +} + +static int glob_error( + const char *epath, + int eerrno) +{ + CONF_LOG(LOG_WARNING, "failed to access '%s' (%s)", epath, + knot_strerror(knot_map_errno_code(eerrno))); + + return 0; +} + +int include_file( + knotd_conf_check_args_t *args) +{ + if (args->data_len == 0) { + return KNOT_YP_ENODATA; + } + + // This function should not be called in more threads. + static int depth = 0; + glob_t glob_buf = { 0 }; + char *path = NULL; + int ret; + + // Check for include loop. + if (depth++ > MAX_INCLUDE_DEPTH) { + CONF_LOG(LOG_ERR, "include loop detected"); + ret = KNOT_EPARSEFAIL; + goto include_error; + } + + // Prepare absolute include path. + if (args->data[0] == '/') { + path = sprintf_alloc("%.*s", (int)args->data_len, args->data); + } else { + const char *file_name = args->extra->file_name != NULL ? + args->extra->file_name : "./"; + char *full_current_name = realpath(file_name, NULL); + if (full_current_name == NULL) { + ret = KNOT_ENOMEM; + goto include_error; + } + + path = sprintf_alloc("%s/%.*s", dirname(full_current_name), + (int)args->data_len, args->data); + free(full_current_name); + } + if (path == NULL) { + ret = KNOT_ESPACE; + goto include_error; + } + + // Evaluate include pattern (empty wildcard match is also valid). + ret = glob(path, 0, glob_error, &glob_buf); + if (ret != 0 && (ret != GLOB_NOMATCH || strchr(path, '*') == NULL)) { + ret = KNOT_EFILE; + goto include_error; + } + + // Process glob result. + for (size_t i = 0; i < glob_buf.gl_pathc; i++) { + // Get file status. + struct stat file_stat; + if (stat(glob_buf.gl_pathv[i], &file_stat) != 0) { + CONF_LOG(LOG_WARNING, "failed to get file status for '%s'", + glob_buf.gl_pathv[i]); + continue; + } + + // Ignore directory or non-regular file. + if (S_ISDIR(file_stat.st_mode)) { + continue; + } else if (!S_ISREG(file_stat.st_mode)) { + CONF_LOG(LOG_WARNING, "invalid include file '%s'", + glob_buf.gl_pathv[i]); + continue; + } + + // Include regular file. + ret = conf_parse(args->extra->conf, args->extra->txn, + glob_buf.gl_pathv[i], true); + if (ret != KNOT_EOK) { + goto include_error; + } + } + + ret = KNOT_EOK; +include_error: + globfree(&glob_buf); + free(path); + depth--; + + return ret; +} + +int load_module( + knotd_conf_check_args_t *args) +{ + conf_val_t val = conf_rawid_get_txn(args->extra->conf, args->extra->txn, + C_MODULE, C_FILE, args->id, args->id_len); + const char *file_name = conf_str(&val); + + char *mod_name = strndup((const char *)args->id, args->id_len); + if (mod_name == NULL) { + return KNOT_ENOMEM; + } + + int ret = conf_mod_load_extra(args->extra->conf, mod_name, file_name, + args->extra->check); + free(mod_name); + if (ret != KNOT_EOK) { + return ret; + } + + // Update currently iterating item. + const yp_item_t *section = yp_schema_find(C_MODULE, NULL, args->extra->conf->schema); + assert(section); + args->item = section->var.g.id; + + return ret; +} diff --git a/src/knot/conf/tools.h b/src/knot/conf/tools.h new file mode 100644 index 0000000..5283c0f --- /dev/null +++ b/src/knot/conf/tools.h @@ -0,0 +1,107 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include <stdbool.h> +#include <stdint.h> + +#include "knot/conf/conf.h" +#include "libknot/yparser/ypschema.h" + +typedef struct knotd_conf_check_extra { + conf_t *conf; + knot_db_txn_t *txn; + const char *file_name; + size_t line; + bool check; /*!< Indication of the confio check mode. */ +} knotd_conf_check_extra_t; + +int conf_exec_callbacks( + knotd_conf_check_args_t *args +); + +int mod_id_to_bin( + YP_TXT_BIN_PARAMS +); + +int mod_id_to_txt( + YP_BIN_TXT_PARAMS +); + +int edns_opt_to_bin( + YP_TXT_BIN_PARAMS +); + +int edns_opt_to_txt( + YP_BIN_TXT_PARAMS +); + +int check_ref( + knotd_conf_check_args_t *args +); + +int check_ref_dflt( + knotd_conf_check_args_t *args +); + +int check_modref( + knotd_conf_check_args_t *args +); + +int check_module_id( + knotd_conf_check_args_t *args +); + +int check_server( + knotd_conf_check_args_t *args +); + +int check_keystore( + knotd_conf_check_args_t *args +); + +int check_policy( + knotd_conf_check_args_t *args +); + +int check_key( + knotd_conf_check_args_t *args +); + +int check_acl( + knotd_conf_check_args_t *args +); + +int check_remote( + knotd_conf_check_args_t *args +); + +int check_template( + knotd_conf_check_args_t *args +); + +int check_zone( + knotd_conf_check_args_t *args +); + +int include_file( + knotd_conf_check_args_t *args +); + +int load_module( + knotd_conf_check_args_t *args +); diff --git a/src/knot/ctl/commands.c b/src/knot/ctl/commands.c new file mode 100644 index 0000000..7d5ad02 --- /dev/null +++ b/src/knot/ctl/commands.c @@ -0,0 +1,1841 @@ +/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <string.h> +#include <unistd.h> +#include <sys/time.h> + +#include "knot/common/log.h" +#include "knot/common/stats.h" +#include "knot/conf/confio.h" +#include "knot/ctl/commands.h" +#include "knot/dnssec/key-events.h" +#include "knot/events/events.h" +#include "knot/events/handlers.h" +#include "knot/nameserver/query_module.h" +#include "knot/updates/zone-update.h" +#include "knot/zone/timers.h" +#include "knot/zone/zonefile.h" +#include "libknot/libknot.h" +#include "libknot/yparser/yptrafo.h" +#include "contrib/macros.h" +#include "contrib/mempattern.h" +#include "contrib/string.h" +#include "contrib/ucw/lists.h" +#include "libzscanner/scanner.h" +#include "contrib/strtonum.h" + +#define MATCH_OR_FILTER(args, code) ((args)->data[KNOT_CTL_IDX_FILTER] == NULL || \ + strchr((args)->data[KNOT_CTL_IDX_FILTER], (code)) != NULL) + +#define MATCH_AND_FILTER(args, code) ((args)->data[KNOT_CTL_IDX_FILTER] != NULL && \ + strchr((args)->data[KNOT_CTL_IDX_FILTER], (code)) != NULL) + +void ctl_log_data(knot_ctl_data_t *data) +{ + if (data == NULL) { + return; + } + + const char *zone = (*data)[KNOT_CTL_IDX_ZONE]; + const char *section = (*data)[KNOT_CTL_IDX_SECTION]; + const char *item = (*data)[KNOT_CTL_IDX_ITEM]; + const char *id = (*data)[KNOT_CTL_IDX_ID]; + + if (section == NULL) { + return; + } + + if (zone != NULL) { + log_ctl_zone_str_debug(zone, + "control, item '%s%s%s%s%s%s'", section, + (id != NULL ? "[" : ""), + (id != NULL ? id : ""), + (id != NULL ? "]" : ""), + (item != NULL ? "." : ""), + (item != NULL ? item : "")); + } else { + log_ctl_debug("control, item '%s%s%s%s%s%s'", section, + (id != NULL ? "[" : ""), + (id != NULL ? id : ""), + (id != NULL ? "]" : ""), + (item != NULL ? "." : ""), + (item != NULL ? item : "")); + } +} + +static void send_error(ctl_args_t *args, const char *msg) +{ + knot_ctl_data_t data; + memcpy(&data, args->data, sizeof(data)); + + data[KNOT_CTL_IDX_ERROR] = msg; + + int ret = knot_ctl_send(args->ctl, KNOT_CTL_TYPE_DATA, &data); + if (ret != KNOT_EOK) { + log_ctl_debug("control, failed to send error (%s)", knot_strerror(ret)); + } +} + +static int get_zone(ctl_args_t *args, zone_t **zone) +{ + const char *name = args->data[KNOT_CTL_IDX_ZONE]; + assert(name != NULL); + + uint8_t buff[KNOT_DNAME_MAXLEN]; + + knot_dname_t *dname = knot_dname_from_str(buff, name, sizeof(buff)); + if (dname == NULL) { + return KNOT_EINVAL; + } + knot_dname_to_lower(dname); + + *zone = knot_zonedb_find(args->server->zone_db, dname); + if (*zone == NULL) { + return KNOT_ENOZONE; + } + + return KNOT_EOK; +} + +static int zones_apply(ctl_args_t *args, int (*fcn)(zone_t *, ctl_args_t *)) +{ + // Process all configured zones if none is specified. + if (args->data[KNOT_CTL_IDX_ZONE] == NULL) { + knot_zonedb_foreach(args->server->zone_db, fcn, args); + return KNOT_EOK; + } + + int ret = KNOT_EOK; + + while (true) { + zone_t *zone; + ret = get_zone(args, &zone); + if (ret == KNOT_EOK) { + ret = fcn(zone, args); + } + if (ret != KNOT_EOK) { + log_ctl_zone_str_error(args->data[KNOT_CTL_IDX_ZONE], + "control, error (%s)", knot_strerror(ret)); + send_error(args, knot_strerror(ret)); + } + + // Get next zone name. + ret = knot_ctl_receive(args->ctl, &args->type, &args->data); + if (ret != KNOT_EOK || args->type != KNOT_CTL_TYPE_DATA) { + break; + } + ctl_log_data(&args->data); + } + + return ret; +} + +static int zone_status(zone_t *zone, ctl_args_t *args) +{ + char name[KNOT_DNAME_TXT_MAXLEN + 1]; + if (knot_dname_to_str(name, zone->name, sizeof(name)) == NULL) { + return KNOT_EINVAL; + } + + knot_ctl_data_t data = { + [KNOT_CTL_IDX_ZONE] = name + }; + + int ret; + char buff[128]; + knot_ctl_type_t type = KNOT_CTL_TYPE_DATA; + + if (MATCH_OR_FILTER(args, CTL_FILTER_STATUS_ROLE)) { + data[KNOT_CTL_IDX_TYPE] = "role"; + + if (zone_is_slave(conf(), zone)) { + data[KNOT_CTL_IDX_DATA] = "slave"; + } else { + data[KNOT_CTL_IDX_DATA] = "master"; + } + + ret = knot_ctl_send(args->ctl, type, &data); + if (ret != KNOT_EOK) { + return ret; + } else { + type = KNOT_CTL_TYPE_EXTRA; + } + } + + if (MATCH_OR_FILTER(args, CTL_FILTER_STATUS_SERIAL)) { + data[KNOT_CTL_IDX_TYPE] = "serial"; + + if (zone->contents != NULL) { + knot_rdataset_t *soa = node_rdataset(zone->contents->apex, + KNOT_RRTYPE_SOA); + ret = snprintf(buff, sizeof(buff), "%u", knot_soa_serial(soa->rdata)); + } else { + ret = snprintf(buff, sizeof(buff), "none"); + } + if (ret < 0 || ret >= sizeof(buff)) { + return KNOT_ESPACE; + } + + data[KNOT_CTL_IDX_DATA] = buff; + + ret = knot_ctl_send(args->ctl, type, &data); + if (ret != KNOT_EOK) { + return ret; + } else { + type = KNOT_CTL_TYPE_EXTRA; + } + } + + if (MATCH_OR_FILTER(args, CTL_FILTER_STATUS_TRANSACTION)) { + data[KNOT_CTL_IDX_TYPE] = "transaction"; + data[KNOT_CTL_IDX_DATA] = (zone->control_update != NULL) ? "open" : "none"; + ret = knot_ctl_send(args->ctl, type, &data); + if (ret != KNOT_EOK) { + return ret; + } else { + type = KNOT_CTL_TYPE_EXTRA; + } + } + + bool ufrozen = zone->events.ufrozen; + if (MATCH_OR_FILTER(args, CTL_FILTER_STATUS_FREEZE)) { + data[KNOT_CTL_IDX_TYPE] = "freeze"; + if (ufrozen) { + if (zone_events_get_time(zone, ZONE_EVENT_UTHAW) < time(NULL)) { + data[KNOT_CTL_IDX_DATA] = "yes"; + } else { + data[KNOT_CTL_IDX_DATA] = "thawing"; + } + } else { + if (zone_events_get_time(zone, ZONE_EVENT_UFREEZE) < time(NULL)) { + data[KNOT_CTL_IDX_DATA] = "no"; + } else { + data[KNOT_CTL_IDX_DATA] = "freezing"; + + } + } + ret = knot_ctl_send(args->ctl, type, &data); + if (ret != KNOT_EOK) { + return ret; + } + } + + if (MATCH_OR_FILTER(args, CTL_FILTER_STATUS_EVENTS)) { + for (zone_event_type_t i = 0; i < ZONE_EVENT_COUNT; i++) { + // Events not worth showing or used elsewhere. + if (i == ZONE_EVENT_UFREEZE || i == ZONE_EVENT_UTHAW) { + continue; + } + + // Skip events affected by freeze. + if (ufrozen && ufreeze_applies(i)) { + continue; + } + + data[KNOT_CTL_IDX_TYPE] = zone_events_get_name(i); + time_t ev_time = zone_events_get_time(zone, i); + if (ev_time <= 0) { + ret = snprintf(buff, sizeof(buff), "not scheduled"); + } else if (ev_time <= time(NULL)) { + ret = snprintf(buff, sizeof(buff), "pending"); + } else { + ret = knot_time_print(TIME_PRINT_HUMAN_MIXED, + ev_time, buff, sizeof(buff)); + } + if (ret < 0 || ret >= sizeof(buff)) { + return KNOT_ESPACE; + } + data[KNOT_CTL_IDX_DATA] = buff; + + ret = knot_ctl_send(args->ctl, type, &data); + if (ret != KNOT_EOK) { + return ret; + } + type = KNOT_CTL_TYPE_EXTRA; + } + } + + return KNOT_EOK; +} + +static int zone_reload(zone_t *zone, ctl_args_t *args) +{ + UNUSED(args); + + if (zone_expired(zone)) { + return KNOT_ENOTSUP; + } + + zone_events_schedule_user(zone, ZONE_EVENT_LOAD); + + return KNOT_EOK; +} + +static int zone_refresh(zone_t *zone, ctl_args_t *args) +{ + UNUSED(args); + + if (!zone_is_slave(conf(), zone)) { + return KNOT_ENOTSUP; + } + + zone_events_schedule_user(zone, ZONE_EVENT_REFRESH); + + return KNOT_EOK; +} + +static int zone_retransfer(zone_t *zone, ctl_args_t *args) +{ + UNUSED(args); + + if (!zone_is_slave(conf(), zone)) { + return KNOT_ENOTSUP; + } + + zone->flags |= ZONE_FORCE_AXFR; + zone_events_schedule_user(zone, ZONE_EVENT_REFRESH); + + return KNOT_EOK; +} + +static int zone_notify(zone_t *zone, ctl_args_t *args) +{ + UNUSED(args); + + zone_events_schedule_user(zone, ZONE_EVENT_NOTIFY); + + return KNOT_EOK; +} + +static int zone_flush(zone_t *zone, ctl_args_t *args) +{ + if (MATCH_AND_FILTER(args, CTL_FILTER_FLUSH_OUTDIR)) { + return zone_dump_to_dir(conf(), zone, args->data[KNOT_CTL_IDX_DATA]); + } + + if (ctl_has_flag(args->data[KNOT_CTL_IDX_FLAGS], CTL_FLAG_FORCE)) { + zone->flags |= ZONE_FORCE_FLUSH; + } + + zone_events_schedule_user(zone, ZONE_EVENT_FLUSH); + + return KNOT_EOK; +} + +static int zone_sign(zone_t *zone, ctl_args_t *args) +{ + UNUSED(args); + + conf_val_t val = conf_zone_get(conf(), C_DNSSEC_SIGNING, zone->name); + if (!conf_bool(&val)) { + return KNOT_ENOTSUP; + } + + zone->flags |= ZONE_FORCE_RESIGN; + zone_events_schedule_user(zone, ZONE_EVENT_DNSSEC); + + return KNOT_EOK; +} + +static int zone_ksk_sbm_confirm(zone_t *zone, ctl_args_t *args) +{ + UNUSED(args); + + kdnssec_ctx_t ctx = { 0 }; + + int ret = kdnssec_ctx_init(conf(), &ctx, zone->name, NULL); + if (ret != KNOT_EOK) { + return ret; + } + + ret = knot_dnssec_ksk_sbm_confirm(&ctx, 0); + kdnssec_ctx_deinit(&ctx); + + conf_val_t val = conf_zone_get(conf(), C_DNSSEC_SIGNING, zone->name); + if (ret == KNOT_EOK && conf_bool(&val)) { + // NOT zone_events_schedule_user(), intentionally! + zone_events_schedule_now(zone, ZONE_EVENT_DNSSEC); + } + + return ret; +} + +static int zone_freeze(zone_t *zone, ctl_args_t *args) +{ + UNUSED(args); + + zone_events_schedule_now(zone, ZONE_EVENT_UFREEZE); + + return KNOT_EOK; +} + +static int zone_thaw(zone_t *zone, ctl_args_t *args) +{ + UNUSED(args); + + zone_events_schedule_now(zone, ZONE_EVENT_UTHAW); + + return KNOT_EOK; +} + +static int zone_txn_begin(zone_t *zone, ctl_args_t *args) +{ + UNUSED(args); + + if (zone->control_update != NULL) { + return KNOT_TXN_EEXISTS; + } + + zone->control_update = malloc(sizeof(zone_update_t)); + if (zone->control_update == NULL) { + return KNOT_ENOMEM; + } + + zone_update_flags_t type = (zone->contents == NULL) ? UPDATE_FULL : UPDATE_INCREMENTAL; + int ret = zone_update_init(zone->control_update, zone, type | UPDATE_SIGN | UPDATE_STRICT); + if (ret != KNOT_EOK) { + free(zone->control_update); + zone->control_update = NULL; + return ret; + } + + return KNOT_EOK; +} + +static void zone_txn_update_clear(zone_t *zone) +{ + assert(zone->control_update); + + zone_update_clear(zone->control_update); + free(zone->control_update); + zone->control_update = NULL; +} + +static int zone_txn_commit(zone_t *zone, ctl_args_t *args) +{ + UNUSED(args); + + if (zone->control_update == NULL) { + return KNOT_TXN_ENOTEXISTS; + } + + // NOOP if empty changeset/contents. + if (((zone->control_update->flags & UPDATE_INCREMENTAL) && + changeset_empty(&zone->control_update->change)) || + ((zone->control_update->flags & UPDATE_FULL) && + zone_contents_is_empty(zone->control_update->new_cont))) { + zone_txn_update_clear(zone); + return KNOT_EOK; + } + + // Sign update. + conf_val_t val = conf_zone_get(conf(), C_DNSSEC_SIGNING, zone->name); + bool dnssec_enable = (zone->control_update->flags & UPDATE_SIGN) && conf_bool(&val); + if (dnssec_enable) { + zone_sign_reschedule_t resch = { 0 }; + int ret = knot_dnssec_sign_update(zone->control_update, &resch); + if (ret != KNOT_EOK) { + zone_txn_update_clear(zone); + return ret; + } + event_dnssec_reschedule(conf(), zone, &resch, false); + } + + int ret = zone_update_commit(conf(), zone->control_update); + if (ret != KNOT_EOK) { + /* Invalidate the transaction if aborted. */ + if (zone->control_update->zone == NULL) { + free(zone->control_update); + zone->control_update = NULL; + } + return ret; + } + + zone_txn_update_clear(zone); + + zone_events_schedule_now(zone, ZONE_EVENT_NOTIFY); + + return KNOT_EOK; +} + +static int zone_txn_abort(zone_t *zone, ctl_args_t *args) +{ + UNUSED(args); + + if (zone->control_update == NULL) { + return KNOT_TXN_ENOTEXISTS; + } + + zone_txn_update_clear(zone); + + return KNOT_EOK; +} + +typedef struct { + ctl_args_t *args; + int type_filter; // -1: no specific type, [0, 2^16]: specific type. + knot_dump_style_t style; + knot_ctl_data_t data; + char zone[KNOT_DNAME_TXT_MAXLEN + 1]; + char owner[KNOT_DNAME_TXT_MAXLEN + 1]; + char ttl[16]; + char type[32]; + char rdata[2 * 65536]; +} send_ctx_t; + +static send_ctx_t *create_send_ctx(const knot_dname_t *zone_name, ctl_args_t *args) +{ + send_ctx_t *ctx = mm_calloc(&args->mm, 1, sizeof(*ctx)); + if (ctx == NULL) { + return NULL; + } + + ctx->args = args; + + // Set the dump style. + ctx->style.show_ttl = true; + ctx->style.original_ttl = true; + ctx->style.human_tmstamp = true; + + // Set the output data buffers. + ctx->data[KNOT_CTL_IDX_ZONE] = ctx->zone; + ctx->data[KNOT_CTL_IDX_OWNER] = ctx->owner; + ctx->data[KNOT_CTL_IDX_TTL] = ctx->ttl; + ctx->data[KNOT_CTL_IDX_TYPE] = ctx->type; + ctx->data[KNOT_CTL_IDX_DATA] = ctx->rdata; + + // Set the ZONE. + if (knot_dname_to_str(ctx->zone, zone_name, sizeof(ctx->zone)) == NULL) { + mm_free(&args->mm, ctx); + return NULL; + } + + // Set the TYPE filter. + if (args->data[KNOT_CTL_IDX_TYPE] != NULL) { + uint16_t type; + if (knot_rrtype_from_string(args->data[KNOT_CTL_IDX_TYPE], &type) != 0) { + mm_free(&args->mm, ctx); + return NULL; + } + ctx->type_filter = type; + } else { + ctx->type_filter = -1; + } + + return ctx; +} + +static int send_rrset(knot_rrset_t *rrset, send_ctx_t *ctx) +{ + if (rrset->type != KNOT_RRTYPE_RRSIG) { + int ret = snprintf(ctx->ttl, sizeof(ctx->ttl), "%u", rrset->ttl); + if (ret <= 0 || ret >= sizeof(ctx->ttl)) { + return KNOT_ESPACE; + } + } + + if (knot_rrtype_to_string(rrset->type, ctx->type, sizeof(ctx->type)) < 0) { + return KNOT_ESPACE; + } + + for (size_t i = 0; i < rrset->rrs.count; ++i) { + if (rrset->type == KNOT_RRTYPE_RRSIG) { + int ret = snprintf(ctx->ttl, sizeof(ctx->ttl), "%u", + knot_rrsig_original_ttl(knot_rdataset_at(&rrset->rrs, i))); + if (ret <= 0 || ret >= sizeof(ctx->ttl)) { + return KNOT_ESPACE; + } + } + + int ret = knot_rrset_txt_dump_data(rrset, i, ctx->rdata, + sizeof(ctx->rdata), &ctx->style); + if (ret < 0) { + return ret; + } + + ret = knot_ctl_send(ctx->args->ctl, KNOT_CTL_TYPE_DATA, &ctx->data); + if (ret != KNOT_EOK) { + return ret; + } + } + + return KNOT_EOK; +} + +static int send_node(zone_node_t *node, void *ctx_void) +{ + send_ctx_t *ctx = ctx_void; + if (knot_dname_to_str(ctx->owner, node->owner, sizeof(ctx->owner)) == NULL) { + return KNOT_EINVAL; + } + + for (size_t i = 0; i < node->rrset_count; ++i) { + knot_rrset_t rrset = node_rrset_at(node, i); + + // Check for requested TYPE. + if (ctx->type_filter != -1 && rrset.type != ctx->type_filter) { + continue; + } + + int ret = send_rrset(&rrset, ctx); + if (ret != KNOT_EOK) { + return ret; + } + } + + return KNOT_EOK; +} + +static int get_owner(uint8_t *out, size_t out_len, knot_dname_t *origin, + ctl_args_t *args) +{ + const char *owner = args->data[KNOT_CTL_IDX_OWNER]; + assert(owner != NULL); + + bool fqdn = false; + size_t prefix_len = 0; + + size_t owner_len = strlen(owner); + if (owner_len > 0 && (owner_len != 1 || owner[0] != '@')) { + // Check if the owner is FQDN. + if (owner[owner_len - 1] == '.') { + fqdn = true; + } + + knot_dname_t *dname = knot_dname_from_str(out, owner, out_len); + if (dname == NULL) { + return KNOT_EINVAL; + } + knot_dname_to_lower(dname); + + prefix_len = knot_dname_size(out); + if (prefix_len == 0) { + return KNOT_EINVAL; + } + + // Ignore trailing dot. + prefix_len--; + } + + // Append the origin. + if (!fqdn) { + size_t origin_len = knot_dname_size(origin); + if (origin_len == 0 || origin_len > out_len - prefix_len) { + return KNOT_EINVAL; + } + memcpy(out + prefix_len, origin, origin_len); + } + + return KNOT_EOK; +} + +static int zone_read(zone_t *zone, ctl_args_t *args) +{ + send_ctx_t *ctx = create_send_ctx(zone->name, args); + if (ctx == NULL) { + return KNOT_ENOMEM; + } + + int ret = KNOT_EOK; + + if (args->data[KNOT_CTL_IDX_OWNER] != NULL) { + uint8_t owner[KNOT_DNAME_MAXLEN]; + + ret = get_owner(owner, sizeof(owner), zone->name, args); + if (ret != KNOT_EOK) { + goto zone_read_failed; + } + + const zone_node_t *node = zone_contents_find_node(zone->contents, owner); + if (node == NULL) { + ret = KNOT_ENONODE; + goto zone_read_failed; + } + + ret = send_node((zone_node_t *)node, ctx); + } else if (zone->contents != NULL) { + ret = zone_contents_apply(zone->contents, send_node, ctx); + } + +zone_read_failed: + mm_free(&args->mm, ctx); + + return ret; +} + +static int zone_flag_txn_get(zone_t *zone, ctl_args_t *args, const char *flag) +{ + if (zone->control_update == NULL) { + return KNOT_TXN_ENOTEXISTS; + } + + send_ctx_t *ctx = create_send_ctx(zone->name, args); + if (ctx == NULL) { + return KNOT_ENOMEM; + } + ctx->data[KNOT_CTL_IDX_FLAGS] = flag; + + int ret = KNOT_EOK; + + if (args->data[KNOT_CTL_IDX_OWNER] != NULL) { + uint8_t owner[KNOT_DNAME_MAXLEN]; + + ret = get_owner(owner, sizeof(owner), zone->name, args); + if (ret != KNOT_EOK) { + goto zone_txn_get_failed; + } + + const zone_node_t *node = zone_update_get_node(zone->control_update, owner); + if (node == NULL) { + ret = KNOT_ENONODE; + goto zone_txn_get_failed; + } + + ret = send_node((zone_node_t *)node, ctx); + } else { + zone_update_iter_t it; + ret = zone_update_iter(&it, zone->control_update); + if (ret != KNOT_EOK) { + goto zone_txn_get_failed; + } + + const zone_node_t *iter_node = zone_update_iter_val(&it); + while (iter_node != NULL) { + ret = send_node((zone_node_t *)iter_node, ctx); + if (ret != KNOT_EOK) { + zone_update_iter_finish(&it); + goto zone_txn_get_failed; + } + + ret = zone_update_iter_next(&it); + if (ret != KNOT_EOK) { + zone_update_iter_finish(&it); + goto zone_txn_get_failed; + } + + iter_node = zone_update_iter_val(&it); + } + zone_update_iter_finish(&it); + } + +zone_txn_get_failed: + mm_free(&args->mm, ctx); + + return ret; +} + +static int zone_txn_get(zone_t *zone, ctl_args_t *args) +{ + return zone_flag_txn_get(zone, args, NULL); +} + +static int send_changeset_part(changeset_t *ch, send_ctx_t *ctx, bool from) +{ + ctx->data[KNOT_CTL_IDX_FLAGS] = from ? CTL_FLAG_REM : CTL_FLAG_ADD; + + // Send SOA only if explicitly changed. + if (ch->soa_to != NULL) { + knot_rrset_t *soa = from ? ch->soa_from : ch->soa_to; + assert(soa); + + char *owner = knot_dname_to_str(ctx->owner, soa->owner, sizeof(ctx->owner)); + if (owner == NULL) { + return KNOT_EINVAL; + } + + int ret = send_rrset(soa, ctx); + if (ret != KNOT_EOK) { + return ret; + } + } + + // Send other records. + changeset_iter_t it; + int ret = from ? changeset_iter_rem(&it, ch) : changeset_iter_add(&it, ch); + if (ret != KNOT_EOK) { + return ret; + } + + knot_rrset_t rrset = changeset_iter_next(&it); + while (!knot_rrset_empty(&rrset)) { + char *owner = knot_dname_to_str(ctx->owner, rrset.owner, sizeof(ctx->owner)); + if (owner == NULL) { + changeset_iter_clear(&it); + return KNOT_EINVAL; + } + + ret = send_rrset(&rrset, ctx); + if (ret != KNOT_EOK) { + changeset_iter_clear(&it); + return ret; + } + + rrset = changeset_iter_next(&it); + } + changeset_iter_clear(&it); + + return KNOT_EOK; +} + +static int send_changeset(changeset_t *ch, send_ctx_t *ctx) +{ + // First send 'from' changeset part. + int ret = send_changeset_part(ch, ctx, true); + if (ret != KNOT_EOK) { + return ret; + } + + // Second send 'to' changeset part. + ret = send_changeset_part(ch, ctx, false); + if (ret != KNOT_EOK) { + return ret; + } + + return KNOT_EOK; +} + +static int zone_txn_diff(zone_t *zone, ctl_args_t *args) +{ + if (zone->control_update == NULL) { + return KNOT_TXN_ENOTEXISTS; + } + + // FULL update has no changeset to print, do a 'get' instead. + if (zone->control_update->flags & UPDATE_FULL) { + return zone_flag_txn_get(zone, args, CTL_FLAG_ADD); + } + + send_ctx_t *ctx = create_send_ctx(zone->name, args); + if (ctx == NULL) { + return KNOT_ENOMEM; + } + + int ret = send_changeset(&zone->control_update->change, ctx); + mm_free(&args->mm, ctx); + return ret; +} + +static int get_ttl(zone_t *zone, ctl_args_t *args, uint32_t *ttl) +{ + uint8_t owner[KNOT_DNAME_MAXLEN]; + + int ret = get_owner(owner, sizeof(owner), zone->name, args); + if (ret != KNOT_EOK) { + return ret; + } + + const zone_node_t *node = zone_update_get_node(zone->control_update, owner); + if (node == NULL) { + return KNOT_EINVAL; + } + + uint16_t type; + if (knot_rrtype_from_string(args->data[KNOT_CTL_IDX_TYPE], &type) != 0) { + return KNOT_EINVAL; + } + + *ttl = node_rrset(node, type).ttl; + + return KNOT_EOK; +} + +static int create_rrset(knot_rrset_t **rrset, zone_t *zone, ctl_args_t *args, + bool need_ttl) +{ + char origin_buff[KNOT_DNAME_TXT_MAXLEN + 1]; + char *origin = knot_dname_to_str(origin_buff, zone->name, sizeof(origin_buff)); + if (origin == NULL) { + return KNOT_EINVAL; + } + + const char *owner = args->data[KNOT_CTL_IDX_OWNER]; + const char *type = args->data[KNOT_CTL_IDX_TYPE]; + const char *data = args->data[KNOT_CTL_IDX_DATA]; + const char *ttl = need_ttl ? args->data[KNOT_CTL_IDX_TTL] : NULL; + + // Prepare a buffer for a reconstructed record. + const size_t buff_len = sizeof(((send_ctx_t *)0)->owner) + + sizeof(((send_ctx_t *)0)->ttl) + + sizeof(((send_ctx_t *)0)->type) + + sizeof(((send_ctx_t *)0)->rdata); + char *buff = mm_alloc(&args->mm, buff_len); + if (buff == NULL) { + return KNOT_ENOMEM; + } + + uint32_t default_ttl = 0; + if (ttl == NULL) { + int ret = get_ttl(zone, args, &default_ttl); + if (need_ttl && ret != KNOT_EOK) { + mm_free(&args->mm, buff); + return ret; + } + } + + // Reconstruct the record. + int ret = snprintf(buff, buff_len, "%s %s %s %s\n", + (owner != NULL ? owner : ""), + (ttl != NULL ? ttl : ""), + (type != NULL ? type : ""), + (data != NULL ? data : "")); + if (ret <= 0 || ret >= buff_len) { + mm_free(&args->mm, buff); + return KNOT_ESPACE; + } + size_t rdata_len = ret; + + // Initialize RR parser. + zs_scanner_t *scanner = mm_alloc(&args->mm, sizeof(*scanner)); + if (scanner == NULL) { + ret = KNOT_ENOMEM; + goto parser_failed; + } + + // Parse the record. + if (zs_init(scanner, origin, KNOT_CLASS_IN, default_ttl) != 0 || + zs_set_input_string(scanner, buff, rdata_len) != 0 || + zs_parse_record(scanner) != 0 || + scanner->state != ZS_STATE_DATA) { + ret = KNOT_EPARSEFAIL; + goto parser_failed; + } + + // Create output rrset. + *rrset = knot_rrset_new(scanner->r_owner, scanner->r_type, + scanner->r_class, scanner->r_ttl, NULL); + if (*rrset == NULL) { + ret = KNOT_ENOMEM; + goto parser_failed; + } + + ret = knot_rrset_add_rdata(*rrset, scanner->r_data, scanner->r_data_length, + NULL); +parser_failed: + zs_deinit(scanner); + mm_free(&args->mm, scanner); + mm_free(&args->mm, buff); + + return ret; +} + +static int zone_txn_set(zone_t *zone, ctl_args_t *args) +{ + if (zone->control_update == NULL) { + return KNOT_TXN_ENOTEXISTS; + } + + if (args->data[KNOT_CTL_IDX_OWNER] == NULL || + args->data[KNOT_CTL_IDX_TYPE] == NULL) { + return KNOT_EINVAL; + } + + knot_rrset_t *rrset; + int ret = create_rrset(&rrset, zone, args, true); + if (ret != KNOT_EOK) { + return ret; + } + + ret = zone_update_add(zone->control_update, rrset); + knot_rrset_free(rrset, NULL); + + return ret; +} + +static int zone_txn_unset(zone_t *zone, ctl_args_t *args) +{ + if (zone->control_update == NULL) { + return KNOT_TXN_ENOTEXISTS; + } + + if (args->data[KNOT_CTL_IDX_OWNER] == NULL) { + return KNOT_EINVAL; + } + + // Remove specific record. + if (args->data[KNOT_CTL_IDX_DATA] != NULL) { + if (args->data[KNOT_CTL_IDX_TYPE] == NULL) { + return KNOT_EINVAL; + } + + knot_rrset_t *rrset; + int ret = create_rrset(&rrset, zone, args, false); + if (ret != KNOT_EOK) { + return ret; + } + + ret = zone_update_remove(zone->control_update, rrset); + knot_rrset_free(rrset, NULL); + return ret; + } else { + uint8_t owner[KNOT_DNAME_MAXLEN]; + + int ret = get_owner(owner, sizeof(owner), zone->name, args); + if (ret != KNOT_EOK) { + return ret; + } + + // Remove whole rrset. + if (args->data[KNOT_CTL_IDX_TYPE] != NULL) { + uint16_t type; + if (knot_rrtype_from_string(args->data[KNOT_CTL_IDX_TYPE], + &type) != 0) { + return KNOT_EINVAL; + } + + return zone_update_remove_rrset(zone->control_update, owner, type); + // Remove whole node. + } else { + return zone_update_remove_node(zone->control_update, owner); + } + } +} + +static bool zone_exists(const knot_dname_t *zone, void *data) +{ + assert(zone); + assert(data); + + knot_zonedb_t *db = data; + + return knot_zonedb_find(db, zone) != NULL; +} + +static bool zone_names_distinct(const knot_dname_t *zone, void *data) +{ + assert(zone); + assert(data); + + knot_dname_t *zone_to_purge = data; + + return !knot_dname_is_equal(zone, zone_to_purge); +} + +static int orphans_purge(ctl_args_t *args) +{ + assert(args->data[KNOT_CTL_IDX_FILTER] != NULL); + bool only_orphan = (strlen(args->data[KNOT_CTL_IDX_FILTER]) == 1); + + if (args->data[KNOT_CTL_IDX_ZONE] == NULL) { + // Purge KASP DB. + if (only_orphan || MATCH_AND_FILTER(args, CTL_FILTER_PURGE_KASPDB)) { + list_t zones; + init_list(&zones); + if (kasp_db_open(*kaspdb()) == KNOT_EOK && + kasp_db_list_zones(*kaspdb(), &zones) == KNOT_EOK) { + ptrnode_t *zn; + WALK_LIST(zn, zones) { + knot_dname_t *zone_name = (knot_dname_t *)zn->d; + if (!zone_exists(zone_name, args->server->zone_db)) { + (void)kasp_db_delete_all(*kaspdb(), zone_name); + } + knot_dname_free(zone_name, NULL); + } + ptrlist_free(&zones, NULL); + } + } + + // Purge zone journals of unconfigured zones. + if (only_orphan || MATCH_AND_FILTER(args, CTL_FILTER_PURGE_JOURNAL)) { + list_t zones; + init_list(&zones); + if (journal_db_list_zones(&args->server->journal_db, &zones) == KNOT_EOK) { + ptrnode_t *zn; + WALK_LIST(zn, zones) { + journal_t journal = { 0 }; + knot_dname_t *zone_name = (knot_dname_t *)zn->d; + if (!zone_exists(zone_name, args->server->zone_db) && + journal_open(&journal, &args->server->journal_db, + zone_name) == KNOT_EOK) { + journal_scrape(&journal); + journal_close(&journal); + } + knot_dname_free(zone_name, NULL); + } + ptrlist_free(&zones, NULL); + } + } + + // Purge timers of unconfigured zones. + if (only_orphan || MATCH_AND_FILTER(args, CTL_FILTER_PURGE_TIMERS)) { + (void)zone_timers_sweep(args->server->timers_db, + zone_exists, args->server->zone_db); + } + } else { + uint8_t buff[KNOT_DNAME_MAXLEN]; + while (true) { + knot_dname_t *zone_name = + knot_dname_from_str(buff, args->data[KNOT_CTL_IDX_ZONE], + sizeof(buff)); + if (zone_name == NULL) { + log_ctl_zone_str_error(args->data[KNOT_CTL_IDX_ZONE], + "control, error (%s)", + knot_strerror(KNOT_EINVAL)); + send_error(args, knot_strerror(KNOT_EINVAL)); + return KNOT_EINVAL; + } + knot_dname_to_lower(zone_name); + + if (!zone_exists(zone_name, args->server->zone_db)) { + // Purge KASP DB. + if (only_orphan || MATCH_AND_FILTER(args, CTL_FILTER_PURGE_KASPDB)) { + if (kasp_db_open(*kaspdb()) == KNOT_EOK) { + (void) kasp_db_delete_all(*kaspdb(), zone_name); + } + } + + // Purge zone journal. + if (only_orphan || MATCH_AND_FILTER(args, CTL_FILTER_PURGE_JOURNAL)) { + journal_t journal = { 0 }; + if (journal_open(&journal, &args->server->journal_db, + zone_name) == KNOT_EOK) { + (void)journal_scrape(&journal); + journal_close(&journal); + } + } + + // Purge zone timers. + if (only_orphan || MATCH_AND_FILTER(args, CTL_FILTER_PURGE_TIMERS)) { + (void)zone_timers_sweep(args->server->timers_db, + zone_names_distinct, zone_name); + } + } + + // Get next zone name. + int ret = knot_ctl_receive(args->ctl, &args->type, &args->data); + if (ret != KNOT_EOK || args->type != KNOT_CTL_TYPE_DATA) { + break; + } + ctl_log_data(&args->data); + } + } + + return KNOT_EOK; +} + +static int zone_purge(zone_t *zone, ctl_args_t *args) +{ + // Abort possible editing transaction. + if (MATCH_OR_FILTER(args, CTL_FILTER_PURGE_EXPIRE)) { + (void)zone_txn_abort(zone, args); + } + + // Purge the zone timers. + if (MATCH_OR_FILTER(args, CTL_FILTER_PURGE_TIMERS)) { + memset(&zone->timers, 0, sizeof(zone->timers)); + (void)zone_timers_sweep(args->server->timers_db, + zone_names_distinct, zone->name); + } + + // Expire the zone. + if (MATCH_OR_FILTER(args, CTL_FILTER_PURGE_EXPIRE)) { + zone_events_schedule_user(zone, ZONE_EVENT_EXPIRE); + } + + // Purge the zone file. + if (MATCH_OR_FILTER(args, CTL_FILTER_PURGE_ZONEFILE)) { + char *zonefile = conf_zonefile(conf(), zone->name); + (void)unlink(zonefile); + free(zonefile); + } + + // Purge the zone journal. + if (MATCH_OR_FILTER(args, CTL_FILTER_PURGE_JOURNAL)) { + if (journal_open(zone->journal, zone->journal_db, zone->name) == KNOT_EOK) { + (void)journal_scrape(zone->journal); + } + } + + // Purge KASP DB. + if (MATCH_OR_FILTER(args, CTL_FILTER_PURGE_KASPDB)) { + if (kasp_db_open(*kaspdb()) == KNOT_EOK) { + (void)kasp_db_delete_all(*kaspdb(), zone->name); + } + } + + return KNOT_EOK; +} + +static int send_stats_ctr(mod_ctr_t *ctr, ctl_args_t *args, knot_ctl_data_t *data) +{ + char index[128]; + char value[32]; + + if (ctr->count == 1) { + uint64_t counter = ATOMIC_GET(ctr->counter); + int ret = snprintf(value, sizeof(value), "%"PRIu64, counter); + if (ret <= 0 || ret >= sizeof(value)) { + return KNOT_ESPACE; + } + + (*data)[KNOT_CTL_IDX_ID] = NULL; + (*data)[KNOT_CTL_IDX_DATA] = value; + + ret = knot_ctl_send(args->ctl, KNOT_CTL_TYPE_DATA, data); + if (ret != KNOT_EOK) { + return ret; + } + } else { + bool force = ctl_has_flag(args->data[KNOT_CTL_IDX_FLAGS], + CTL_FLAG_FORCE); + + for (uint32_t i = 0; i < ctr->count; i++) { + uint64_t counter = ATOMIC_GET(ctr->counters[i]); + + // Skip empty counters. + if (counter == 0 && !force) { + continue; + } + + int ret; + if (ctr->idx_to_str) { + char *str = ctr->idx_to_str(i, ctr->count); + if (str == NULL) { + continue; + } + ret = snprintf(index, sizeof(index), "%s", str); + free(str); + } else { + ret = snprintf(index, sizeof(index), "%u", i); + } + if (ret <= 0 || ret >= sizeof(index)) { + return KNOT_ESPACE; + } + + ret = snprintf(value, sizeof(value), "%"PRIu64, counter); + if (ret <= 0 || ret >= sizeof(value)) { + return KNOT_ESPACE; + } + + (*data)[KNOT_CTL_IDX_ID] = index; + (*data)[KNOT_CTL_IDX_DATA] = value; + + knot_ctl_type_t type = (i == 0) ? KNOT_CTL_TYPE_DATA : + KNOT_CTL_TYPE_EXTRA; + ret = knot_ctl_send(args->ctl, type, data); + if (ret != KNOT_EOK) { + return ret; + } + } + } + + return KNOT_EOK; +} + +static int modules_stats(list_t *query_modules, ctl_args_t *args, knot_dname_t *zone) +{ + if (query_modules == NULL) { + return KNOT_EOK; + } + + const char *section = args->data[KNOT_CTL_IDX_SECTION]; + const char *item = args->data[KNOT_CTL_IDX_ITEM]; + + char name[KNOT_DNAME_TXT_MAXLEN + 1] = { 0 }; + knot_ctl_data_t data = { 0 }; + + bool section_found = (section == NULL) ? true : false; + bool item_found = (item == NULL) ? true : false; + + knotd_mod_t *mod = NULL; + WALK_LIST(mod, *query_modules) { + // Skip modules without statistics. + if (mod->stats_count == 0) { + continue; + } + + // Check for specific module. + if (section != NULL) { + if (section_found) { + break; + } else if (strcasecmp(mod->id->name + 1, section) == 0) { + section_found = true; + } else { + continue; + } + } + + data[KNOT_CTL_IDX_SECTION] = mod->id->name + 1; + + for (int i = 0; i < mod->stats_count; i++) { + mod_ctr_t *ctr = mod->stats + i; + + // Skip empty counter. + if (ctr->name == NULL) { + continue; + } + + // Check for specific counter. + if (item != NULL) { + if (item_found) { + break; + } else if (strcasecmp(ctr->name, item) == 0) { + item_found = true; + } else { + continue; + } + } + + // Prepare zone name if not already prepared. + if (zone != NULL && name[0] == '\0') { + if (knot_dname_to_str(name, zone, sizeof(name)) == NULL) { + return KNOT_EINVAL; + } + data[KNOT_CTL_IDX_ZONE] = name; + } + + data[KNOT_CTL_IDX_ITEM] = ctr->name; + + // Send the counters. + int ret = send_stats_ctr(ctr, args, &data); + if (ret != KNOT_EOK) { + return ret; + } + } + } + + return (section_found && item_found) ? KNOT_EOK : KNOT_ENOENT; +} + +static int zone_stats(zone_t *zone, ctl_args_t *args) +{ + return modules_stats(&zone->query_modules, args, zone->name); +} + +static int ctl_zone(ctl_args_t *args, ctl_cmd_t cmd) +{ + switch (cmd) { + case CTL_ZONE_STATUS: + return zones_apply(args, zone_status); + case CTL_ZONE_RELOAD: + return zones_apply(args, zone_reload); + case CTL_ZONE_REFRESH: + return zones_apply(args, zone_refresh); + case CTL_ZONE_RETRANSFER: + return zones_apply(args, zone_retransfer); + case CTL_ZONE_NOTIFY: + return zones_apply(args, zone_notify); + case CTL_ZONE_FLUSH: + return zones_apply(args, zone_flush); + case CTL_ZONE_SIGN: + return zones_apply(args, zone_sign); + case CTL_ZONE_KSK_SBM: + return zones_apply(args, zone_ksk_sbm_confirm); + case CTL_ZONE_FREEZE: + return zones_apply(args, zone_freeze); + case CTL_ZONE_THAW: + return zones_apply(args, zone_thaw); + case CTL_ZONE_READ: + return zones_apply(args, zone_read); + case CTL_ZONE_BEGIN: + return zones_apply(args, zone_txn_begin); + case CTL_ZONE_COMMIT: + return zones_apply(args, zone_txn_commit); + case CTL_ZONE_ABORT: + return zones_apply(args, zone_txn_abort); + case CTL_ZONE_DIFF: + return zones_apply(args, zone_txn_diff); + case CTL_ZONE_GET: + return zones_apply(args, zone_txn_get); + case CTL_ZONE_SET: + return zones_apply(args, zone_txn_set); + case CTL_ZONE_UNSET: + return zones_apply(args, zone_txn_unset); + case CTL_ZONE_PURGE: + if (MATCH_AND_FILTER(args, CTL_FILTER_PURGE_ORPHAN)) { + return orphans_purge(args); + } else { + return zones_apply(args, zone_purge); + } + case CTL_ZONE_STATS: + return zones_apply(args, zone_stats); + default: + assert(0); + return KNOT_EINVAL; + } +} + +static int server_status(ctl_args_t *args) +{ + const char *type = args->data[KNOT_CTL_IDX_TYPE]; + + if (type == NULL || strlen(type) == 0) { + return KNOT_EOK; + } + + char buff[2048] = ""; + + int ret; + if (strcasecmp(type, "version") == 0) { + ret = snprintf(buff, sizeof(buff), "Version: %s", PACKAGE_VERSION); + } else if (strcasecmp(type, "workers") == 0) { + int running_bkg_wrk, wrk_queue; + worker_pool_status(args->server->workers, &running_bkg_wrk, &wrk_queue); + ret = snprintf(buff, sizeof(buff), "UDP workers: %zu, TCP workers %zu, " + "background workers: %zu (running: %d, pending: %d)", + conf_udp_threads(conf()), conf_tcp_threads(conf()), + conf_bg_threads(conf()), running_bkg_wrk, wrk_queue); + } else if (strcasecmp(type, "configure") == 0) { + ret = snprintf(buff, sizeof(buff), "%s", CONFIGURE_SUMMARY); + } else { + return KNOT_EINVAL; + } + if (ret <= 0 || ret >= sizeof(buff)) { + return KNOT_ESPACE; + } + + args->data[KNOT_CTL_IDX_DATA] = buff; + + return knot_ctl_send(args->ctl, KNOT_CTL_TYPE_DATA, &args->data); +} + +static int ctl_server(ctl_args_t *args, ctl_cmd_t cmd) +{ + int ret = KNOT_EOK; + + switch (cmd) { + case CTL_STATUS: + ret = server_status(args); + if (ret != KNOT_EOK) { + send_error(args, knot_strerror(ret)); + } + break; + case CTL_STOP: + ret = KNOT_CTL_ESTOP; + break; + case CTL_RELOAD: + ret = server_reload(args->server); + if (ret != KNOT_EOK) { + send_error(args, knot_strerror(ret)); + } + break; + default: + assert(0); + ret = KNOT_EINVAL; + } + + return ret; +} + +static int ctl_stats(ctl_args_t *args, ctl_cmd_t cmd) +{ + const char *section = args->data[KNOT_CTL_IDX_SECTION]; + const char *item = args->data[KNOT_CTL_IDX_ITEM]; + + bool found = (section == NULL) ? true : false; + + // Process server metrics. + if (section == NULL || strcasecmp(section, "server") == 0) { + char value[32]; + knot_ctl_data_t data = { + [KNOT_CTL_IDX_SECTION] = "server", + [KNOT_CTL_IDX_DATA] = value + }; + + for (const stats_item_t *i = server_stats; i->name != NULL; i++) { + if (item != NULL) { + if (found) { + break; + } else if (strcmp(i->name, item) == 0) { + found = true; + } else { + continue; + } + } else { + found = true; + } + + data[KNOT_CTL_IDX_ITEM] = i->name; + int ret = snprintf(value, sizeof(value), "%"PRIu64, + i->val(args->server)); + if (ret <= 0 || ret >= sizeof(value)) { + ret = KNOT_ESPACE; + send_error(args, knot_strerror(ret)); + return ret; + } + + ret = knot_ctl_send(args->ctl, KNOT_CTL_TYPE_DATA, &data); + if (ret != KNOT_EOK) { + send_error(args, knot_strerror(ret)); + return ret; + } + } + } + + // Process modules metrics. + if (section == NULL || strncasecmp(section, "mod-", strlen("mod-")) == 0) { + int ret = modules_stats(conf()->query_modules, args, NULL); + if (ret != KNOT_EOK) { + send_error(args, knot_strerror(ret)); + return ret; + } + + found = true; + } + + if (!found) { + send_error(args, knot_strerror(KNOT_EINVAL)); + return KNOT_EINVAL; + } + + return KNOT_EOK; +} + +static int send_block_data(conf_io_t *io, knot_ctl_data_t *data) +{ + knot_ctl_t *ctl = (knot_ctl_t *)io->misc; + + const yp_item_t *item = (io->key1 != NULL) ? io->key1 : io->key0; + assert(item != NULL); + + char buff[YP_MAX_TXT_DATA_LEN + 1] = "\0"; + + (*data)[KNOT_CTL_IDX_DATA] = buff; + + // Format explicit binary data value. + if (io->data.bin != NULL) { + size_t buff_len = sizeof(buff); + int ret = yp_item_to_txt(item, io->data.bin, io->data.bin_len, buff, + &buff_len, YP_SNOQUOTE); + if (ret != KNOT_EOK) { + return ret; + } + return knot_ctl_send(ctl, KNOT_CTL_TYPE_DATA, data); + // Format all multivalued item data if no specified index. + } else if ((item->flags & YP_FMULTI) && io->data.index == 0) { + size_t values = conf_val_count(io->data.val); + for (size_t i = 0; i < values; i++) { + conf_val(io->data.val); + size_t buff_len = sizeof(buff); + int ret = yp_item_to_txt(item, io->data.val->data, + io->data.val->len, buff,&buff_len, + YP_SNOQUOTE); + if (ret != KNOT_EOK) { + return ret; + } + + knot_ctl_type_t type = (i == 0) ? KNOT_CTL_TYPE_DATA : + KNOT_CTL_TYPE_EXTRA; + ret = knot_ctl_send(ctl, type, data); + if (ret != KNOT_EOK) { + return ret; + } + + conf_val_next(io->data.val); + } + return KNOT_EOK; + // Format singlevalued item data or a specified one from multivalued. + } else { + conf_val(io->data.val); + size_t buff_len = sizeof(buff); + int ret = yp_item_to_txt(item, io->data.val->data, io->data.val->len, + buff, &buff_len, YP_SNOQUOTE); + if (ret != KNOT_EOK) { + return ret; + } + return knot_ctl_send(ctl, KNOT_CTL_TYPE_DATA, data); + } +} + +static int send_block(conf_io_t *io) +{ + knot_ctl_t *ctl = (knot_ctl_t *)io->misc; + + // Get possible error message. + const char *err = io->error.str; + if (err == NULL && io->error.code != KNOT_EOK) { + err = knot_strerror(io->error.code); + } + + knot_ctl_data_t data = { + [KNOT_CTL_IDX_ERROR] = err, + }; + + if (io->key0 != NULL) { + data[KNOT_CTL_IDX_SECTION] = io->key0->name + 1; + } + if (io->key1 != NULL) { + data[KNOT_CTL_IDX_ITEM] = io->key1->name + 1; + } + + // Get the item prefix. + switch (io->type) { + case NEW: data[KNOT_CTL_IDX_FLAGS] = CTL_FLAG_ADD; break; + case OLD: data[KNOT_CTL_IDX_FLAGS] = CTL_FLAG_REM; break; + default: break; + } + + char id[KNOT_DNAME_TXT_MAXLEN + 1] = "\0"; + + // Get the textual item id. + if (io->id_len > 0 && io->key0 != NULL) { + size_t id_len = sizeof(id); + int ret = yp_item_to_txt(io->key0->var.g.id, io->id, io->id_len, + id, &id_len, YP_SNOQUOTE); + if (ret != KNOT_EOK) { + return ret; + } + if (io->id_as_data) { + data[KNOT_CTL_IDX_DATA] = id; + } else { + data[KNOT_CTL_IDX_ID] = id; + } + } + + if (io->data.val == NULL && io->data.bin == NULL) { + return knot_ctl_send(ctl, KNOT_CTL_TYPE_DATA, &data); + } else { + return send_block_data(io, &data); + } +} + +static int ctl_conf_txn(ctl_args_t *args, ctl_cmd_t cmd) +{ + conf_io_t io = { + .fcn = send_block, + .misc = args->ctl + }; + + int ret = KNOT_EOK; + + switch (cmd) { + case CTL_CONF_BEGIN: + ret = conf_io_begin(false); + break; + case CTL_CONF_ABORT: + conf_io_abort(false); + ret = KNOT_EOK; + break; + case CTL_CONF_COMMIT: + // First check the database. + ret = conf_io_check(&io); + if (ret != KNOT_EOK) { + // A semantic error is already sent by the check function. + if (io.error.code != KNOT_EOK) { + return KNOT_EOK; + } + // No transaction abort! + break; + } + + ret = conf_io_commit(false); + if (ret != KNOT_EOK) { + conf_io_abort(false); + break; + } + + ret = server_reload(args->server); + break; + default: + assert(0); + ret = KNOT_EINVAL; + } + + if (ret != KNOT_EOK) { + send_error(args, knot_strerror(ret)); + } + + return ret; +} + +static int ctl_conf_read(ctl_args_t *args, ctl_cmd_t cmd) +{ + conf_io_t io = { + .fcn = send_block, + .misc = args->ctl + }; + + int ret = KNOT_EOK; + + while (true) { + const char *key0 = args->data[KNOT_CTL_IDX_SECTION]; + const char *key1 = args->data[KNOT_CTL_IDX_ITEM]; + const char *id = args->data[KNOT_CTL_IDX_ID]; + + switch (cmd) { + case CTL_CONF_LIST: + ret = conf_io_list(key0, &io); + break; + case CTL_CONF_READ: + ret = conf_io_get(key0, key1, id, true, &io); + break; + case CTL_CONF_DIFF: + ret = conf_io_diff(key0, key1, id, &io); + break; + case CTL_CONF_GET: + ret = conf_io_get(key0, key1, id, false, &io); + break; + default: + assert(0); + ret = KNOT_EINVAL; + } + if (ret != KNOT_EOK) { + send_error(args, knot_strerror(ret)); + break; + } + + // Get next data unit. + ret = knot_ctl_receive(args->ctl, &args->type, &args->data); + if (ret != KNOT_EOK || args->type != KNOT_CTL_TYPE_DATA) { + break; + } + ctl_log_data(&args->data); + } + + return ret; +} + +static int ctl_conf_modify(ctl_args_t *args, ctl_cmd_t cmd) +{ + // Start child transaction. + int ret = conf_io_begin(true); + if (ret != KNOT_EOK) { + send_error(args, knot_strerror(ret)); + return ret; + } + + while (true) { + const char *key0 = args->data[KNOT_CTL_IDX_SECTION]; + const char *key1 = args->data[KNOT_CTL_IDX_ITEM]; + const char *id = args->data[KNOT_CTL_IDX_ID]; + const char *data = args->data[KNOT_CTL_IDX_DATA]; + + switch (cmd) { + case CTL_CONF_SET: + ret = conf_io_set(key0, key1, id, data); + break; + case CTL_CONF_UNSET: + ret = conf_io_unset(key0, key1, id, data); + break; + default: + assert(0); + ret = KNOT_EINVAL; + } + if (ret != KNOT_EOK) { + send_error(args, knot_strerror(ret)); + break; + } + + // Get next data unit. + ret = knot_ctl_receive(args->ctl, &args->type, &args->data); + if (ret != KNOT_EOK || args->type != KNOT_CTL_TYPE_DATA) { + break; + } + ctl_log_data(&args->data); + } + + // Finish child transaction. + if (ret == KNOT_EOK) { + ret = conf_io_commit(true); + if (ret != KNOT_EOK) { + send_error(args, knot_strerror(ret)); + } + } else { + conf_io_abort(true); + } + + return ret; +} + +typedef struct { + const char *name; + int (*fcn)(ctl_args_t *, ctl_cmd_t); +} desc_t; + +static const desc_t cmd_table[] = { + [CTL_NONE] = { "" }, + + [CTL_STATUS] = { "status", ctl_server }, + [CTL_STOP] = { "stop", ctl_server }, + [CTL_RELOAD] = { "reload", ctl_server }, + [CTL_STATS] = { "stats", ctl_stats }, + + [CTL_ZONE_STATUS] = { "zone-status", ctl_zone }, + [CTL_ZONE_RELOAD] = { "zone-reload", ctl_zone }, + [CTL_ZONE_REFRESH] = { "zone-refresh", ctl_zone }, + [CTL_ZONE_RETRANSFER] = { "zone-retransfer", ctl_zone }, + [CTL_ZONE_NOTIFY] = { "zone-notify", ctl_zone }, + [CTL_ZONE_FLUSH] = { "zone-flush", ctl_zone }, + [CTL_ZONE_SIGN] = { "zone-sign", ctl_zone }, + [CTL_ZONE_KSK_SBM] = { "zone-ksk-submitted", ctl_zone }, + [CTL_ZONE_FREEZE] = { "zone-freeze", ctl_zone }, + [CTL_ZONE_THAW] = { "zone-thaw", ctl_zone }, + + [CTL_ZONE_READ] = { "zone-read", ctl_zone }, + [CTL_ZONE_BEGIN] = { "zone-begin", ctl_zone }, + [CTL_ZONE_COMMIT] = { "zone-commit", ctl_zone }, + [CTL_ZONE_ABORT] = { "zone-abort", ctl_zone }, + [CTL_ZONE_DIFF] = { "zone-diff", ctl_zone }, + [CTL_ZONE_GET] = { "zone-get", ctl_zone }, + [CTL_ZONE_SET] = { "zone-set", ctl_zone }, + [CTL_ZONE_UNSET] = { "zone-unset", ctl_zone }, + [CTL_ZONE_PURGE] = { "zone-purge", ctl_zone }, + [CTL_ZONE_STATS] = { "zone-stats", ctl_zone }, + + [CTL_CONF_LIST] = { "conf-list", ctl_conf_read }, + [CTL_CONF_READ] = { "conf-read", ctl_conf_read }, + [CTL_CONF_BEGIN] = { "conf-begin", ctl_conf_txn }, + [CTL_CONF_COMMIT] = { "conf-commit", ctl_conf_txn }, + [CTL_CONF_ABORT] = { "conf-abort", ctl_conf_txn }, + [CTL_CONF_DIFF] = { "conf-diff", ctl_conf_read }, + [CTL_CONF_GET] = { "conf-get", ctl_conf_read }, + [CTL_CONF_SET] = { "conf-set", ctl_conf_modify }, + [CTL_CONF_UNSET] = { "conf-unset", ctl_conf_modify }, +}; + +#define MAX_CTL_CODE (sizeof(cmd_table) / sizeof(desc_t) - 1) + +const char *ctl_cmd_to_str(ctl_cmd_t cmd) +{ + if (cmd <= CTL_NONE || cmd > MAX_CTL_CODE) { + return NULL; + } + + return cmd_table[cmd].name; +} + +ctl_cmd_t ctl_str_to_cmd(const char *cmd_str) +{ + if (cmd_str == NULL) { + return CTL_NONE; + } + + for (ctl_cmd_t cmd = CTL_NONE + 1; cmd <= MAX_CTL_CODE; cmd++) { + if (strcmp(cmd_str, cmd_table[cmd].name) == 0) { + return cmd; + } + } + + return CTL_NONE; +} + +int ctl_exec(ctl_cmd_t cmd, ctl_args_t *args) +{ + if (args == NULL) { + return KNOT_EINVAL; + } + + return cmd_table[cmd].fcn(args, cmd); +} + +bool ctl_has_flag(const char *flags, const char *flag) +{ + if (flags == NULL || flag == NULL) { + return false; + } + + return strstr(flags, flag) != NULL; +} diff --git a/src/knot/ctl/commands.h b/src/knot/ctl/commands.h new file mode 100644 index 0000000..8b6e351 --- /dev/null +++ b/src/knot/ctl/commands.h @@ -0,0 +1,135 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "libknot/libknot.h" +#include "knot/server/server.h" + +#define CTL_FLAG_FORCE "F" +#define CTL_FLAG_ADD "+" +#define CTL_FLAG_REM "-" + +#define CTL_FILTER_FLUSH_OUTDIR 'd' + +#define CTL_FILTER_STATUS_ROLE 'r' +#define CTL_FILTER_STATUS_SERIAL 's' +#define CTL_FILTER_STATUS_TRANSACTION 't' +#define CTL_FILTER_STATUS_FREEZE 'f' +#define CTL_FILTER_STATUS_EVENTS 'e' + +#define CTL_FILTER_PURGE_EXPIRE 'e' +#define CTL_FILTER_PURGE_TIMERS 't' +#define CTL_FILTER_PURGE_ZONEFILE 'f' +#define CTL_FILTER_PURGE_JOURNAL 'j' +#define CTL_FILTER_PURGE_KASPDB 'k' +#define CTL_FILTER_PURGE_ORPHAN 'o' + +/*! Control commands. */ +typedef enum { + CTL_NONE, + + CTL_STATUS, + CTL_STOP, + CTL_RELOAD, + CTL_STATS, + + CTL_ZONE_STATUS, + CTL_ZONE_RELOAD, + CTL_ZONE_REFRESH, + CTL_ZONE_RETRANSFER, + CTL_ZONE_NOTIFY, + CTL_ZONE_FLUSH, + CTL_ZONE_SIGN, + CTL_ZONE_KSK_SBM, + CTL_ZONE_FREEZE, + CTL_ZONE_THAW, + + CTL_ZONE_READ, + CTL_ZONE_BEGIN, + CTL_ZONE_COMMIT, + CTL_ZONE_ABORT, + CTL_ZONE_DIFF, + CTL_ZONE_GET, + CTL_ZONE_SET, + CTL_ZONE_UNSET, + CTL_ZONE_PURGE, + CTL_ZONE_STATS, + + CTL_CONF_LIST, + CTL_CONF_READ, + CTL_CONF_BEGIN, + CTL_CONF_COMMIT, + CTL_CONF_ABORT, + CTL_CONF_DIFF, + CTL_CONF_GET, + CTL_CONF_SET, + CTL_CONF_UNSET, +} ctl_cmd_t; + +/*! Control command parameters. */ +typedef struct { + knot_mm_t mm; + knot_ctl_t *ctl; + knot_ctl_type_t type; + knot_ctl_data_t data; + server_t *server; +} ctl_args_t; + +/*! + * Returns a string equivalent of the command. + * + * \param[in] cmd Command. + * + * \return Command string or NULL. + */ +const char *ctl_cmd_to_str(ctl_cmd_t cmd); + +/*! + * Returns a command corresponding to the string. + * + * \param[in] cmd_str Command string. + * + * \return Command. + */ +ctl_cmd_t ctl_str_to_cmd(const char *cmd_str); + +/*! + * Executes a control command. + * + * \param[in] cmd Control command. + * \param[in] args Command arguments. + * + * \return Error code, KNOT_EOK if successful. + */ +int ctl_exec(ctl_cmd_t cmd, ctl_args_t *args); + +/*! + * Logs control data items at the debug level. + * + * \param[in] data Control data. + */ +void ctl_log_data(knot_ctl_data_t *data); + +/*! + * Checks flag presence in flags. + * + * \param[in] flags Flags to check presence in. + * \param[in] flag Checked flag. + * + * \return True if presented. + */ +bool ctl_has_flag(const char *flags, const char *flag); diff --git a/src/knot/ctl/process.c b/src/knot/ctl/process.c new file mode 100644 index 0000000..d845bed --- /dev/null +++ b/src/knot/ctl/process.c @@ -0,0 +1,128 @@ +/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "contrib/mempattern.h" +#include "contrib/ucw/mempool.h" +#include "knot/common/log.h" +#include "knot/ctl/commands.h" +#include "knot/ctl/process.h" +#include "libknot/error.h" + +int ctl_process(knot_ctl_t *ctl, server_t *server) +{ + if (ctl == NULL || server == NULL) { + return KNOT_EINVAL; + } + + ctl_args_t args = { + .ctl = ctl, + .type = KNOT_CTL_TYPE_END, + .server = server + }; + + mm_ctx_mempool(&args.mm, MM_DEFAULT_BLKSIZE); + + // Strip redundant/unprocessed data units in the current block. + bool strip = false; + + while (true) { + // Receive data unit. + int ret = knot_ctl_receive(args.ctl, &args.type, &args.data); + if (ret != KNOT_EOK) { + log_ctl_debug("control, failed to receive (%s)", + knot_strerror(ret)); + mp_delete(args.mm.ctx); + return ret; + } + + // Decide what to do. + switch (args.type) { + case KNOT_CTL_TYPE_DATA: + // Leading data unit with a command name. + if (!strip) { + // Set to strip unprocessed data unit. + strip = true; + break; + } + // FALLTHROUGH + case KNOT_CTL_TYPE_EXTRA: + // All non-first data units should be parsed in a callback. + // Ignore if probable previous error. + continue; + case KNOT_CTL_TYPE_BLOCK: + strip = false; + continue; + case KNOT_CTL_TYPE_END: + mp_delete(args.mm.ctx); + return KNOT_EOF; + default: + assert(0); + } + + const char *cmd_name = args.data[KNOT_CTL_IDX_CMD]; + const char *zone_name = args.data[KNOT_CTL_IDX_ZONE]; + + ctl_cmd_t cmd = ctl_str_to_cmd(cmd_name); + if (cmd != CTL_NONE) { + if (zone_name != NULL) { + log_ctl_zone_str_info(zone_name, + "control, received command '%s'", cmd_name); + } else { + log_ctl_info("control, received command '%s'", cmd_name); + } + ctl_log_data(&args.data); + } else if (cmd_name != NULL){ + log_ctl_debug("control, invalid command '%s'", cmd_name); + continue; + } else { + log_ctl_debug("control, empty command"); + continue; + } + + // Execute the command. + int cmd_ret = ctl_exec(cmd, &args); + switch (cmd_ret) { + case KNOT_EOK: + strip = false; + case KNOT_CTL_ESTOP: + break; + default: + log_ctl_debug("control, command '%s' (%s)", cmd_name, + knot_strerror(cmd_ret)); + break; + } + + // Finalize the answer block. + ret = knot_ctl_send(ctl, KNOT_CTL_TYPE_BLOCK, NULL); + if (ret != KNOT_EOK) { + log_ctl_debug("control, failed to reply (%s)", + knot_strerror(ret)); + } + + // Stop if required. + if (cmd_ret == KNOT_CTL_ESTOP) { + // Finalize the answer message. + ret = knot_ctl_send(ctl, KNOT_CTL_TYPE_END, NULL); + if (ret != KNOT_EOK) { + log_ctl_debug("control, failed to reply (%s)", + knot_strerror(ret)); + } + + mp_delete(args.mm.ctx); + return cmd_ret; + } + } +} diff --git a/src/knot/ctl/process.h b/src/knot/ctl/process.h new file mode 100644 index 0000000..8236bfc --- /dev/null +++ b/src/knot/ctl/process.h @@ -0,0 +1,30 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "libknot/libknot.h" +#include "knot/server/server.h" + +/*! + * Processes incoming control commands. + * + * \param[in] ctl Control context. + * \param[in] server Server instance. + * + * \return Error code, KNOT_EOK if successful. + */ +int ctl_process(knot_ctl_t *ctl, server_t *server); diff --git a/src/knot/dnssec/context.c b/src/knot/dnssec/context.c new file mode 100644 index 0000000..c3c6f51 --- /dev/null +++ b/src/knot/dnssec/context.c @@ -0,0 +1,218 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stdio.h> +#include <string.h> + +#include "contrib/time.h" +#include "libknot/libknot.h" +#include "knot/dnssec/context.h" +#include "knot/dnssec/kasp/keystore.h" + +dynarray_define(parent, knot_kasp_parent_t, DYNARRAY_VISIBILITY_PUBLIC) + +static void policy_load(knot_kasp_policy_t *policy, conf_val_t *id) +{ + if (conf_str(id) == NULL) { + policy->string = strdup("default"); + } else { + policy->string = strdup(conf_str(id)); + } + + conf_val_t val = conf_id_get(conf(), C_POLICY, C_MANUAL, id); + policy->manual = conf_bool(&val); + + val = conf_id_get(conf(), C_POLICY, C_SINGLE_TYPE_SIGNING, id); + policy->singe_type_signing = conf_bool(&val); + + val = conf_id_get(conf(), C_POLICY, C_ALG, id); + policy->algorithm = conf_opt(&val); + + val = conf_id_get(conf(), C_POLICY, C_KSK_SHARED, id); + policy->ksk_shared = conf_bool(&val); + + val = conf_id_get(conf(), C_POLICY, C_KSK_SIZE, id); + int64_t num = conf_int(&val); + policy->ksk_size = (num != YP_NIL) ? num : + dnssec_algorithm_key_size_default(policy->algorithm); + + val = conf_id_get(conf(), C_POLICY, C_ZSK_SIZE, id); + num = conf_int(&val); + policy->zsk_size = (num != YP_NIL) ? num : + dnssec_algorithm_key_size_default(policy->algorithm); + + val = conf_id_get(conf(), C_POLICY, C_DNSKEY_TTL, id); + int64_t ttl = conf_int(&val); + policy->dnskey_ttl = (ttl != YP_NIL) ? ttl : UINT32_MAX; + + val = conf_id_get(conf(), C_POLICY, C_ZSK_LIFETIME, id); + policy->zsk_lifetime = conf_int(&val); + + val = conf_id_get(conf(), C_POLICY, C_KSK_LIFETIME, id); + policy->ksk_lifetime = conf_int(&val); + + val = conf_id_get(conf(), C_POLICY, C_PROPAG_DELAY, id); + policy->propagation_delay = conf_int(&val); + + val = conf_id_get(conf(), C_POLICY, C_RRSIG_LIFETIME, id); + policy->rrsig_lifetime = conf_int(&val); + + val = conf_id_get(conf(), C_POLICY, C_RRSIG_REFRESH, id); + policy->rrsig_refresh_before = conf_int(&val); + + val = conf_id_get(conf(), C_POLICY, C_NSEC3, id); + policy->nsec3_enabled = conf_bool(&val); + + val = conf_id_get(conf(), C_POLICY, C_NSEC3_OPT_OUT, id); + policy->nsec3_opt_out = conf_bool(&val); + + val = conf_id_get(conf(), C_POLICY, C_NSEC3_ITER, id); + policy->nsec3_iterations = conf_int(&val); + + val = conf_id_get(conf(), C_POLICY, C_NSEC3_SALT_LEN, id); + policy->nsec3_salt_length = conf_int(&val); + + val = conf_id_get(conf(), C_POLICY, C_NSEC3_SALT_LIFETIME, id); + policy->nsec3_salt_lifetime = conf_int(&val); + + val = conf_id_get(conf(), C_POLICY, C_CHILD_RECORDS, id); + policy->child_records_publish = conf_opt(&val); + + conf_val_t ksk_sbm = conf_id_get(conf(), C_POLICY, C_KSK_SBM, id); + if (ksk_sbm.code == KNOT_EOK) { + val = conf_id_get(conf(), C_SBM, C_CHK_INTERVAL, &ksk_sbm); + policy->ksk_sbm_check_interval = conf_int(&val); + + val = conf_id_get(conf(), C_SBM, C_TIMEOUT, &ksk_sbm); + policy->ksk_sbm_timeout = conf_int(&val); + + val = conf_id_get(conf(), C_SBM, C_PARENT, &ksk_sbm); + while (val.code == KNOT_EOK) { + conf_val_t addr = conf_id_get(conf(), C_RMT, C_ADDR, &val); + knot_kasp_parent_t p = { .addrs = conf_val_count(&addr) }; + p.addr = p.addrs ? malloc(p.addrs * sizeof(*p.addr)) : NULL; + if (p.addr != NULL) { + for (size_t i = 0; i < p.addrs; i++) { + p.addr[i] = conf_remote(conf(), &val, i); + } + parent_dynarray_add(&policy->parents, &p); + } + conf_val_next(&val); + } + } +} + +int kdnssec_ctx_init(conf_t *conf, kdnssec_ctx_t *ctx, const knot_dname_t *zone_name, + const conf_mod_id_t *from_module) +{ + if (ctx == NULL || zone_name == NULL) { + return KNOT_EINVAL; + } + + int ret; + + memset(ctx, 0, sizeof(*ctx)); + + ctx->zone = calloc(1, sizeof(*ctx->zone)); + if (ctx->zone == NULL) { + ret = KNOT_ENOMEM; + goto init_error; + } + ctx->kasp_db = kaspdb(); + + ret = kasp_db_open(*ctx->kasp_db); + if (ret != KNOT_EOK) { + goto init_error; + } + + ret = kasp_zone_load(ctx->zone, zone_name, *ctx->kasp_db); + if (ret != KNOT_EOK) { + goto init_error; + } + + ctx->kasp_zone_path = conf_kaspdir(conf); + if (ctx->kasp_zone_path == NULL) { + ret = KNOT_ENOMEM; + goto init_error; + } + + ctx->policy = calloc(1, sizeof(*ctx->policy)); + if (ctx->policy == NULL) { + ret = KNOT_ENOMEM; + goto init_error; + } + + conf_val_t policy_id; + if (from_module == NULL) { + policy_id = conf_zone_get(conf, C_DNSSEC_POLICY, zone_name); + } else { + policy_id = conf_mod_get(conf, C_POLICY, from_module); + } + conf_id_fix_default(&policy_id); + policy_load(ctx->policy, &policy_id); + + conf_val_t keystore_id = conf_id_get(conf, C_POLICY, C_KEYSTORE, &policy_id); + conf_id_fix_default(&keystore_id); + + conf_val_t val = conf_id_get(conf, C_KEYSTORE, C_BACKEND, &keystore_id); + unsigned backend = conf_opt(&val); + + val = conf_id_get(conf, C_KEYSTORE, C_CONFIG, &keystore_id); + const char *config = conf_str(&val); + + ret = keystore_load(config, backend, ctx->kasp_zone_path, &ctx->keystore); + if (ret != KNOT_EOK) { + goto init_error; + } + + ctx->now = knot_time(); + + return KNOT_EOK; +init_error: + kdnssec_ctx_deinit(ctx); + return ret; +} + +int kdnssec_ctx_commit(kdnssec_ctx_t *ctx) +{ + if (ctx == NULL || ctx->kasp_zone_path == NULL) { + return KNOT_EINVAL; + } + + // do something with keytore? Probably not.. + + return kasp_zone_save(ctx->zone, ctx->zone->dname, *ctx->kasp_db); +} + +void kdnssec_ctx_deinit(kdnssec_ctx_t *ctx) +{ + if (ctx == NULL) { + return; + } + + if (ctx->policy != NULL) { + free(ctx->policy->string); + dynarray_foreach(parent, knot_kasp_parent_t, i, ctx->policy->parents) { + free(i->addr); + } + free(ctx->policy); + } + dnssec_keystore_deinit(ctx->keystore); + kasp_zone_free(&ctx->zone); + free(ctx->kasp_zone_path); + + memset(ctx, 0, sizeof(*ctx)); +} diff --git a/src/knot/dnssec/context.h b/src/knot/dnssec/context.h new file mode 100644 index 0000000..77174c2 --- /dev/null +++ b/src/knot/dnssec/context.h @@ -0,0 +1,62 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include <time.h> + +#include "libdnssec/keystore.h" + +#include "knot/conf/conf.h" +#include "knot/dnssec/kasp/kasp_zone.h" +#include "knot/dnssec/kasp/policy.h" + +/*! + * \brief DNSSEC signing context. + */ +typedef struct { + knot_time_t now; + + kasp_db_t **kasp_db; + knot_kasp_zone_t *zone; + knot_kasp_policy_t *policy; + dnssec_keystore_t *keystore; + + char *kasp_zone_path; + + bool rrsig_drop_existing; +} kdnssec_ctx_t; + +/*! + * \brief Initialize DNSSEC signing context. + * + * \param conf Configuration. + * \param ctx Signing context to be initialized. + * \param zone_name Name of the zone. + * \param from_module Module identifier if initialized from a module. + */ +int kdnssec_ctx_init(conf_t *conf, kdnssec_ctx_t *ctx, const knot_dname_t *zone_name, + const conf_mod_id_t *from_module); + +/*! + * \brief Save the changes in ctx (in kasp zone). + */ +int kdnssec_ctx_commit(kdnssec_ctx_t *ctx); + +/*! + * \brief Cleanup DNSSEC signing context. + */ +void kdnssec_ctx_deinit(kdnssec_ctx_t *ctx); diff --git a/src/knot/dnssec/ds_query.c b/src/knot/dnssec/ds_query.c new file mode 100644 index 0000000..89e5c4a --- /dev/null +++ b/src/knot/dnssec/ds_query.c @@ -0,0 +1,244 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> + +#include "contrib/macros.h" +#include "knot/common/log.h" +#include "knot/conf/conf.h" +#include "knot/dnssec/ds_query.h" +#include "knot/dnssec/key-events.h" +#include "knot/query/layer.h" +#include "knot/query/query.h" +#include "knot/query/requestor.h" + +static bool match_key_ds(zone_key_t *key, knot_rdata_t *ds) +{ + assert(key); + assert(ds); + + dnssec_binary_t ds_rdata = { + .size = ds->len, + .data = ds->data, + }; + + dnssec_binary_t cds_rdata = { 0 }; + + int ret = zone_key_calculate_ds(key, &cds_rdata); + if (ret != KNOT_EOK) { + return false; + } + + return (dnssec_binary_cmp(&cds_rdata, &ds_rdata) == 0); +} + +struct ds_query_data { + const knot_dname_t *zone_name; + const struct sockaddr *remote; + + zone_key_t *key; + + uint16_t edns_max_payload; + + bool ds_ok; + bool result_logged; + + uint32_t ttl; +}; + +static int ds_query_begin(knot_layer_t *layer, void *params) +{ + layer->data = params; + + return KNOT_STATE_PRODUCE; +} + +static int ds_query_produce(knot_layer_t *layer, knot_pkt_t *pkt) +{ + struct ds_query_data *data = layer->data; + struct query_edns_data edns = { .max_payload = data->edns_max_payload, .do_flag = true, }; + + query_init_pkt(pkt); + + int r = knot_pkt_put_question(pkt, data->zone_name, KNOT_CLASS_IN, KNOT_RRTYPE_DS); + if (r != KNOT_EOK) { + return KNOT_STATE_FAIL; + } + + r = query_put_edns(pkt, &edns); + if (r != KNOT_EOK) { + return KNOT_STATE_FAIL; + } + + knot_wire_set_rd(pkt->wire); + + return KNOT_STATE_CONSUME; +} + +static int ds_query_consume(knot_layer_t *layer, knot_pkt_t *pkt) +{ + struct ds_query_data *data = layer->data; + data->result_logged = true; + + uint16_t rcode = knot_pkt_ext_rcode(pkt); + if (rcode != KNOT_RCODE_NOERROR) { + ns_log((rcode == KNOT_RCODE_NXDOMAIN ? LOG_NOTICE : LOG_WARNING), + data->zone_name, LOG_OPERATION_PARENT, + LOG_DIRECTION_OUT, data->remote, "failed (%s)", knot_pkt_ext_rcode_name(pkt)); + return KNOT_STATE_FAIL; + } + + const knot_pktsection_t *answer = knot_pkt_section(pkt, KNOT_ANSWER); + + bool match = false; + + for (size_t j = 0; j < answer->count; j++) { + const knot_rrset_t *rr = knot_pkt_rr(answer, j); + switch ((rr && rr->rrs.count > 0) ? rr->type : 0) { + case KNOT_RRTYPE_DS: + if (match_key_ds(data->key, rr->rrs.rdata)) { + match = true; + if (data->ttl == 0) { // fallback: if there is no RRSIG + data->ttl = rr->ttl; + } + } + break; + case KNOT_RRTYPE_RRSIG: + data->ttl = knot_rrsig_original_ttl(rr->rrs.rdata); + break; + default: + break; + } + } + + ns_log(LOG_INFO, data->zone_name, LOG_OPERATION_PARENT, + LOG_DIRECTION_OUT, data->remote, "KSK submission attempt: %s", + (match ? "positive" : "negative")); + + if (match) { + data->ds_ok = true; + } + return KNOT_STATE_DONE; +} + +static const knot_layer_api_t ds_query_api = { + .begin = ds_query_begin, + .produce = ds_query_produce, + .consume = ds_query_consume, + .reset = NULL, + .finish = NULL, +}; + +static int try_ds(const knot_dname_t *zone_name, const conf_remote_t *parent, zone_key_t *key, + size_t timeout, uint32_t *ds_ttl) +{ + // TODO: Abstract interface to issue DNS queries. This is almost copy-pasted. + + assert(zone_name); + assert(parent); + + struct ds_query_data data = { + .zone_name = zone_name, + .remote = (struct sockaddr *)&parent->addr, + .key = key, + .ds_ok = false, + .result_logged = false, + .ttl = 0, + }; + + struct knot_requestor requestor; + knot_requestor_init(&requestor, &ds_query_api, &data, NULL); + + knot_pkt_t *pkt = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, NULL); + if (!pkt) { + knot_requestor_clear(&requestor); + return KNOT_ENOMEM; + } + + const struct sockaddr *dst = (struct sockaddr *)&parent->addr; + const struct sockaddr *src = (struct sockaddr *)&parent->via; + struct knot_request *req = knot_request_make(NULL, dst, src, pkt, &parent->key, 0); + if (!req) { + knot_request_free(req, NULL); + knot_requestor_clear(&requestor); + return KNOT_ENOMEM; + } + + data.edns_max_payload = dst->sa_family == AF_INET6 ? + conf()->cache.srv_max_ipv6_udp_payload : + conf()->cache.srv_max_ipv4_udp_payload; + + int ret = knot_requestor_exec(&requestor, req, timeout); + knot_request_free(req, NULL); + knot_requestor_clear(&requestor); + + // alternative: we could put answer back through ctx instead of errcode + if (ret == KNOT_EOK && !data.ds_ok) { + ret = KNOT_ENORECORD; + } + + if (ret != KNOT_EOK && !data.result_logged) { + ns_log(LOG_WARNING, zone_name, LOG_OPERATION_PARENT, + LOG_DIRECTION_OUT, data.remote, "failed (%s)", knot_strerror(ret)); + } + + *ds_ttl = data.ttl; + + return ret; +} + +static bool parents_have_ds(kdnssec_ctx_t *kctx, zone_key_t *key, size_t timeout, + uint32_t *max_ds_ttl) +{ + bool success = false; + dynarray_foreach(parent, knot_kasp_parent_t, i, kctx->policy->parents) { + success = false; + for (size_t j = 0; j < i->addrs; j++) { + uint32_t ds_ttl = 0; + int ret = try_ds(kctx->zone->dname, &i->addr[j], key, timeout, &ds_ttl); + if (ret == KNOT_EOK) { + *max_ds_ttl = MAX(*max_ds_ttl, ds_ttl); + success = true; + break; + } else if (ret == KNOT_ENORECORD) { + // parent was queried successfully, answer was negative + break; + } + } + // Each parent must succeed. + if (!success) { + return false; + } + } + return success; +} + +int knot_parent_ds_query(kdnssec_ctx_t *kctx, zone_keyset_t *keyset, size_t timeout) +{ + uint32_t max_ds_ttl = 0; + + for (size_t i = 0; i < keyset->count; i++) { + zone_key_t *key = &keyset->keys[i]; + if (key->is_ksk && key->cds_priority > 1) { + if (parents_have_ds(kctx, key, timeout, &max_ds_ttl)) { + return knot_dnssec_ksk_sbm_confirm(kctx, max_ds_ttl); + } else { + return KNOT_ENOENT; + } + } + } + return KNOT_ENOENT; +} diff --git a/src/knot/dnssec/ds_query.h b/src/knot/dnssec/ds_query.h new file mode 100644 index 0000000..5ef40a4 --- /dev/null +++ b/src/knot/dnssec/ds_query.h @@ -0,0 +1,22 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "knot/dnssec/zone-keys.h" +#include "knot/dnssec/context.h" + +int knot_parent_ds_query(kdnssec_ctx_t *kctx, zone_keyset_t *keyset, size_t timeout); diff --git a/src/knot/dnssec/kasp/kasp_db.c b/src/knot/dnssec/kasp/kasp_db.c new file mode 100644 index 0000000..7b48115 --- /dev/null +++ b/src/knot/dnssec/kasp/kasp_db.c @@ -0,0 +1,751 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "knot/dnssec/kasp/kasp_db.h" + +#include <stdarg.h> // just for va_free() +#include <pthread.h> +#include <sys/stat.h> + +#include "contrib/files.h" +#include "contrib/wire_ctx.h" + +struct kasp_db { + knot_db_t *keys_db; + char *db_path; + size_t db_mapsize; + pthread_mutex_t opening_mutex; +}; + +typedef enum { + KASPDBKEY_PARAMS = 0x1, + KASPDBKEY_POLICYLAST = 0x2, + KASPDBKEY_NSEC3SALT = 0x3, + KASPDBKEY_NSEC3TIME = 0x4, + KASPDBKEY_MASTERSERIAL = 0x5, + KASPDBKEY_LASTSIGNEDSERIAL = 0x6, +} keyclass_t; + +static const knot_db_api_t *db_api = NULL; + +static kasp_db_t *global_kasp_db = NULL; + +kasp_db_t **kaspdb(void) +{ + return &global_kasp_db; +} + +int kasp_db_init(kasp_db_t **db, const char *path, size_t mapsize) +{ + if (db == NULL || path == NULL || *db != NULL) { + return KNOT_EINVAL; + } + + db_api = knot_db_lmdb_api(); + + *db = calloc(1, sizeof(**db)); + if (*db == NULL) { + return KNOT_ENOMEM; + } + + (*db)->db_path = strdup(path); + if ((*db)->db_path == NULL) { + free(*db); + return KNOT_ENOMEM; + } + + (*db)->db_mapsize = mapsize; + + pthread_mutex_init(&(*db)->opening_mutex, NULL); + return KNOT_EOK; +} + +int kasp_db_reconfigure(kasp_db_t **db, const char *new_path, size_t new_mapsize) +{ + if (db == NULL || new_path == NULL || *db == NULL || (*db)->db_path == NULL) { + return KNOT_EINVAL; + } + + pthread_mutex_lock(&(*db)->opening_mutex); + + bool changed_path = (strcmp(new_path, (*db)->db_path) != 0); + bool changed_mapsize = (new_mapsize != (*db)->db_mapsize); + + if ((*db)->keys_db != NULL) { + pthread_mutex_unlock(&(*db)->opening_mutex); + if (changed_path) { + return KNOT_EBUSY; + } else if (changed_mapsize) { + return KNOT_EEXIST; + } else { + return KNOT_ENODIFF; + } + } + + free((*db)->db_path); + (*db)->db_path = strdup(new_path); + if ((*db)->db_path == NULL) { + pthread_mutex_unlock(&(*db)->opening_mutex); + return KNOT_ENOMEM; + } + (*db)->db_mapsize = new_mapsize; + + pthread_mutex_unlock(&(*db)->opening_mutex); + return KNOT_EOK; +} + +bool kasp_db_exists(kasp_db_t *db) +{ + if (db->keys_db == NULL) { + struct stat st; + if (stat(db->db_path, &st) != 0) { + return false; + } + } + return true; +} + +int kasp_db_open(kasp_db_t *db) +{ + if (db == NULL || db->db_path == NULL) { + return KNOT_EINVAL; + } + + pthread_mutex_lock(&db->opening_mutex); + + if (db->keys_db != NULL) { + pthread_mutex_unlock(&db->opening_mutex); + return KNOT_EOK; // already open + } + + int ret = make_dir(db->db_path, S_IRWXU | S_IRGRP | S_IXGRP, true); + if (ret != KNOT_EOK) { + pthread_mutex_unlock(&db->opening_mutex); + return ret; + } + + struct knot_db_lmdb_opts opts = KNOT_DB_LMDB_OPTS_INITIALIZER; + opts.path = db->db_path; + opts.mapsize = db->db_mapsize; + opts.maxdbs = 1; + opts.dbname = "keys_db"; + + ret = db_api->init(&db->keys_db, NULL, &opts); + if (ret != KNOT_EOK) { + pthread_mutex_unlock(&db->opening_mutex); + return ret; + } + + pthread_mutex_unlock(&db->opening_mutex); + + return ret; +} + +void kasp_db_close(kasp_db_t **db) +{ + if (db != NULL && *db != NULL) { + pthread_mutex_lock(&(*db)->opening_mutex); + db_api->deinit((*db)->keys_db); + (*db)->keys_db = NULL; + pthread_mutex_unlock(&(*db)->opening_mutex); + free((*db)->db_path); + pthread_mutex_destroy(&(*db)->opening_mutex); + free(*db); + *db = NULL; + } +} + +static knot_db_val_t make_key(keyclass_t kclass, const knot_dname_t *dname, const char *str) +{ + size_t dnlen = knot_dname_size(dname); + size_t slen = (str == NULL ? 0 : strlen(str) + 1); + knot_db_val_t res = { .len = 1 + dnlen + slen, .data = malloc(1 + dnlen + slen) }; + if (res.data != NULL) { + wire_ctx_t wire = wire_ctx_init(res.data, res.len); + wire_ctx_write_u8(&wire, (uint8_t)kclass); + wire_ctx_write(&wire, dname, dnlen); + wire_ctx_write(&wire, str, slen); + } else { + res.len = 0; + } + return res; +} + +static void free_key(knot_db_val_t *key) +{ + free(key->data); + memset(key, 0, sizeof(*key)); +} + +static char *keyid_fromkey(const knot_db_val_t *key) +{ + if (key->len < 2 || *(uint8_t *)key->data != KASPDBKEY_PARAMS) { + return NULL; + } + size_t skip = knot_dname_size((const uint8_t *)key->data + 1); + return (key->len < skip + 2 ? NULL : strdup(key->data + skip + 1)); +} + +static bool check_key_zone(const knot_db_val_t *key, const knot_dname_t *zone_name) +{ + if (key->len < 2 || *(uint8_t *)key->data == KASPDBKEY_POLICYLAST) { + return false; + } + return knot_dname_is_equal(key->data + 1, zone_name); +} + +static int serialize_key_params(const key_params_t *params, const knot_dname_t *dname, knot_db_val_t *key, knot_db_val_t *val) +{ + assert(params != NULL); + assert(dname != NULL); + assert(key != NULL); + assert(val != NULL); + + *key = make_key(KASPDBKEY_PARAMS, dname, params->id); + val->len = sizeof(uint16_t) + 2 * sizeof(uint8_t) + 11 * sizeof(uint64_t) + + params->public_key.size; + val->data = malloc(val->len); + if (val->data == NULL) { + free(key->data); + key->data = NULL; + key->len = 0; + return KNOT_ENOMEM; + } + wire_ctx_t wire = wire_ctx_init(val->data, val->len); + + wire_ctx_write_u64(&wire, params->public_key.size); + wire_ctx_write_u64(&wire, 0); // length of Unused-future block at the end + wire_ctx_write_u16(&wire, params->keytag); + wire_ctx_write_u8(&wire, params->algorithm); + uint8_t flags = 0x02; + flags |= (params->is_ksk ? 0x01 : 0); + flags |= (params->is_pub_only ? 0x04 : 0); + flags |= (params->is_csk ? 0x08 : 0); + wire_ctx_write_u8(&wire, flags); + wire_ctx_write_u64(&wire, (uint64_t)params->timing.created); + wire_ctx_write_u64(&wire, (uint64_t)params->timing.pre_active); + wire_ctx_write_u64(&wire, (uint64_t)params->timing.publish); + wire_ctx_write_u64(&wire, (uint64_t)params->timing.ready); + wire_ctx_write_u64(&wire, (uint64_t)params->timing.active); + wire_ctx_write_u64(&wire, (uint64_t)params->timing.retire_active); + wire_ctx_write_u64(&wire, (uint64_t)params->timing.retire); + wire_ctx_write_u64(&wire, (uint64_t)params->timing.post_active); + wire_ctx_write_u64(&wire, (uint64_t)params->timing.remove); + wire_ctx_write(&wire, params->public_key.data, params->public_key.size); + + if (wire.error != KNOT_EOK) { + free(key->data); + free(val->data); + key->data = NULL; + key->len = 0; + val->data = NULL; + val->len = 0; + return KNOT_ERROR; + } + return KNOT_EOK; +} + +static int deserialize_key_params(key_params_t *params, const knot_db_val_t *key, const knot_db_val_t *val) +{ + assert(params != NULL); + assert(key != NULL); + assert(val != NULL); + assert(key->data != NULL); + assert(val->data != NULL); + assert(val->len >= sizeof(uint64_t)); + + wire_ctx_t wire = wire_ctx_init_const(val->data, val->len); + params->public_key.size = wire_ctx_read_u64(&wire); + uint64_t unused_future_length = wire_ctx_read_u64(&wire); + params->keytag = wire_ctx_read_u16(&wire); + params->algorithm = wire_ctx_read_u8(&wire); + uint8_t isksk_plus_flags = wire_ctx_read_u8(&wire); + params->is_ksk = ((isksk_plus_flags & (uint8_t)0x01) != (uint8_t)0x00); + params->is_pub_only = ((isksk_plus_flags & (uint8_t)0x04) != (uint8_t)0x00); + params->is_csk = ((isksk_plus_flags & (uint8_t)0x08) != (uint8_t)0x00); + if (params->is_csk && !params->is_ksk) { + return KNOT_EMALF; + } + if ((isksk_plus_flags & (uint8_t)0x02) != (uint8_t)0x00) { + params->timing.created = (knot_time_t)wire_ctx_read_u64(&wire); + params->timing.pre_active = (knot_time_t)wire_ctx_read_u64(&wire); + params->timing.publish = (knot_time_t)wire_ctx_read_u64(&wire); + params->timing.ready = (knot_time_t)wire_ctx_read_u64(&wire); + params->timing.active = (knot_time_t)wire_ctx_read_u64(&wire); + params->timing.retire_active = (knot_time_t)wire_ctx_read_u64(&wire); + params->timing.retire = (knot_time_t)wire_ctx_read_u64(&wire); + params->timing.post_active = (knot_time_t)wire_ctx_read_u64(&wire); + params->timing.remove = (knot_time_t)wire_ctx_read_u64(&wire); + } else { + // import of old kasp db format missing some timers + params->timing.created = (knot_time_t)wire_ctx_read_u64(&wire); + params->timing.pre_active = 0; + params->timing.publish = (knot_time_t)wire_ctx_read_u64(&wire); + params->timing.ready = (knot_time_t)wire_ctx_read_u64(&wire); + params->timing.active = (knot_time_t)wire_ctx_read_u64(&wire); + params->timing.retire_active = 0; + params->timing.retire = (knot_time_t)wire_ctx_read_u64(&wire); + params->timing.post_active = 0; + params->timing.remove = (knot_time_t)wire_ctx_read_u64(&wire); + } + if (wire.error != KNOT_EOK) { + return KNOT_ERROR; + } + + free(params->public_key.data); + params->public_key.data = malloc(params->public_key.size); + if (params->public_key.data == NULL) { + return KNOT_ENOMEM; + } + wire_ctx_read(&wire, params->public_key.data, params->public_key.size); + + free(params->id); + params->id = keyid_fromkey(key); + if (params->id == NULL) { + wire.error = KNOT_EMALF; + } + + if (wire.error != KNOT_EOK || wire_ctx_available(&wire) != unused_future_length) { + free(params->id); + free(params->public_key.data); + params->id = NULL; + params->public_key.data = NULL; + params->public_key.size = 0; + return KNOT_EMALF; + } + return KNOT_EOK; +} + +static key_params_t *keyval2params(const knot_db_val_t *key, const knot_db_val_t *val) +{ + key_params_t *res = calloc(1, sizeof(*res)); + if (res != NULL) { + if (deserialize_key_params(res, key, val) != KNOT_EOK) { + free(res); + return NULL; + } + } + return res; +} + +#define txn_check(...) \ + if (ret != KNOT_EOK) { \ + db_api->txn_abort(txn); \ + va_free(NULL, __VA_ARGS__); \ + return ret; \ + } \ + +#define with_txn(what, ...) \ + int ret = KNOT_EOK; \ + knot_db_txn_t local_txn, *txn = &local_txn; \ + ret = db_api->txn_begin(db->keys_db, txn, (what & 0x1) ? 0 : KNOT_DB_RDONLY); \ + txn_check(__VA_ARGS__); \ + +#define with_txn_end(...) \ + txn_check(__VA_ARGS__); \ + ret = db_api->txn_commit(txn); \ + if (ret != KNOT_EOK) { \ + db_api->txn_abort(txn); \ + } \ + +#define KEYS_RO 0x0 +#define KEYS_RW 0x1 + +// TODO move elsewhere +static void va_free(void *p, ...) +{ + va_list args; + va_start(args, p); + for (void *f = p; f != NULL; f = va_arg(args, void *)) { + free(f); + } + va_end(args); +} + +int kasp_db_list_keys(kasp_db_t *db, const knot_dname_t *zone_name, list_t *dst) +{ + if (db == NULL || db->keys_db == NULL || zone_name == NULL || dst == NULL) { + return KNOT_ENOENT; + } + + knot_db_val_t key = make_key(KASPDBKEY_PARAMS, zone_name, NULL), val = { 0 }; + + with_txn(KEYS_RO, NULL); + knot_db_iter_t *iter = db_api->iter_begin(txn, KNOT_DB_NOOP); + if (iter != NULL) { + iter = db_api->iter_seek(iter, &key, KNOT_DB_GEQ); + } + free_key(&key); + + init_list(dst); + while (iter != NULL && ret == KNOT_EOK) { + ret = db_api->iter_key(iter, &key); + if (ret != KNOT_EOK || *(uint8_t *)key.data != KASPDBKEY_PARAMS || !check_key_zone(&key, zone_name)) { + break; + } + ret = db_api->iter_val(iter, &val); + if (ret == KNOT_EOK) { + key_params_t *parm = keyval2params(&key, &val); + if (parm != NULL) { + ptrlist_add(dst, parm, NULL); + } + iter = db_api->iter_next(iter); + } + } + db_api->iter_finish(iter); + db_api->txn_abort(txn); + + if (ret != KNOT_EOK) { + ptrlist_deep_free(dst, NULL); + return ret; + } + return (EMPTY_LIST(*dst) ? KNOT_ENOENT : KNOT_EOK); +} + +static bool keyid_inuse(knot_db_txn_t *txn, const char *key_id, key_params_t **optional) +{ + knot_db_iter_t *iter = db_api->iter_begin(txn, KNOT_DB_FIRST); + while (iter != NULL) { + knot_db_val_t key, val; + if (db_api->iter_key(iter, &key) == KNOT_EOK && *(uint8_t *)key.data == KASPDBKEY_PARAMS) { + char *keyid = keyid_fromkey(&key); + if (keyid != NULL && strcmp(keyid, key_id) == 0) { + if (optional != NULL && db_api->iter_val(iter, &val) == KNOT_EOK) { + *optional = keyval2params(&key, &val); + } + db_api->iter_finish(iter); + free(keyid); + return true; + } + free(keyid); + } + iter = db_api->iter_next(iter); + } + db_api->iter_finish(iter); + return false; +} + +int kasp_db_delete_key(kasp_db_t *db, const knot_dname_t *zone_name, const char *key_id, bool *still_used) +{ + if (db == NULL || db->keys_db == NULL || zone_name == NULL || key_id == NULL) { + return KNOT_EINVAL; + } + + knot_db_val_t key = make_key(KASPDBKEY_PARAMS, zone_name, key_id); + + with_txn(KEYS_RW, key.data, NULL); + ret = db_api->del(txn, &key); + free_key(&key); + if (still_used != NULL) { + *still_used = keyid_inuse(txn, key_id, NULL); + } + with_txn_end(NULL, NULL); + return ret; +} + +int kasp_db_delete_all(kasp_db_t *db, const knot_dname_t *zone_name) +{ + list_t allkeys; + init_list(&allkeys); + int r = kasp_db_list_keys(db, zone_name, &allkeys); + if (r != KNOT_EOK) { + return r; + } + + with_txn(KEYS_RW, NULL); + + ptrnode_t *n; + WALK_LIST(n, allkeys) { + key_params_t *parm = n->d; + knot_db_val_t key = make_key(KASPDBKEY_PARAMS, zone_name, parm->id); + (void)db_api->del(txn, &key); + free_key(&key); + free(parm->id); + free(parm->public_key.data); + memset(parm, 0, sizeof(*parm)); + } + ptrlist_deep_free(&allkeys, NULL); + + for (keyclass_t keyclass = KASPDBKEY_NSEC3SALT; keyclass <= KASPDBKEY_LASTSIGNEDSERIAL; keyclass++) { + knot_db_val_t key = make_key(keyclass, zone_name, NULL); + (void)db_api->del(txn, &key); + free_key(&key); + } + + with_txn_end(NULL, NULL); + return ret; +} + +int kasp_db_add_key(kasp_db_t *db, const knot_dname_t *zone_name, const key_params_t *params) +{ + if (db == NULL || db->keys_db == NULL || zone_name == NULL || params == NULL) { + return KNOT_EINVAL; + } + + knot_db_val_t key = { 0 }, val = { 0 }; + + with_txn(KEYS_RW, NULL); + ret = serialize_key_params(params, zone_name, &key, &val); + txn_check(NULL); + ret = db_api->insert(txn, &key, &val, 0); + free_key(&key); + free_key(&val); + with_txn_end(NULL, NULL); + return ret; +} + +int kasp_db_share_key(kasp_db_t *db, const knot_dname_t *zone_from, const knot_dname_t *zone_to, const char *key_id) +{ + if (db == NULL || db->keys_db == NULL || zone_from == NULL || + zone_to == NULL || key_id == NULL) { + return KNOT_EINVAL; + } + + knot_db_val_t key_from = make_key(KASPDBKEY_PARAMS, zone_from, key_id), + key_to = make_key(KASPDBKEY_PARAMS, zone_to, key_id), val = { 0 }; + + with_txn(KEYS_RW, NULL); + ret = db_api->find(txn, &key_from, &val, 0); + txn_check(txn, key_from.data, key_to.data, NULL); + ret = db_api->insert(txn, &key_to, &val, 0); + free_key(&key_from); + free_key(&key_to); + with_txn_end(NULL); + return ret; +} + +int kasp_db_store_nsec3salt(kasp_db_t *db, const knot_dname_t *zone_name, + const dnssec_binary_t *nsec3salt, knot_time_t salt_created) +{ + if (db == NULL || db->keys_db == NULL || + zone_name == NULL || nsec3salt == NULL || salt_created <= 0) { + return KNOT_EINVAL; + } + + with_txn(KEYS_RW, NULL); + knot_db_val_t key = make_key(KASPDBKEY_NSEC3SALT, zone_name, NULL); + knot_db_val_t val = { .len = nsec3salt->size, .data = nsec3salt->data }; + ret = db_api->insert(txn, &key, &val, 0); + free_key(&key); + txn_check(NULL); + key = make_key(KASPDBKEY_NSEC3TIME, zone_name, NULL); + uint64_t tmp = htobe64((uint64_t)salt_created); + val.len = sizeof(tmp); + val.data = &tmp; + ret = db_api->insert(txn, &key, &val, 0); + free_key(&key); + with_txn_end(NULL); + return ret; +} + +int kasp_db_load_nsec3salt(kasp_db_t *db, const knot_dname_t *zone_name, + dnssec_binary_t *nsec3salt, knot_time_t *salt_created) +{ + if (db == NULL || db->keys_db == NULL || + zone_name == NULL || nsec3salt == NULL || salt_created == NULL) { + return KNOT_EINVAL; + } + + with_txn(KEYS_RW, NULL); + knot_db_val_t key = make_key(KASPDBKEY_NSEC3TIME, zone_name, NULL), val = { 0 }; + ret = db_api->find(txn, &key, &val, 0); + free_key(&key); + if (ret == KNOT_EOK) { + if (val.len == sizeof(uint64_t)) { + *salt_created = (knot_time_t)be64toh(*(uint64_t *)val.data); + } + else { + ret = KNOT_EMALF; + } + } + txn_check(NULL); + key = make_key(KASPDBKEY_NSEC3SALT, zone_name, NULL); + ret = db_api->find(txn, &key, &val, 0); + free_key(&key); + if (ret == KNOT_EOK) { + nsec3salt->data = malloc(val.len); + if (nsec3salt->data == NULL) { + ret = KNOT_ENOMEM; + } else { + nsec3salt->size = val.len; + memcpy(nsec3salt->data, val.data, val.len); + } + } + with_txn_end(NULL); + return ret; +} + +int kasp_db_store_serial(kasp_db_t *db, const knot_dname_t *zone_name, + kaspdb_serial_t serial_type, uint32_t serial) +{ + if (db == NULL || db->keys_db == NULL || zone_name == NULL) { + return KNOT_EINVAL; + } + + uint32_t be = htobe32(serial); + with_txn(KEYS_RW, NULL); + knot_db_val_t key = make_key((keyclass_t)serial_type, zone_name, NULL); + knot_db_val_t val = { .len = sizeof(uint32_t), .data = &be }; + ret = db_api->insert(txn, &key, &val, 0); + free_key(&key); + with_txn_end(NULL); + return ret; +} + +int kasp_db_load_serial(kasp_db_t *db, const knot_dname_t *zone_name, + kaspdb_serial_t serial_type, uint32_t *serial) +{ + if (db == NULL || db->keys_db == NULL || zone_name == NULL || serial == NULL) { + return KNOT_EINVAL; + } + + with_txn(KEYS_RO, NULL); + knot_db_val_t key = make_key((keyclass_t)serial_type, zone_name, NULL), val = { 0 }; + ret = db_api->find(txn, &key, &val, 0); + free_key(&key); + if (ret == KNOT_EOK) { + if (val.len == sizeof(uint32_t)) { + *serial = be32toh(*(uint32_t *)val.data); + } else { + ret = KNOT_EMALF; + } + } + with_txn_end(NULL); + return ret; +} + +int kasp_db_get_policy_last(kasp_db_t *db, const char *policy_string, knot_dname_t **lp_zone, + char **lp_keyid) +{ + if (db == NULL || db->keys_db == NULL || policy_string == NULL || + lp_zone == NULL || lp_keyid == NULL) { + return KNOT_EINVAL; + } + + with_txn(KEYS_RO, NULL); + knot_db_val_t key = make_key(KASPDBKEY_POLICYLAST, NULL, policy_string), val = { 0 }; + ret = db_api->find(txn, &key, &val, 0); + free_key(&key); + if (ret == KNOT_EOK) { + if (*(uint8_t *)val.data != KASPDBKEY_PARAMS) { + ret = KNOT_EMALF; + } else { + *lp_zone = knot_dname_copy((knot_dname_t *)(val.data + 1), NULL); + *lp_keyid = keyid_fromkey(&val); + if (*lp_zone == NULL || *lp_keyid == NULL) { + free(*lp_zone); + free(*lp_keyid); + ret = KNOT_ENOMEM; + } else { + // check that the shared key ID really exists + key = make_key(KASPDBKEY_PARAMS, *lp_zone, *lp_keyid); + ret = db_api->find(txn, &key, &val, 0); + free_key(&key); + if (ret != KNOT_EOK) { + free(*lp_zone); + free(*lp_keyid); + } + } + } + } + with_txn_end(NULL); + return ret; +} + +int kasp_db_set_policy_last(kasp_db_t *db, const char *policy_string, const char *last_lp_keyid, + const knot_dname_t *new_lp_zone, const char *new_lp_keyid) +{ + if (db == NULL || db->keys_db == NULL || + new_lp_zone == NULL || new_lp_keyid == NULL) { + return KNOT_EINVAL; + } + with_txn(KEYS_RW, NULL); + knot_db_val_t key = make_key(KASPDBKEY_POLICYLAST, NULL, policy_string), val = { 0 }; + ret = db_api->find(txn, &key, &val, 0); + switch (ret) { + case KNOT_EOK: + if (*(uint8_t *)val.data != KASPDBKEY_PARAMS) { + ret = KNOT_EMALF; + } else { + char *real_last = keyid_fromkey(&val); + if (real_last == NULL) { + ret = KNOT_ENOMEM; + } else { + if (last_lp_keyid == NULL || strcmp(real_last, last_lp_keyid) != 0) { + ret = KNOT_ESEMCHECK; + } + free(real_last); + } + } + break; + case KNOT_ENOENT: + ret = KNOT_EOK; + break; + } + if (ret == KNOT_EOK) { + val = make_key(KASPDBKEY_PARAMS, new_lp_zone, new_lp_keyid); + ret = db_api->insert(txn, &key, &val, 0); + free(val.data); + } + free(key.data); + with_txn_end(NULL); + return ret; +} + +int kasp_db_list_zones(kasp_db_t *db, list_t *dst) +{ + if (db == NULL || db->keys_db == NULL || dst == NULL) { + return KNOT_EINVAL; + } + + with_txn(KEYS_RO, NULL); + knot_db_iter_t *iter = db_api->iter_begin(txn, KNOT_DB_FIRST); + while (iter != NULL) { + knot_db_val_t key; + if (db_api->iter_key(iter, &key) == KNOT_EOK && key.len > 1 && + *(uint8_t *)key.data != KASPDBKEY_POLICYLAST) { + // obtain a domain name of a record in KASP db + knot_dname_t *key_dn = (knot_dname_t *)(key.data + 1); + // check if not already in dst + ptrnode_t *n; + WALK_LIST(n, *dst) { + knot_dname_t *exist_dn = (knot_dname_t *)n->d; + if (knot_dname_is_equal(key_dn, exist_dn)) { + key_dn = NULL; + break; + } + } + // copy it from txn and add to dst + if (key_dn != NULL) { + knot_dname_t *add_dn = knot_dname_copy(key_dn, NULL); + if (add_dn == NULL) { + ret = KNOT_ENOMEM; + break; + } + ptrlist_add(dst, add_dn, NULL); + } + } + iter = db_api->iter_next(iter); + } + db_api->iter_finish(iter); + db_api->txn_abort(txn); + + if (ret != KNOT_EOK) { + ptrlist_deep_free(dst, NULL); + return ret; + } + return (EMPTY_LIST(*dst) ? KNOT_ENOENT : KNOT_EOK); +} diff --git a/src/knot/dnssec/kasp/kasp_db.h b/src/knot/dnssec/kasp/kasp_db.h new file mode 100644 index 0000000..a6f1b97 --- /dev/null +++ b/src/knot/dnssec/kasp/kasp_db.h @@ -0,0 +1,235 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include <time.h> + +#include "contrib/time.h" +#include "contrib/ucw/lists.h" +#include "libknot/db/db_lmdb.h" +#include "libknot/dname.h" +#include "knot/dnssec/kasp/policy.h" + +typedef struct kasp_db kasp_db_t; + +typedef enum { // the enum values MUST match those from keyclass_t !! + KASPDB_SERIAL_MASTER = 0x5, + KASPDB_SERIAL_LASTSIGNED = 0x6, +} kaspdb_serial_t; + +/*! + * \brief Returns kasp_db_t singleton, to be used for signing all zones. + * + * De/initialized with server_t, used in zone contents signing context. + */ +kasp_db_t **kaspdb(void); + +/*! + * \brief Initialize kasp_db_t, prepare to simple open on-demand. + * + * \param db structure to initialize + * \param path path to the LMDB directory (will be created) + * \param mapsize LMDB map size + * + * \return KNOT_E* + */ +int kasp_db_init(kasp_db_t **db, const char *path, size_t mapsize); + +/*! + * \brief Re-initialize kasp_db_t if not already open. + * + * \param db structure to initialize + * \param new_path new path to LMDB + * \param new_mapsize new LMDB map size + * + * \retval KNOT_EBUSY can't reconfigure DB path because already open + * \retval KNOT_EEXIST can't reconfigure mapsize because already open + * \retval KNOT_ENODIFF already open, but no change needed => OK + * \retval KNOT_EINVAL, KNOT_ENOMEM, etc. standard errors + * \return KNOT_EOK reconfigured successfully + */ +int kasp_db_reconfigure(kasp_db_t **db, const char *new_path, size_t new_mapsize); + +/*! + * \brief Determine if kasp_db possibly exists at all. + * + * This is useful to avoid creating kasp_db by opening it just to check if anything is there. + */ +bool kasp_db_exists(kasp_db_t *db); + +/*! + * \brief Perform real ctreate/open of KASP db. + */ +int kasp_db_open(kasp_db_t *db); + +/*! + * \brief Close KASP db if open and free the structure. + */ +void kasp_db_close(kasp_db_t **db); + +/*! + * \brief For given zone, list all keys (their IDs) belonging to it. + * + * \param db KASP db + * \param zone_name name of the zone in question + * \param dst output if KNOT_EOK: ptrlist of keys' params + * + * \return KNOT_E* (KNOT_ENOENT if no keys) + */ +int kasp_db_list_keys(kasp_db_t *db, const knot_dname_t *zone_name, list_t *dst); + +/*! + * \brief Remove a key from zone. Delete the key if no zone has it anymore. + * + * \param db KASP db + * \param zone_name zone to be removed from + * \param key_id ID of key to be removed + * \param still_used output if KNOT_EOK: is the key still in use by other zones? + * + * \return KNOT_E* + */ +int kasp_db_delete_key(kasp_db_t *db, const knot_dname_t *zone_name, const char *key_id, bool *still_used); + +/*! + * \brief Remove all zone's keys from DB, including nsec3param + * \param db KASP db + * \param zone_name zoen to be removed + * + * \return KNOT_E* + */ +int kasp_db_delete_all(kasp_db_t *db, const knot_dname_t *zone_name); + +/*! + * \brief Add a key to the DB (possibly overwrite) and link it to a zone. + * + * Stores new key with given params into KASP db. If a key with the same ID had been present + * in KASP db already, its params get silently overwritten by those new params. + * Moreover, the key ID is linked to the zone. + * + * \param db KASP db + * \param zone_name name of the zone the new key shall belong to + * \param params key params, incl. ID + * + * \return KNOT_E* + */ +int kasp_db_add_key(kasp_db_t *db, const knot_dname_t *zone_name, const key_params_t *params); + +/*! + * \brief Link a key from another zone. + * + * \param db KASP db + * \param zone_from name of the zone the key belongs to + * \param zone_to name of the zone the key shall belong to as well + * \param key_id ID of the key in question + * + * \return KNOT_E* + */ +int kasp_db_share_key(kasp_db_t *db, const knot_dname_t *zone_from, const knot_dname_t *zone_to, const char *key_id); + +/*! + * \brief Store NSEC3 salt for given zone (possibly overwrites old salt). + * + * \param db KASP db + * \param zone_name zone name + * \param nsec3salt new NSEC3 salt + * \param salt_created timestamp when the salt was created + * + * \return KNOT_E* + */ +int kasp_db_store_nsec3salt(kasp_db_t *db, const knot_dname_t *zone_name, + const dnssec_binary_t *nsec3salt, knot_time_t salt_created); + +/*! + * \brief Load NSEC3 salt for given zone. + * + * \param db KASP db + * \param zone_name zone name + * \param nsec3salt output if KNOT_EOK: the zone's NSEC3 salt + * \param salt_created output if KNOT_EOK: timestamp when the salt was created + * + * \return KNOT_E* (KNOT_ENOENT if not stored before) + */ +int kasp_db_load_nsec3salt(kasp_db_t *db, const knot_dname_t *zone_name, + dnssec_binary_t *nsec3salt, knot_time_t *salt_created); + +/*! + * \brief Store SOA serial number of master or last signed serial. + * + * \param db KASP db + * \param zone_name zone name + * \param serial_type kind of serial to be stored + * \param serial new serial to be stored + * + * \return KNOT_E* + */ +int kasp_db_store_serial(kasp_db_t *db, const knot_dname_t *zone_name, + kaspdb_serial_t serial_type, uint32_t serial); + +/*! + * \brief Load saved SOA serial number of master or last signed serial. + * + * \param db KASP db + * \param zone_name zone name + * \param serial_type kind of serial to be loaded + * \param serial output if KNOT_EOK: desired serial number + * + * \return KNOT_E* (KNOT_ENOENT if not stored before) + */ +int kasp_db_load_serial(kasp_db_t *db, const knot_dname_t *zone_name, + kaspdb_serial_t serial_type, uint32_t *serial); + +/*! + * \brief For given policy name, obtain last generated key. + * + * \param db KASP db + * \param policy_string a name identifying the signing policy with shared keys + * \param lp_zone out: the zone owning the last generated key + * \param lp_keyid out: the ID of the last generated key + * + * \return KNOT_E* + */ +int kasp_db_get_policy_last(kasp_db_t *db, const char *policy_string, knot_dname_t **lp_zone, + char **lp_keyid); + +/*! + * \brief For given policy name, try to reset last generated key. + * + * \param db KASP db + * \param policy_string a name identifying the signing policy with shared keys + * \param last_lp_keyid just for check: ID of the key the caller thinks is the policy-last + * \param new_lp_zone zone name of the new policy-last key + * \param new_lp_keyid ID of the new policy-last key + * + * \retval KNOT_ESEMCHECK lasp_lp_keyid does not correspond to real last key. Probably another zone + * changed policy-last key in the meantime. Re-run kasp_db_get_policy_last() + * \retval KNOT_EOK policy-last key set up successfully to given zone/ID + * \return KNOT_E* common error + */ +int kasp_db_set_policy_last(kasp_db_t *db, const char *policy_string, const char *last_lp_keyid, + const knot_dname_t *new_lp_zone, const char *new_lp_keyid); + +/*! + * \brief List all zones that have anything stored in KASP db. + * + * It's quite slow, but we expect KASP db not to be so large. + * + * \param db KASP db + * \param dst List of zone names + * + * \return KNOT_E* + */ +int kasp_db_list_zones(kasp_db_t *db, list_t *dst); diff --git a/src/knot/dnssec/kasp/kasp_zone.c b/src/knot/dnssec/kasp/kasp_zone.c new file mode 100644 index 0000000..57a435e --- /dev/null +++ b/src/knot/dnssec/kasp/kasp_zone.c @@ -0,0 +1,296 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "knot/dnssec/kasp/kasp_zone.h" +#include "knot/dnssec/zone-keys.h" +#include "libdnssec/binary.h" + +// FIXME DNSSEC errors versus knot errors + +/*! + * Check if key parameters allow to create a key. + */ +static int key_params_check(key_params_t *params) +{ + assert(params); + + if (params->algorithm == 0) { + return KNOT_INVALID_KEY_ALGORITHM; + } + + if (params->public_key.size == 0) { + return KNOT_NO_PUBLIC_KEY; + } + + return KNOT_EOK; +} + +/*! \brief Determine presence of SEP bit by trial-end-error using known keytag. */ +static int dnskey_guess_flags(dnssec_key_t *key, uint16_t keytag) +{ + dnssec_key_set_flags(key, DNSKEY_FLAGS_KSK); + if (dnssec_key_get_keytag(key) == keytag) { + return KNOT_EOK; + } + + dnssec_key_set_flags(key, DNSKEY_FLAGS_ZSK); + if (dnssec_key_get_keytag(key) == keytag) { + return KNOT_EOK; + } + + return KNOT_EMALF; +} + +static int params2dnskey(const knot_dname_t *dname, key_params_t *params, + dnssec_key_t **key_ptr) +{ + assert(dname); + assert(params); + assert(key_ptr); + + int ret = key_params_check(params); + if (ret != KNOT_EOK) { + return ret; + } + + dnssec_key_t *key = NULL; + ret = dnssec_key_new(&key); + if (ret != KNOT_EOK) { + return knot_error_from_libdnssec(ret); + } + + ret = dnssec_key_set_dname(key, dname); + if (ret != KNOT_EOK) { + dnssec_key_free(key); + return knot_error_from_libdnssec(ret); + } + + dnssec_key_set_algorithm(key, params->algorithm); + + ret = dnssec_key_set_pubkey(key, ¶ms->public_key); + if (ret != KNOT_EOK) { + dnssec_key_free(key); + return knot_error_from_libdnssec(ret); + } + + ret = dnskey_guess_flags(key, params->keytag); + if (ret != KNOT_EOK) { + dnssec_key_free(key); + return ret; + } + + *key_ptr = key; + + return KNOT_EOK; +} + +static int params2kaspkey(const knot_dname_t *dname, key_params_t *params, + knot_kasp_key_t *key) +{ + assert(dname != NULL); + assert(params != NULL); + assert(key != NULL); + + int ret = params2dnskey(dname, params, &key->key); + if (ret != KNOT_EOK) { + return ret; + } + + key->id = strdup(params->id); + if (key->id == NULL) { + dnssec_key_free(key->key); + return KNOT_ENOMEM; + } + + key->timing = params->timing; + key->is_pub_only = params->is_pub_only; + assert(params->is_ksk || !params->is_csk); + key->is_ksk = params->is_ksk; + key->is_zsk = (params->is_csk || !params->is_ksk); + return KNOT_EOK; +} + +static void kaspkey2params(knot_kasp_key_t *key, key_params_t *params) +{ + assert(key); + assert(params); + + params->id = key->id; + params->keytag = dnssec_key_get_keytag(key->key); + dnssec_key_get_pubkey(key->key, ¶ms->public_key); + params->algorithm = dnssec_key_get_algorithm(key->key); + params->is_ksk = key->is_ksk; + params->is_csk = (key->is_ksk && key->is_zsk); + params->timing = key->timing; + params->is_pub_only = key->is_pub_only; +} + +int kasp_zone_load(knot_kasp_zone_t *zone, + const knot_dname_t *zone_name, + kasp_db_t *kdb) +{ + if (zone == NULL || zone_name == NULL || kdb == NULL) { + return KNOT_EINVAL; + } + + knot_kasp_key_t *dkeys = NULL; + size_t num_dkeys = 0; + dnssec_binary_t salt = { 0 }; + knot_time_t sc = 0; + + list_t key_params; + init_list(&key_params); + int ret = kasp_db_list_keys(kdb, zone_name, &key_params); + if (ret == KNOT_ENOENT) { + zone->keys = NULL; + zone->num_keys = 0; + ret = KNOT_EOK; + goto kzl_salt; + } else if (ret != KNOT_EOK) { + goto kzl_end; + } + + num_dkeys = list_size(&key_params); + dkeys = calloc(num_dkeys, sizeof(*dkeys)); + if (dkeys == NULL) { + goto kzl_end; + } + + ptrnode_t *n; + int i = 0; + WALK_LIST(n, key_params) { + key_params_t *parm = n->d; + ret = params2kaspkey(zone_name, parm, &dkeys[i++]); + free_key_params(parm); + if (ret != KNOT_EOK) { + goto kzl_end; + } + } + +kzl_salt: + (void)kasp_db_load_nsec3salt(kdb, zone_name, &salt, &sc); + // if error, salt was probably not present, no problem to have zero ? + + zone->dname = knot_dname_copy(zone_name, NULL); + if (zone->dname == NULL) { + ret = KNOT_ENOMEM; + goto kzl_end; + } + zone->keys = dkeys; + zone->num_keys = num_dkeys; + zone->nsec3_salt = salt; + zone->nsec3_salt_created = sc; + +kzl_end: + ptrlist_deep_free(&key_params, NULL); + if (ret != KNOT_EOK) { + free(dkeys); + } + return ret; +} + +int kasp_zone_append(knot_kasp_zone_t *zone, const knot_kasp_key_t *appkey) +{ + if (zone == NULL || appkey == NULL || (zone->keys == NULL && zone->num_keys > 0)) { + return KNOT_EINVAL; + } + + size_t new_num_keys = zone->num_keys + 1; + knot_kasp_key_t *new_keys = calloc(new_num_keys, sizeof(*new_keys)); + if (!new_keys) { + return KNOT_ENOMEM; + } + if (zone->num_keys > 0) { + memcpy(new_keys, zone->keys, zone->num_keys * sizeof(*new_keys)); + } + memcpy(&new_keys[new_num_keys - 1], appkey, sizeof(*appkey)); + free(zone->keys); + zone->keys = new_keys; + zone->num_keys = new_num_keys; + return KNOT_EOK; +} + +int kasp_zone_save(const knot_kasp_zone_t *zone, + const knot_dname_t *zone_name, + kasp_db_t *kdb) +{ + if (zone == NULL || zone_name == NULL || kdb == NULL) { + return KNOT_EINVAL; + } + + key_params_t parm; + for (size_t i = 0; i < zone->num_keys; i++) { + kaspkey2params(&zone->keys[i], &parm); + + // Force overwrite already existing key-val pairs. + int ret = kasp_db_add_key(kdb, zone_name, &parm); + if (ret != KNOT_EOK) { + return ret; + } + } + + if (zone->nsec3_salt.size > 0) { + int ret = kasp_db_store_nsec3salt(kdb, zone_name, &zone->nsec3_salt, + zone->nsec3_salt_created); + if (ret != KNOT_EOK) { + return ret; + } + } + + return KNOT_EOK; +} + +int kasp_zone_init(knot_kasp_zone_t **zone) +{ + if (zone == NULL) { + return KNOT_EINVAL; + } + *zone = calloc(1, sizeof(**zone)); + return (*zone ? KNOT_EOK : KNOT_ENOMEM); +} + +void kasp_zone_clear(knot_kasp_zone_t *zone) +{ + if (zone == NULL) { + return; + } + knot_dname_free(zone->dname, NULL); + for (size_t i = 0; i < zone->num_keys; i++) { + dnssec_key_free(zone->keys[i].key); + free(zone->keys[i].id); + } + free(zone->keys); + free(zone->nsec3_salt.data); + memset(zone, 0, sizeof(*zone)); +} + +void kasp_zone_free(knot_kasp_zone_t **zone) +{ + if (zone != NULL) { + kasp_zone_clear(*zone); + free(*zone); + *zone = NULL; + } +} + +void free_key_params(key_params_t *parm) +{ + if (parm != NULL) { + free(parm->id); + dnssec_binary_free(&parm->public_key); + memset(parm, 0 , sizeof(*parm)); + } +} diff --git a/src/knot/dnssec/kasp/kasp_zone.h b/src/knot/dnssec/kasp/kasp_zone.h new file mode 100644 index 0000000..83eaea2 --- /dev/null +++ b/src/knot/dnssec/kasp/kasp_zone.h @@ -0,0 +1,45 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include "knot/dnssec/kasp/kasp_db.h" + +typedef struct { + knot_dname_t *dname; + + knot_kasp_key_t *keys; + size_t num_keys; + + dnssec_binary_t nsec3_salt; + knot_time_t nsec3_salt_created; +} knot_kasp_zone_t; + +int kasp_zone_load(knot_kasp_zone_t *zone, + const knot_dname_t *zone_name, + kasp_db_t *kdb); + +int kasp_zone_save(const knot_kasp_zone_t *zone, + const knot_dname_t *zone_name, + kasp_db_t *kdb); + +int kasp_zone_append(knot_kasp_zone_t *zone, + const knot_kasp_key_t *appkey); + +void kasp_zone_clear(knot_kasp_zone_t *zone); +void kasp_zone_free(knot_kasp_zone_t **zone); + +void free_key_params(key_params_t *parm); diff --git a/src/knot/dnssec/kasp/keystate.c b/src/knot/dnssec/kasp/keystate.c new file mode 100644 index 0000000..acf7cee --- /dev/null +++ b/src/knot/dnssec/kasp/keystate.c @@ -0,0 +1,70 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "knot/dnssec/kasp/keystate.h" + +key_state_t get_key_state(const knot_kasp_key_t *key, knot_time_t moment) +{ + if (!key || moment <= 0) { + return DNSSEC_KEY_STATE_INVALID; + } + + const knot_kasp_key_timing_t *t = &key->timing; + + bool removed = (knot_time_cmp(t->remove, moment) <= 0); + bool post_active = (knot_time_cmp(t->post_active, moment) <= 0); + bool retired = (knot_time_cmp(t->retire, moment) <= 0); + bool retire_active = (knot_time_cmp(t->retire_active, moment) <= 0); + bool active = (knot_time_cmp(t->active, moment) <= 0); + bool ready = (knot_time_cmp(t->ready, moment) <= 0); + bool published = (knot_time_cmp(t->publish, moment) <= 0); + bool pre_active = (knot_time_cmp(t->pre_active, moment) <= 0); + bool created = (knot_time_cmp(t->created, moment) <= 0); + + if (removed) { + return DNSSEC_KEY_STATE_REMOVED; + } + if (post_active) { + if (retired) { + return DNSSEC_KEY_STATE_INVALID; + } else { + return DNSSEC_KEY_STATE_POST_ACTIVE; + } + } + if (retired) { + return DNSSEC_KEY_STATE_RETIRED; + } + if (retire_active) { + return DNSSEC_KEY_STATE_RETIRE_ACTIVE; + } + if (active) { + return DNSSEC_KEY_STATE_ACTIVE; + } + if (ready) { + return DNSSEC_KEY_STATE_READY; + } + if (published) { + return DNSSEC_KEY_STATE_PUBLISHED; + } + if (pre_active) { + return DNSSEC_KEY_STATE_PRE_ACTIVE; + } + if (created) { + // don't care + } + + return DNSSEC_KEY_STATE_INVALID; +} diff --git a/src/knot/dnssec/kasp/keystate.h b/src/knot/dnssec/kasp/keystate.h new file mode 100644 index 0000000..9da9f14 --- /dev/null +++ b/src/knot/dnssec/kasp/keystate.h @@ -0,0 +1,34 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include "contrib/time.h" +#include "knot/dnssec/kasp/policy.h" + +typedef enum { + DNSSEC_KEY_STATE_INVALID = 0, + DNSSEC_KEY_STATE_PRE_ACTIVE, + DNSSEC_KEY_STATE_PUBLISHED, + DNSSEC_KEY_STATE_READY, + DNSSEC_KEY_STATE_ACTIVE, + DNSSEC_KEY_STATE_RETIRE_ACTIVE, + DNSSEC_KEY_STATE_RETIRED, + DNSSEC_KEY_STATE_POST_ACTIVE, + DNSSEC_KEY_STATE_REMOVED, +} key_state_t; + +key_state_t get_key_state(const knot_kasp_key_t *key, knot_time_t moment); diff --git a/src/knot/dnssec/kasp/keystore.c b/src/knot/dnssec/kasp/keystore.c new file mode 100644 index 0000000..96dee8f --- /dev/null +++ b/src/knot/dnssec/kasp/keystore.c @@ -0,0 +1,89 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <assert.h> +#include <stdio.h> +#include <string.h> + +#include "libdnssec/error.h" +#include "knot/dnssec/kasp/keystore.h" +#include "knot/conf/schema.h" +#include "libknot/error.h" + +static char *fix_path(const char *config, const char *base_path) +{ + assert(config); + assert(base_path); + + char *path = NULL; + + if (config[0] == '/') { + path = strdup(config); + } else { + if (asprintf(&path, "%s/%s", base_path, config) == -1) { + path = NULL; + } + } + + return path; +} + +int keystore_load(const char *config, unsigned backend, + const char *kasp_base_path, dnssec_keystore_t **keystore) +{ + int ret = DNSSEC_EINVAL; + char *fixed_config = NULL; + + switch (backend) { + case KEYSTORE_BACKEND_PEM: + ret = dnssec_keystore_init_pkcs8_dir(keystore); + fixed_config = fix_path(config, kasp_base_path); + break; + case KEYSTORE_BACKEND_PKCS11: + ret = dnssec_keystore_init_pkcs11(keystore); + fixed_config = strdup(config); + break; + default: + assert(0); + } + if (ret != DNSSEC_EOK) { + free(fixed_config); + return knot_error_from_libdnssec(ret); + } + if (fixed_config == NULL) { + dnssec_keystore_deinit(*keystore); + *keystore = NULL; + return KNOT_ENOMEM; + } + + ret = dnssec_keystore_init(*keystore, fixed_config); + if (ret != DNSSEC_EOK) { + free(fixed_config); + dnssec_keystore_deinit(*keystore); + *keystore = NULL; + return knot_error_from_libdnssec(ret); + } + + ret = dnssec_keystore_open(*keystore, fixed_config); + free(fixed_config); + if (ret != DNSSEC_EOK) { + dnssec_keystore_deinit(*keystore); + *keystore = NULL; + return knot_error_from_libdnssec(ret); + } + + return KNOT_EOK; +} diff --git a/src/knot/dnssec/kasp/keystore.h b/src/knot/dnssec/kasp/keystore.h new file mode 100644 index 0000000..028d6fc --- /dev/null +++ b/src/knot/dnssec/kasp/keystore.h @@ -0,0 +1,22 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include "libdnssec/keystore.h" + +int keystore_load(const char *config, unsigned backend, + const char *kasp_base_path, dnssec_keystore_t **keystore); diff --git a/src/knot/dnssec/kasp/policy.h b/src/knot/dnssec/kasp/policy.h new file mode 100644 index 0000000..2ce236e --- /dev/null +++ b/src/knot/dnssec/kasp/policy.h @@ -0,0 +1,112 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include <stdbool.h> + +#include "contrib/time.h" +#include "libdnssec/key.h" +#include "knot/conf/conf.h" + +/*! + * KASP key timing information. + */ +typedef struct { + knot_time_t created; /*!< Time the key was generated/imported. */ + knot_time_t pre_active; /*!< Signing start with new algorithm. */ + knot_time_t publish; /*!< Time of DNSKEY record publication. */ + knot_time_t ready; /*!< Start of RRSIG generation, waiting for parent zone. */ + knot_time_t active; /*!< RRSIG records generating, other keys can be retired */ + knot_time_t retire_active; /*!< Still active, but obsoleted. */ + knot_time_t retire; /*!< End of RRSIG records generating. */ + knot_time_t post_active; /*!< Still signing with old algorithm, not published. */ + knot_time_t remove; /*!< Time of DNSKEY record removal. */ +} knot_kasp_key_timing_t; + +/*! + * Key parameters as writing in zone config file. + */ +typedef struct { + char *id; + bool is_ksk; + bool is_csk; + bool is_pub_only; + uint16_t keytag; + uint8_t algorithm; + dnssec_binary_t public_key; + knot_kasp_key_timing_t timing; +} key_params_t; + +/*! + * Zone key. + */ +typedef struct { + char *id; /*!< Keystore unique key ID. */ + dnssec_key_t *key; /*!< Instance of the key. */ + knot_kasp_key_timing_t timing; /*!< Key timing information. */ + bool is_pub_only; + bool is_ksk; + bool is_zsk; +} knot_kasp_key_t; + +/*! + * Parent for DS checks. + */ +typedef struct { + conf_remote_t *addr; + size_t addrs; +} knot_kasp_parent_t; + +dynarray_declare(parent, knot_kasp_parent_t, DYNARRAY_VISIBILITY_PUBLIC, 3) + +/*! + * Key and signature policy. + */ +typedef struct { + bool manual; + char *string; + // DNSKEY + dnssec_key_algorithm_t algorithm; + uint16_t ksk_size; + uint16_t zsk_size; + uint32_t dnskey_ttl; + uint32_t zsk_lifetime; + uint32_t ksk_lifetime; + bool ksk_shared; + bool singe_type_signing; + // RRSIG + uint32_t rrsig_lifetime; + uint32_t rrsig_refresh_before; + // NSEC3 + bool nsec3_enabled; + bool nsec3_opt_out; + uint32_t nsec3_salt_lifetime; + uint16_t nsec3_iterations; + uint8_t nsec3_salt_length; + // SOA + uint32_t soa_minimal_ttl; + // zone + uint32_t zone_maximal_ttl; + // data propagation delay + uint32_t propagation_delay; + // various + uint32_t ksk_sbm_timeout; + uint32_t ksk_sbm_check_interval; + unsigned child_records_publish; + parent_dynarray_t parents; +} knot_kasp_policy_t; +// TODO make the time parameters knot_timediff_t ?? diff --git a/src/knot/dnssec/key-events.c b/src/knot/dnssec/key-events.c new file mode 100644 index 0000000..6551db5 --- /dev/null +++ b/src/knot/dnssec/key-events.c @@ -0,0 +1,713 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <assert.h> + +#include "contrib/macros.h" +#include "knot/common/log.h" +#include "knot/dnssec/kasp/keystate.h" +#include "knot/dnssec/key-events.h" +#include "knot/dnssec/policy.h" +#include "knot/dnssec/zone-keys.h" + +static bool key_present(const kdnssec_ctx_t *ctx, bool ksk, bool zsk) +{ + assert(ctx); + assert(ctx->zone); + for (size_t i = 0; i < ctx->zone->num_keys; i++) { + const knot_kasp_key_t *key = &ctx->zone->keys[i]; + if (key->is_ksk == ksk && key->is_zsk == zsk) { + return true; + } + } + return false; +} + +static bool key_id_present(const kdnssec_ctx_t *ctx, const char *keyid, bool want_ksk) +{ + assert(ctx); + assert(ctx->zone); + for (size_t i = 0; i < ctx->zone->num_keys; i++) { + const knot_kasp_key_t *key = &ctx->zone->keys[i]; + if (strcmp(keyid, key->id) == 0 && + key->is_ksk == want_ksk) { + return true; + } + } + return false; +} + +static unsigned algorithm_present(const kdnssec_ctx_t *ctx, uint8_t alg) +{ + assert(ctx); + assert(ctx->zone); + unsigned ret = 0; + for (size_t i = 0; i < ctx->zone->num_keys; i++) { + const knot_kasp_key_t *key = &ctx->zone->keys[i]; + knot_time_t activated = knot_time_min(key->timing.pre_active, key->timing.ready); + if (knot_time_cmp(knot_time_min(activated, key->timing.active), ctx->now) <= 0 && + dnssec_key_get_algorithm(key->key) == alg) { + ret++; + } + } + return ret; +} + +static bool signing_scheme_present(const kdnssec_ctx_t *ctx) +{ + if (ctx->policy->singe_type_signing) { + return (!key_present(ctx, true, false) || !key_present(ctx, false, true) || key_present(ctx, true, true)); + } else { + return (key_present(ctx, true, false) && key_present(ctx, false, true)); + } +} + +static knot_kasp_key_t *key_get_by_id(kdnssec_ctx_t *ctx, const char *keyid) +{ + assert(ctx); + assert(ctx->zone); + for (size_t i = 0; i < ctx->zone->num_keys; i++) { + knot_kasp_key_t *key = &ctx->zone->keys[i]; + if (strcmp(keyid, key->id) == 0) { + return key; + } + } + return NULL; +} + +static int generate_key(kdnssec_ctx_t *ctx, kdnssec_generate_flags_t flags, + knot_time_t when_active, bool pre_active) +{ + assert(!pre_active || when_active == 0); + + knot_kasp_key_t *key = NULL; + int ret = kdnssec_generate_key(ctx, flags, &key); + if (ret != KNOT_EOK) { + return ret; + } + + key->timing.remove = 0; + key->timing.retire = 0; + key->timing.active = ((flags & DNSKEY_GENERATE_KSK) ? 0 : when_active); + key->timing.ready = ((flags & DNSKEY_GENERATE_KSK) ? when_active : 0); + key->timing.publish = (pre_active ? 0 : ctx->now); + key->timing.pre_active = (pre_active ? ctx->now : 0); + + return KNOT_EOK; +} + +static int share_or_generate_key(kdnssec_ctx_t *ctx, kdnssec_generate_flags_t flags, + knot_time_t when_active, bool pre_active) +{ + assert(!pre_active || when_active == 0); + + knot_dname_t *borrow_zone = NULL; + char *borrow_key = NULL; + + if (!(flags & DNSKEY_GENERATE_KSK)) { + return KNOT_EINVAL; + } // for now not designed for rotating shared ZSK + + int ret = kasp_db_get_policy_last(*ctx->kasp_db, ctx->policy->string, + &borrow_zone, &borrow_key); + if (ret != KNOT_EOK && ret != KNOT_ENOENT) { + return ret; + } + + // if we already have the policy-last key, we have to generate new one + if (ret == KNOT_ENOENT || key_id_present(ctx, borrow_key, true)) { + knot_kasp_key_t *key = NULL; + ret = kdnssec_generate_key(ctx, flags, &key); + if (ret != KNOT_EOK) { + return ret; + } + key->timing.remove = 0; + key->timing.retire = 0; + key->timing.active = ((flags & DNSKEY_GENERATE_KSK) ? 0 : when_active); + key->timing.ready = ((flags & DNSKEY_GENERATE_KSK) ? when_active : 0); + key->timing.publish = (pre_active ? 0 : ctx->now); + key->timing.pre_active = (pre_active ? ctx->now : 0); + + ret = kdnssec_ctx_commit(ctx); + if (ret != KNOT_EOK) { + return ret; + } + + ret = kasp_db_set_policy_last(*ctx->kasp_db, ctx->policy->string, + borrow_key, ctx->zone->dname, key->id); + free(borrow_zone); + free(borrow_key); + borrow_zone = NULL; + borrow_key = NULL; + if (ret != KNOT_ESEMCHECK) { + // all ok, we generated new kay and updated policy-last + return ret; + } else { + // another zone updated policy-last key in the meantime + ret = kdnssec_delete_key(ctx, key); + if (ret == KNOT_EOK) { + ret = kdnssec_ctx_commit(ctx); + } + if (ret != KNOT_EOK) { + return ret; + } + + ret = kasp_db_get_policy_last(*ctx->kasp_db, ctx->policy->string, + &borrow_zone, &borrow_key); + } + } + + if (ret == KNOT_EOK) { + ret = kdnssec_share_key(ctx, borrow_zone, borrow_key); + if (ret == KNOT_EOK) { + knot_kasp_key_t *newkey = key_get_by_id(ctx, borrow_key); + assert(newkey != NULL); + newkey->timing.remove = 0; + newkey->timing.retire = 0; + newkey->timing.active = ((flags & DNSKEY_GENERATE_KSK) ? 0 : when_active); + newkey->timing.ready = ((flags & DNSKEY_GENERATE_KSK) ? when_active : 0); + newkey->timing.publish = (pre_active ? 0 : ctx->now); + newkey->timing.pre_active = (pre_active ? ctx->now : 0); + newkey->is_ksk = (flags & DNSKEY_GENERATE_KSK); + newkey->is_zsk = (flags & DNSKEY_GENERATE_ZSK); + } + } + free(borrow_zone); + free(borrow_key); + return ret; +} + +#define GEN_KSK_FLAGS (DNSKEY_GENERATE_KSK | (ctx->policy->singe_type_signing ? DNSKEY_GENERATE_ZSK : 0)) + +static bool running_rollover(const kdnssec_ctx_t *ctx) +{ + bool res = false; + bool ready_ksk = false, active_ksk = false; + + for (size_t i = 0; i < ctx->zone->num_keys; i++) { + knot_kasp_key_t *key = &ctx->zone->keys[i]; + switch (get_key_state(key, ctx->now)) { + case DNSSEC_KEY_STATE_PRE_ACTIVE: + res = true; + break; + case DNSSEC_KEY_STATE_PUBLISHED: + res = (res || !key->is_pub_only); + break; + case DNSSEC_KEY_STATE_READY: + ready_ksk = (ready_ksk || key->is_ksk); + break; + case DNSSEC_KEY_STATE_ACTIVE: + active_ksk = (active_ksk || key->is_ksk); + break; + case DNSSEC_KEY_STATE_RETIRE_ACTIVE: + case DNSSEC_KEY_STATE_POST_ACTIVE: + res = true; + break; + case DNSSEC_KEY_STATE_RETIRED: + case DNSSEC_KEY_STATE_REMOVED: + default: + break; + } + } + if (ready_ksk && active_ksk) { + res = true; + } + return res; +} + +typedef enum { + INVALID = 0, + GENERATE = 1, + PUBLISH, + SUBMIT, + REPLACE, + RETIRE, + REMOVE, +} roll_action_type_t; + +typedef struct { + roll_action_type_t type; + bool ksk; + knot_time_t time; + knot_kasp_key_t *key; +} roll_action_t; + +static const char *roll_action_name(roll_action_type_t type) +{ + switch (type) { + case GENERATE: return "generate"; + case PUBLISH: return "publish"; + case SUBMIT: return "submit"; + case REPLACE: return "replace"; + case RETIRE: return "retire"; + case REMOVE: return "remove"; + case INVALID: + // FALLTHROUGH + default: return "invalid"; + } +} + +static knot_time_t zsk_rollover_time(knot_time_t active_time, const kdnssec_ctx_t *ctx) +{ + if (active_time <= 0 || ctx->policy->zsk_lifetime == 0) { + return 0; + } + return knot_time_add(active_time, ctx->policy->zsk_lifetime); +} + +static knot_time_t zsk_active_time(knot_time_t publish_time, const kdnssec_ctx_t *ctx) +{ + if (publish_time <= 0) { + return 0; + } + return knot_time_add(publish_time, ctx->policy->propagation_delay + ctx->policy->dnskey_ttl); +} + +static knot_time_t zsk_remove_time(knot_time_t retire_time, const kdnssec_ctx_t *ctx) +{ + if (retire_time <= 0) { + return 0; + } + return knot_time_add(retire_time, ctx->policy->propagation_delay + ctx->policy->zone_maximal_ttl); +} + +static knot_time_t ksk_rollover_time(knot_time_t created_time, const kdnssec_ctx_t *ctx) +{ + if (created_time <= 0 || ctx->policy->ksk_lifetime == 0) { + return 0; + } + return knot_time_add(created_time, ctx->policy->ksk_lifetime); +} + +static knot_time_t ksk_ready_time(knot_time_t publish_time, const kdnssec_ctx_t *ctx) +{ + if (publish_time <= 0) { + return 0; + } + return knot_time_add(publish_time, ctx->policy->propagation_delay + ctx->policy->dnskey_ttl); +} + +static knot_time_t ksk_sbm_max_time(knot_time_t ready_time, const kdnssec_ctx_t *ctx) +{ + if (ready_time <= 0 || ctx->policy->ksk_sbm_timeout == 0) { + return 0; + } + return knot_time_add(ready_time, ctx->policy->ksk_sbm_timeout); +} + +static knot_time_t ksk_retire_time(knot_time_t retire_active_time, const kdnssec_ctx_t *ctx) +{ + if (retire_active_time <= 0) { + return 0; + } + // this is not correct! It should be parent DS TTL. + return knot_time_add(retire_active_time, ctx->policy->propagation_delay + ctx->policy->dnskey_ttl); +} + +static knot_time_t ksk_remove_time(knot_time_t retire_time, const kdnssec_ctx_t *ctx) +{ + if (retire_time <= 0) { + return 0; + } + knot_timediff_t use_ttl = ctx->policy->dnskey_ttl; + if (ctx->policy->singe_type_signing && ctx->policy->zone_maximal_ttl > use_ttl) { + use_ttl = ctx->policy->zone_maximal_ttl; + } + return knot_time_add(retire_time, ctx->policy->propagation_delay + use_ttl); +} + +// algorithm rollover related timers must be the same for KSK and ZSK + +static knot_time_t alg_publish_time(knot_time_t pre_active_time, const kdnssec_ctx_t *ctx) +{ + if (pre_active_time <= 0) { + return 0; + } + return knot_time_add(pre_active_time, ctx->policy->propagation_delay + ctx->policy->zone_maximal_ttl); +} + +static knot_time_t alg_remove_time(knot_time_t post_active_time, const kdnssec_ctx_t *ctx) +{ + return MAX(ksk_remove_time(post_active_time, ctx), zsk_remove_time(post_active_time, ctx)); +} + +static roll_action_t next_action(kdnssec_ctx_t *ctx) +{ + roll_action_t res = { 0 }; + res.time = 0; + + for (size_t i = 0; i < ctx->zone->num_keys; i++) { + knot_kasp_key_t *key = &ctx->zone->keys[i]; + knot_time_t keytime = 0; + roll_action_type_t restype = INVALID; + if (key->is_pub_only) { + continue; + } + if (key->is_ksk) { + switch (get_key_state(key, ctx->now)) { + case DNSSEC_KEY_STATE_PRE_ACTIVE: + keytime = alg_publish_time(key->timing.pre_active, ctx); + restype = PUBLISH; + break; + case DNSSEC_KEY_STATE_PUBLISHED: + keytime = ksk_ready_time(key->timing.publish, ctx); + restype = SUBMIT; + break; + case DNSSEC_KEY_STATE_READY: + keytime = ksk_sbm_max_time(key->timing.ready, ctx); + restype = REPLACE; + break; + case DNSSEC_KEY_STATE_ACTIVE: + if (!running_rollover(ctx) && + dnssec_key_get_algorithm(key->key) == ctx->policy->algorithm) { + keytime = ksk_rollover_time(key->timing.created, ctx); + restype = GENERATE; + } + break; + case DNSSEC_KEY_STATE_RETIRE_ACTIVE: + if (key->timing.retire == 0 && key->timing.post_active == 0) { // this shouldn't normally happen + // when a KSK is retire_active, it has already retire or post_active timer set + keytime = ksk_retire_time(key->timing.retire_active, ctx); + restype = RETIRE; + } + break; + case DNSSEC_KEY_STATE_POST_ACTIVE: + keytime = alg_remove_time(key->timing.post_active, ctx); + restype = REMOVE; + break; + case DNSSEC_KEY_STATE_RETIRED: + case DNSSEC_KEY_STATE_REMOVED: + // ad REMOVED state: normally this wouldn't happen + // (key in removed state is instantly deleted) + // but if imported keys, they can be in this state + keytime = knot_time_min(key->timing.retire, key->timing.remove); + keytime = ksk_remove_time(keytime, ctx); + restype = REMOVE; + break; + default: + continue; + } + } else { + switch (get_key_state(key, ctx->now)) { + case DNSSEC_KEY_STATE_PRE_ACTIVE: + keytime = alg_publish_time(key->timing.pre_active, ctx); + restype = PUBLISH; + break; + case DNSSEC_KEY_STATE_PUBLISHED: + keytime = zsk_active_time(key->timing.publish, ctx); + restype = REPLACE; + break; + case DNSSEC_KEY_STATE_ACTIVE: + if (!running_rollover(ctx) && + dnssec_key_get_algorithm(key->key) == ctx->policy->algorithm) { + keytime = zsk_rollover_time(key->timing.active, ctx); + restype = GENERATE; + } + break; + case DNSSEC_KEY_STATE_RETIRE_ACTIVE: + // simply waiting for submitted KSK to retire me. + break; + case DNSSEC_KEY_STATE_POST_ACTIVE: + keytime = alg_remove_time(key->timing.post_active, ctx); + restype = REMOVE; + break; + case DNSSEC_KEY_STATE_RETIRED: + case DNSSEC_KEY_STATE_REMOVED: + // ad REMOVED state: normally this wouldn't happen + // (key in removed state is instantly deleted) + // but if imported keys, they can be in this state + keytime = knot_time_min(key->timing.retire, key->timing.remove); + keytime = zsk_remove_time(keytime, ctx); + restype = REMOVE; + break; + case DNSSEC_KEY_STATE_READY: + default: + continue; + } + } + if (knot_time_cmp(keytime, res.time) < 0) { + res.key = key; + res.ksk = key->is_ksk; + res.time = keytime; + res.type = restype; + } + } + + return res; +} + +static int submit_key(kdnssec_ctx_t *ctx, knot_kasp_key_t *newkey) +{ + assert(get_key_state(newkey, ctx->now) == DNSSEC_KEY_STATE_PUBLISHED); + assert(newkey->is_ksk); + + // pushing from READY into ACTIVE decreases the other key's cds_priority + for (size_t i = 0; i < ctx->zone->num_keys; i++) { + knot_kasp_key_t *key = &ctx->zone->keys[i]; + if (key->is_ksk && get_key_state(key, ctx->now) == DNSSEC_KEY_STATE_READY) { + key->timing.active = ctx->now; + } + } + + newkey->timing.ready = ctx->now; + return KNOT_EOK; +} + +static int exec_new_signatures(kdnssec_ctx_t *ctx, knot_kasp_key_t *newkey, uint32_t active_retire_delay) +{ + if (newkey->is_ksk) { + log_zone_notice(ctx->zone->dname, "DNSSEC, KSK submission, confirmed"); + } + + for (size_t i = 0; i < ctx->zone->num_keys; i++) { + knot_kasp_key_t *key = &ctx->zone->keys[i]; + key_state_t keystate = get_key_state(key, ctx->now); + uint8_t keyalg = dnssec_key_get_algorithm(key->key); + if (((newkey->is_ksk && key->is_ksk) || (newkey->is_zsk && key->is_zsk && !key->is_ksk)) + && keystate == DNSSEC_KEY_STATE_ACTIVE) { + if (key->is_ksk || keyalg != dnssec_key_get_algorithm(newkey->key)) { + key->timing.retire_active = ctx->now; + } else { + key->timing.retire = ctx->now; + } + } + if (newkey->is_ksk && (keystate == DNSSEC_KEY_STATE_ACTIVE || + keystate == DNSSEC_KEY_STATE_RETIRE_ACTIVE)) { + if (keyalg != dnssec_key_get_algorithm(newkey->key)) { + key->timing.post_active = ctx->now + active_retire_delay; + } else if (key->is_ksk) { + key->timing.retire = ctx->now + active_retire_delay; + } + } + } + + if (newkey->is_ksk) { + assert(get_key_state(newkey, ctx->now) == DNSSEC_KEY_STATE_READY); + } else { + assert(get_key_state(newkey, ctx->now) == DNSSEC_KEY_STATE_PUBLISHED); + } + newkey->timing.active = knot_time_min(ctx->now, newkey->timing.active); + + return KNOT_EOK; +} + +static int exec_publish(kdnssec_ctx_t *ctx, knot_kasp_key_t *key) +{ + assert(get_key_state(key, ctx->now) == DNSSEC_KEY_STATE_PRE_ACTIVE); + key->timing.publish = ctx->now; + + return KNOT_EOK; +} + +static int exec_ksk_retire(kdnssec_ctx_t *ctx, knot_kasp_key_t *key) +{ + bool alg_rollover = false; + knot_kasp_key_t *alg_rollover_friend = NULL; + + for (size_t i = 0; i < ctx->zone->num_keys; i++) { + knot_kasp_key_t *k = &ctx->zone->keys[i]; + int magic = (k->is_ksk && k->is_zsk ? 2 : 3); // :( + if (k->is_zsk && get_key_state(k, ctx->now) == DNSSEC_KEY_STATE_RETIRE_ACTIVE && + algorithm_present(ctx, dnssec_key_get_algorithm(k->key)) < magic) { + alg_rollover = true; + alg_rollover_friend = k; + } + } + + assert(get_key_state(key, ctx->now) == DNSSEC_KEY_STATE_RETIRE_ACTIVE); + + if (alg_rollover) { + key->timing.post_active = ctx->now; + alg_rollover_friend->timing.post_active = ctx->now; + } else { + key->timing.retire = ctx->now; + } + + return KNOT_EOK; +} + +static int exec_remove_old_key(kdnssec_ctx_t *ctx, knot_kasp_key_t *key) +{ + assert(get_key_state(key, ctx->now) == DNSSEC_KEY_STATE_RETIRED || + get_key_state(key, ctx->now) == DNSSEC_KEY_STATE_POST_ACTIVE || + get_key_state(key, ctx->now) == DNSSEC_KEY_STATE_REMOVED); + key->timing.remove = ctx->now; + + return kdnssec_delete_key(ctx, key); +} + +int knot_dnssec_key_rollover(kdnssec_ctx_t *ctx, zone_sign_reschedule_t *reschedule) +{ + if (ctx == NULL || reschedule == NULL) { + return KNOT_EINVAL; + } + if (ctx->policy->manual) { + return KNOT_EOK; + } + int ret = KNOT_EOK; + uint16_t plan_ds_keytag = 0; + // generate initial keys if missing + if (!key_present(ctx, true, false) && !key_present(ctx, true, true)) { + if (ctx->policy->ksk_shared) { + ret = share_or_generate_key(ctx, GEN_KSK_FLAGS, ctx->now, false); + } else { + ret = generate_key(ctx, GEN_KSK_FLAGS, ctx->now, false); + } + if (ret == KNOT_EOK) { + reschedule->plan_ds_query = true; + plan_ds_keytag = dnssec_key_get_keytag(ctx->zone->keys[0].key); + reschedule->keys_changed = true; + if (!ctx->policy->singe_type_signing && + !key_present(ctx, false, true)) { + ret = generate_key(ctx, DNSKEY_GENERATE_ZSK, ctx->now, false); + } + } + } + // algorithm rollover + if (algorithm_present(ctx, ctx->policy->algorithm) == 0 && + !running_rollover(ctx) && ret == KNOT_EOK) { + if (ctx->policy->ksk_shared) { + ret = share_or_generate_key(ctx, GEN_KSK_FLAGS, 0, true); + } else { + ret = generate_key(ctx, GEN_KSK_FLAGS, 0, true); + } + if (!ctx->policy->singe_type_signing && ret == KNOT_EOK) { + ret = generate_key(ctx, DNSKEY_GENERATE_ZSK, 0, true); + } + log_zone_info(ctx->zone->dname, "DNSSEC, algorithm rollover started"); + if (ret == KNOT_EOK) { + reschedule->keys_changed = true; + } + } + // scheme rollover + if (!signing_scheme_present(ctx) && + !running_rollover(ctx) && ret == KNOT_EOK) { + if (ctx->policy->ksk_shared) { + ret = share_or_generate_key(ctx, GEN_KSK_FLAGS, 0, false); + } else { + ret = generate_key(ctx, GEN_KSK_FLAGS, 0, false); + } + if (!ctx->policy->singe_type_signing && ret == KNOT_EOK) { + ret = generate_key(ctx, DNSKEY_GENERATE_ZSK, 0, false); + } + log_zone_info(ctx->zone->dname, "DNSSEC, signing scheme rollover started"); + if (ret == KNOT_EOK) { + reschedule->keys_changed = true; + } + } + if (ret != KNOT_EOK) { + return ret; + } + + roll_action_t next = next_action(ctx); + + reschedule->next_rollover = next.time; + + if (knot_time_cmp(reschedule->next_rollover, ctx->now) <= 0) { + switch (next.type) { + case GENERATE: + if (next.ksk && ctx->policy->ksk_shared) { + ret = share_or_generate_key(ctx, GEN_KSK_FLAGS, 0, false); + } else { + ret = generate_key(ctx, next.ksk ? GEN_KSK_FLAGS : DNSKEY_GENERATE_ZSK, 0, false); + } + if (ret == KNOT_EOK) { + log_zone_info(ctx->zone->dname, "DNSSEC, %cSK rollover started", + (next.ksk ? 'K' : 'Z')); + } + break; + case PUBLISH: + ret = exec_publish(ctx, next.key); + break; + case SUBMIT: + ret = submit_key(ctx, next.key); + if (ret == KNOT_EOK) { + reschedule->plan_ds_query = true; + plan_ds_keytag = dnssec_key_get_keytag(next.key->key); + } + break; + case REPLACE: + ret = exec_new_signatures(ctx, next.key, 0); + break; + case RETIRE: + ret = exec_ksk_retire(ctx, next.key); + break; + case REMOVE: + ret = exec_remove_old_key(ctx, next.key); + break; + default: + ret = KNOT_EINVAL; + } + + if (ret == KNOT_EOK) { + reschedule->keys_changed = true; + next = next_action(ctx); + reschedule->next_rollover = next.time; + } else { + log_zone_warning(ctx->zone->dname, "DNSSEC, key rollover, action %s (%s)", + roll_action_name(next.type), knot_strerror(ret)); + // fail => try in 10 seconds #TODO better? + reschedule->next_rollover = knot_time_add(knot_time(), 10); + } + } + + if (ret == KNOT_EOK && knot_time_cmp(reschedule->next_rollover, ctx->now) <= 0) { + ret = knot_dnssec_key_rollover(ctx, reschedule); + } + + if (ret == KNOT_EOK && reschedule->keys_changed) { + ret = kdnssec_ctx_commit(ctx); + } + + if (ret == KNOT_EOK && reschedule->plan_ds_query) { + char param[32]; + (void)snprintf(param, sizeof(param), "KEY_SUBMISSION=%hu", plan_ds_keytag); + log_fmt_zone(LOG_NOTICE, LOG_SOURCE_ZONE, ctx->zone->dname, param, + "DNSSEC, KSK submission, waiting for confirmation"); + } + + return ret; +} + +int knot_dnssec_ksk_sbm_confirm(kdnssec_ctx_t *ctx, uint32_t retire_delay) +{ + for (size_t i = 0; i < ctx->zone->num_keys; i++) { + knot_kasp_key_t *key = &ctx->zone->keys[i]; + if (key->is_ksk && get_key_state(key, ctx->now) == DNSSEC_KEY_STATE_READY) { + int ret = exec_new_signatures(ctx, key, retire_delay); + if (ret == KNOT_EOK) { + ret = kdnssec_ctx_commit(ctx); + } + return ret; + } + } + return KNOT_ENOENT; +} + +bool zone_has_key_sbm(const kdnssec_ctx_t *ctx) +{ + assert(ctx->zone); + + for (size_t i = 0; i < ctx->zone->num_keys; i++) { + knot_kasp_key_t *key = &ctx->zone->keys[i]; + if (key->is_ksk && + (get_key_state(key, ctx->now) == DNSSEC_KEY_STATE_READY || + get_key_state(key, ctx->now) == DNSSEC_KEY_STATE_ACTIVE)) { + return true; + } + } + return false; +} diff --git a/src/knot/dnssec/key-events.h b/src/knot/dnssec/key-events.h new file mode 100644 index 0000000..ce0b72e --- /dev/null +++ b/src/knot/dnssec/key-events.h @@ -0,0 +1,57 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "knot/dnssec/context.h" +#include "knot/dnssec/zone-events.h" + +/*! + * \brief Perform correct ZSK and KSK rollover action and plan next one. + * + * For given zone, check keys in KASP db and decide what shall be done + * according to their timers. Perform the action if they shall be done now, + * and tell the user the next time it shall be called. + * + * This function is optimized to be called from KEY_ROLLOVER_EVENT, + * but also during zone load so that the zone gets loaded already with + * proper DNSSEC chain. + * + * \param ctx zone signing context + * \param reschedule Out: timestamp of desired next invoke + * + * \return KNOT_E* + */ +int knot_dnssec_key_rollover(kdnssec_ctx_t *ctx, zone_sign_reschedule_t *reschedule); + +/*! + * \brief Set the submitted KSK to active state and the active one to retired + * + * \param ctx Zone signing context. + * \param retire_delay Retire event delay. + * + * \return KNOT_E* + */ +int knot_dnssec_ksk_sbm_confirm(kdnssec_ctx_t *ctx, uint32_t retire_delay); + +/*! + * \brief Is there a key in sumbmission phase? + * + * \param ctx zone signing context + * + * \return False if there is no submitted key or if error; True otherwise + */ +bool zone_has_key_sbm(const kdnssec_ctx_t *ctx); diff --git a/src/knot/dnssec/nsec-chain.c b/src/knot/dnssec/nsec-chain.c new file mode 100644 index 0000000..dddb8b2 --- /dev/null +++ b/src/knot/dnssec/nsec-chain.c @@ -0,0 +1,444 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> + +#include "knot/dnssec/nsec-chain.h" +#include "knot/dnssec/rrset-sign.h" +#include "knot/dnssec/zone-nsec.h" +#include "knot/dnssec/zone-sign.h" + +/* - NSEC chain construction ------------------------------------------------ */ + +/*! + * \brief Create NSEC RR set. + * + * \param rrset RRSet to be initialized. + * \param from Node that should contain the new RRSet. + * \param to Node that should be pointed to from 'from'. + * \param ttl Record TTL (SOA's minimum TTL). + * + * \return Error code, KNOT_EOK if successful. + */ +static int create_nsec_rrset(knot_rrset_t *rrset, const zone_node_t *from, + const zone_node_t *to, uint32_t ttl) +{ + assert(from); + assert(to); + knot_rrset_init(rrset, from->owner, KNOT_RRTYPE_NSEC, KNOT_CLASS_IN, ttl); + + // Create bitmap + dnssec_nsec_bitmap_t *rr_types = dnssec_nsec_bitmap_new(); + if (!rr_types) { + return KNOT_ENOMEM; + } + + bitmap_add_node_rrsets(rr_types, KNOT_RRTYPE_NSEC, from); + dnssec_nsec_bitmap_add(rr_types, KNOT_RRTYPE_NSEC); + dnssec_nsec_bitmap_add(rr_types, KNOT_RRTYPE_RRSIG); + + // Create RDATA + assert(to->owner); + size_t next_owner_size = knot_dname_size(to->owner); + size_t rdata_size = next_owner_size + dnssec_nsec_bitmap_size(rr_types); + uint8_t rdata[rdata_size]; + + // Fill RDATA + memcpy(rdata, to->owner, next_owner_size); + dnssec_nsec_bitmap_write(rr_types, rdata + next_owner_size); + dnssec_nsec_bitmap_free(rr_types); + + return knot_rrset_add_rdata(rrset, rdata, rdata_size, NULL); +} + +/*! + * \brief Connect two nodes by adding a NSEC RR into the first node. + * + * Callback function, signature chain_iterate_cb. + * + * \param a First node. + * \param b Second node (immediate follower of a). + * \param data Pointer to nsec_chain_iterate_data_t holding parameters + * including changeset. + * + * \return Error code, KNOT_EOK if successful. + */ +static int connect_nsec_nodes(zone_node_t *a, zone_node_t *b, + nsec_chain_iterate_data_t *data) +{ + assert(a); + assert(b); + assert(data); + + if (b->rrset_count == 0 || b->flags & NODE_FLAGS_NONAUTH) { + return NSEC_NODE_SKIP; + } + + int ret = KNOT_EOK; + + /*! + * If the node has no other RRSets than NSEC (and possibly RRSIGs), + * just remove the NSEC and its RRSIG, they are redundant + */ + if (node_rrtype_exists(b, KNOT_RRTYPE_NSEC) + && knot_nsec_empty_nsec_and_rrsigs_in_node(b)) { + ret = knot_nsec_changeset_remove(b, data->changeset); + if (ret != KNOT_EOK) { + return ret; + } + // Skip the 'b' node + return NSEC_NODE_SKIP; + } + + // create new NSEC + knot_rrset_t new_nsec; + ret = create_nsec_rrset(&new_nsec, a, b, data->ttl); + if (ret != KNOT_EOK) { + return ret; + } + + knot_rrset_t old_nsec = node_rrset(a, KNOT_RRTYPE_NSEC); + + if (!knot_rrset_empty(&old_nsec)) { + /* Convert old NSEC to lowercase, just in case it's not. */ + knot_rrset_t *old_nsec_lc = knot_rrset_copy(&old_nsec, NULL); + ret = knot_rrset_rr_to_canonical(old_nsec_lc); + if (ret != KNOT_EOK) { + knot_rrset_free(old_nsec_lc, NULL); + return ret; + } + + bool equal = knot_rrset_equal(&new_nsec, old_nsec_lc, + KNOT_RRSET_COMPARE_WHOLE); + equal = (equal && (old_nsec_lc->ttl == new_nsec.ttl)); + knot_rrset_free(old_nsec_lc, NULL); + + if (equal) { + // current NSEC is valid, do nothing + knot_rdataset_clear(&new_nsec.rrs, NULL); + return KNOT_EOK; + } + + ret = knot_nsec_changeset_remove(a, data->changeset); + if (ret != KNOT_EOK) { + knot_rdataset_clear(&new_nsec.rrs, NULL); + return ret; + } + } + + // Add new NSEC to the changeset (no matter if old was removed) + ret = changeset_add_addition(data->changeset, &new_nsec, 0); + knot_rdataset_clear(&new_nsec.rrs, NULL); + return ret; +} + +/* - API - iterations ------------------------------------------------------- */ + +/*! + * \brief Call a function for each piece of the chain formed by sorted nodes. + */ +int knot_nsec_chain_iterate_create(zone_tree_t *nodes, + chain_iterate_create_cb callback, + nsec_chain_iterate_data_t *data) +{ + assert(nodes); + assert(callback); + + trie_it_t *it = trie_it_begin(nodes); + if (!it) { + return KNOT_ENOMEM; + } + + if (trie_it_finished(it)) { + trie_it_free(it); + return KNOT_EINVAL; + } + + zone_node_t *first = (zone_node_t *)*trie_it_val(it); + zone_node_t *previous = first; + zone_node_t *current = first; + + trie_it_next(it); + + int result = KNOT_EOK; + while (!trie_it_finished(it)) { + current = (zone_node_t *)*trie_it_val(it); + + result = callback(previous, current, data); + if (result == NSEC_NODE_SKIP) { + // No NSEC should be created for 'current' node, skip + ; + } else if (result == KNOT_EOK) { + previous = current; + } else { + trie_it_free(it); + return result; + } + trie_it_next(it); + } + + trie_it_free(it); + + return result == NSEC_NODE_SKIP ? callback(previous, first, data) : + callback(current, first, data); +} + +inline static zone_node_t *it_val(trie_it_t *it) +{ + return (zone_node_t *)*trie_it_val(it); +} + +inline static zone_node_t *it_next0(trie_it_t *it, zone_node_t *first) +{ + trie_it_next(it); + return (trie_it_finished(it) ? first : it_val(it)); +} + +static zone_node_t *it_next1(trie_it_t *it, zone_node_t *first) +{ + zone_node_t *res; + do { + res = it_next0(it, first); + } while (knot_nsec_empty_nsec_and_rrsigs_in_node(res) || (res->flags & NODE_FLAGS_NONAUTH)); + return res; +} + +static zone_node_t *it_next2(trie_it_t *it, zone_node_t *first, changeset_t *ch) +{ + zone_node_t *res = it_next0(it, first); + while (knot_nsec_empty_nsec_and_rrsigs_in_node(res) || (res->flags & NODE_FLAGS_NONAUTH)) { + (void)knot_nsec_changeset_remove(res, ch); + res = it_next0(it, first); + } + return res; +} + +static int node_cmp(zone_node_t *a, zone_node_t *b, zone_node_t *first_a, zone_node_t *first_b) +{ + assert(knot_dname_is_equal(first_a->owner, first_b->owner)); + assert(knot_dname_cmp(first_a->owner, a->owner) <= 0); + assert(knot_dname_cmp(first_b->owner, b->owner) <= 0); + int rev = (a == first_a || b == first_b ? -1 : 1); + return rev * knot_dname_cmp(a->owner, b->owner); +} + +#define CHECK_RET if (ret != KNOT_EOK) goto cleanup + +int knot_nsec_chain_iterate_fix(zone_tree_t *old_nodes, zone_tree_t *new_nodes, + chain_iterate_create_cb callback, + nsec_chain_iterate_data_t *data) +{ + assert(old_nodes); + assert(new_nodes); + assert(callback); + + int ret = KNOT_EOK; + + trie_it_t *old_it = trie_it_begin(old_nodes), *new_it = trie_it_begin(new_nodes); + if (old_it == NULL || new_it == NULL) { + ret = KNOT_ENOMEM; + goto cleanup; + } + + if (trie_it_finished(new_it)) { + ret = KNOT_ENORECORD; + goto cleanup; + } + if (trie_it_finished(old_it)) { + ret = KNOT_ENORECORD; + goto cleanup; + } + + zone_node_t *old_first = it_val(old_it), *new_first = it_val(new_it); + + if (!knot_dname_is_equal(old_first->owner, new_first->owner)) { + // this may happen with NSEC3 (on NSEC, it will be apex) + // it can be solved, but it would complicate the code + // 1. find a common node in both trees (ENORECORD if none) + // 2. start from there and cycle around trie_it_finished() until hit first again + // 3. modify the dname comparison operator ! + ret = KNOT_ENORECORD; + goto cleanup; + } + + if (knot_nsec_empty_nsec_and_rrsigs_in_node(new_first)) { + ret = KNOT_EINVAL; + goto cleanup; + } + + zone_node_t *old_prev = old_first, *new_prev = new_first; + zone_node_t *old_curr = it_next1(old_it, old_first); + zone_node_t *new_curr = it_next2(new_it, new_first, data->changeset); + + while (1) { + bool bitmap_change = !node_bitmap_equal(old_prev, new_prev); + + int cmp = node_cmp(old_curr, new_curr, old_first, new_first); + if (bitmap_change && cmp == 0) { + // if cmp != 0, the nsec chain will be locally rebuilt anyway, + // so no need to update bitmap in such case + // overall, we now have dnames: old_prev == new_prev && old_curr == new_curr + ret = knot_nsec_changeset_remove(old_prev, data->changeset); + CHECK_RET; + ret = callback(new_prev, new_curr, data); + CHECK_RET; + } + + while (cmp != 0) { + if (cmp < 0) { + // a node was removed + ret = knot_nsec_changeset_remove(old_prev, data->changeset); + CHECK_RET; + ret = knot_nsec_changeset_remove(old_curr, data->changeset); + CHECK_RET; + old_prev = old_curr; + old_curr = it_next1(old_it, old_first); + ret = callback(new_prev, new_curr, data); + CHECK_RET; + } else { + // a node was added + ret = knot_nsec_changeset_remove(old_prev, data->changeset); + CHECK_RET; + ret = callback(new_prev, new_curr, data); + CHECK_RET; + new_prev = new_curr; + new_curr = it_next2(new_it, new_first, data->changeset); + ret = callback(new_prev, new_curr, data); + CHECK_RET; + } + cmp = node_cmp(old_curr, new_curr, old_first, new_first); + } + + if (old_curr == old_first && new_curr == new_first) { + break; + } + + old_prev = old_curr; + new_prev = new_curr; + old_curr = it_next1(old_it, old_first); + new_curr = it_next2(new_it, new_first, data->changeset); + } + +cleanup: + trie_it_free(old_it); + trie_it_free(new_it); + return ret; +} + +/* - API - utility functions ------------------------------------------------ */ + +/*! + * \brief Add entry for removed NSEC to the changeset. + */ +int knot_nsec_changeset_remove(const zone_node_t *n, changeset_t *changeset) +{ + if (changeset == NULL) { + return KNOT_EINVAL; + } + + int result = KNOT_EOK; + + knot_rrset_t nsec = node_rrset(n, KNOT_RRTYPE_NSEC); + if (knot_rrset_empty(&nsec)) { + nsec = node_rrset(n, KNOT_RRTYPE_NSEC3); + } + if (!knot_rrset_empty(&nsec)) { + // update changeset + result = changeset_add_removal(changeset, &nsec, 0); + if (result != KNOT_EOK) { + return result; + } + } + + knot_rrset_t rrsigs = node_rrset(n, KNOT_RRTYPE_RRSIG); + if (!knot_rrset_empty(&rrsigs)) { + knot_rrset_t synth_rrsigs; + knot_rrset_init(&synth_rrsigs, n->owner, KNOT_RRTYPE_RRSIG, + KNOT_CLASS_IN, rrsigs.ttl); + result = knot_synth_rrsig(KNOT_RRTYPE_NSEC, &rrsigs.rrs, + &synth_rrsigs.rrs, NULL); + if (result == KNOT_ENOENT) { + // Try removing NSEC3 RRSIGs + result = knot_synth_rrsig(KNOT_RRTYPE_NSEC3, &rrsigs.rrs, + &synth_rrsigs.rrs, NULL); + } + + if (result != KNOT_EOK) { + knot_rdataset_clear(&synth_rrsigs.rrs, NULL); + if (result != KNOT_ENOENT) { + return result; + } + return KNOT_EOK; + } + + // store RRSIG + result = changeset_add_removal(changeset, &synth_rrsigs, 0); + knot_rdataset_clear(&synth_rrsigs.rrs, NULL); + } + + return result; +} + +/*! + * \brief Checks whether the node is empty or eventually contains only NSEC and + * RRSIGs. + */ +bool knot_nsec_empty_nsec_and_rrsigs_in_node(const zone_node_t *n) +{ + assert(n); + for (int i = 0; i < n->rrset_count; ++i) { + knot_rrset_t rrset = node_rrset_at(n, i); + if (rrset.type != KNOT_RRTYPE_NSEC && + rrset.type != KNOT_RRTYPE_RRSIG) { + return false; + } + } + + return true; +} + +/* - API - Chain creation --------------------------------------------------- */ + +/*! + * \brief Create new NSEC chain, add differences from current into a changeset. + */ +int knot_nsec_create_chain(const zone_contents_t *zone, uint32_t ttl, + changeset_t *changeset) +{ + assert(zone); + assert(zone->nodes); + assert(changeset); + + nsec_chain_iterate_data_t data = { ttl, changeset, zone }; + + return knot_nsec_chain_iterate_create(zone->nodes, + connect_nsec_nodes, &data); +} + +int knot_nsec_fix_chain(const zone_contents_t *old_zone, const zone_contents_t *new_zone, + uint32_t ttl, changeset_t *changeset) +{ + assert(old_zone); + assert(new_zone); + assert(old_zone->nodes); + assert(new_zone->nodes); + assert(changeset); + + nsec_chain_iterate_data_t data = { ttl, changeset, new_zone }; + + return knot_nsec_chain_iterate_fix(old_zone->nodes, new_zone->nodes, + connect_nsec_nodes, &data); +} diff --git a/src/knot/dnssec/nsec-chain.h b/src/knot/dnssec/nsec-chain.h new file mode 100644 index 0000000..a2f102e --- /dev/null +++ b/src/knot/dnssec/nsec-chain.h @@ -0,0 +1,151 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <assert.h> +#include <stdbool.h> +#include <stdint.h> + +#include "knot/zone/contents.h" +#include "knot/updates/changesets.h" +#include "libdnssec/nsec.h" + +/*! + * \brief Parameters to be used in connect_nsec_nodes callback. + */ +typedef struct { + uint32_t ttl; // TTL for NSEC(3) records + changeset_t *changeset; // Changeset for NSEC(3) changes + const zone_contents_t *zone; // Updated zone +} nsec_chain_iterate_data_t; + +/*! + * \brief Used to control changeset iteration functions. + */ +enum { + NSEC_NODE_SKIP = 1, +}; + +/*! + * \brief Callback used when creating NSEC chains. + */ +typedef int (*chain_iterate_create_cb)(zone_node_t *, zone_node_t *, + nsec_chain_iterate_data_t *); + +/*! + * \brief Add all RR types from a node into the bitmap. + */ +inline static void bitmap_add_node_rrsets(dnssec_nsec_bitmap_t *bitmap, + enum knot_rr_type nsec_type, + const zone_node_t *node) +{ + bool deleg = node->flags & NODE_FLAGS_DELEG; + bool apex = node->parent == NULL; + for (int i = 0; i < node->rrset_count; i++) { + knot_rrset_t rr = node_rrset_at(node, i); + if (deleg && (rr.type != KNOT_RRTYPE_NS && rr.type != KNOT_RRTYPE_DS)) { + continue; + } + if (rr.type == KNOT_RRTYPE_NSEC || rr.type == KNOT_RRTYPE_RRSIG) { + continue; + } + // NSEC3PARAM in zone apex is maintained automatically + if (apex && rr.type == KNOT_RRTYPE_NSEC3PARAM && nsec_type != KNOT_RRTYPE_NSEC3) { + continue; + } + + dnssec_nsec_bitmap_add(bitmap, rr.type); + } +} + +/*! + * \brief Call a function for each piece of the chain formed by sorted nodes. + * + * \note If the callback function returns anything other than KNOT_EOK, the + * iteration is terminated and the error code is propagated. + * + * \param nodes Zone nodes. + * \param callback Callback function. + * \param data Custom data supplied to the callback function. + * + * \return Error code, KNOT_EOK if successful. + */ +int knot_nsec_chain_iterate_create(zone_tree_t *nodes, + chain_iterate_create_cb callback, + nsec_chain_iterate_data_t *data); + +/*! + * \brief Call the chain-connecting function for modified records and their neighbours. + * + * \param old_nodes Old state of zone nodes. + * \param new_nodes New state of zone nodes. + * \param callback Callback function. + * \param data Custom data supplied, incl. changeset to be updated. + * + * \retval KNOT_ENORECORD if the chain must be recreated from scratch. + * \return KNOT_E* + */ +int knot_nsec_chain_iterate_fix(zone_tree_t *old_nodes, zone_tree_t *new_nodes, + chain_iterate_create_cb callback, + nsec_chain_iterate_data_t *data); + +/*! + * \brief Add entry for removed NSEC(3) and its RRSIG to the changeset. + * + * \param n Node to extract NSEC(3) from. + * \param changeset Changeset to add the old RR into. + * + * \return Error code, KNOT_EOK if successful. + */ +int knot_nsec_changeset_remove(const zone_node_t *n, changeset_t *changeset); + +/*! + * \brief Checks whether the node is empty or eventually contains only NSEC and + * RRSIGs. + * + * \param n Node to check. + * + * \retval true if the node is empty or contains only NSEC and RRSIGs. + * \retval false otherwise. + */ +bool knot_nsec_empty_nsec_and_rrsigs_in_node(const zone_node_t *n); + +/*! + * \brief Create new NSEC chain, add differences from current into a changeset. + * + * \param zone Zone. + * \param ttl TTL for created NSEC records. + * \param changeset Changeset the differences will be put into. + * + * \return Error code, KNOT_EOK if successful. + */ +int knot_nsec_create_chain(const zone_contents_t *zone, uint32_t ttl, + changeset_t *changeset); + +/*! + * \brief Fix existing NSEC chain to cover the changes in zone contents. + * + * \param old_zone Old zone contents. + * \param new_zone New zone contents. + * \param ttl TTL for created NSEC records. + * \param changeset Changeset the differences will be put into. + * + * \retval KNOT_ENORECORD if the chain must be recreated from scratch. + * \return KNOT_E* + */ +int knot_nsec_fix_chain(const zone_contents_t *old_zone, const zone_contents_t *new_zone, + uint32_t ttl, changeset_t *changeset); diff --git a/src/knot/dnssec/nsec3-chain.c b/src/knot/dnssec/nsec3-chain.c new file mode 100644 index 0000000..eb756e1 --- /dev/null +++ b/src/knot/dnssec/nsec3-chain.c @@ -0,0 +1,830 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> + +#include "libknot/dname.h" +#include "knot/dnssec/nsec-chain.h" +#include "knot/dnssec/nsec3-chain.h" +#include "knot/dnssec/zone-sign.h" +#include "knot/dnssec/zone-nsec.h" +#include "knot/zone/zone-diff.h" +#include "contrib/base32hex.h" +#include "contrib/macros.h" +#include "contrib/wire_ctx.h" + +/* - NSEC3 node comparison -------------------------------------------------- */ + +/*! + * \brief Perform some basic checks that the node is a valid NSEC3 node. + */ +inline static bool valid_nsec3_node(const zone_node_t *node) +{ + assert(node); + + if (node->rrset_count > 2) { + return false; + } + + const knot_rdataset_t *nsec3 = node_rdataset(node, KNOT_RRTYPE_NSEC3); + if (nsec3 == NULL) { + return false; + } + + if (nsec3->count != 1) { + return false; + } + + return true; +} + +/*! + * \brief Check if two nodes are equal. + */ +static bool are_nsec3_nodes_equal(const zone_node_t *a, const zone_node_t *b) +{ + if (!(valid_nsec3_node(a) && valid_nsec3_node(b))) { + return false; + } + + knot_rrset_t a_rrset = node_rrset(a, KNOT_RRTYPE_NSEC3); + knot_rrset_t b_rrset = node_rrset(b, KNOT_RRTYPE_NSEC3); + return knot_rrset_equal(&a_rrset, &b_rrset, KNOT_RRSET_COMPARE_WHOLE) && + (a_rrset.ttl == b_rrset.ttl); +} + +static bool nsec3_opt_out(const zone_node_t *node, bool opt_out_enabled) +{ + return (opt_out_enabled && (node->flags & NODE_FLAGS_DELEG) && + !node_rrtype_exists(node, KNOT_RRTYPE_DS)); +} + +/*! + * \brief Check whether at least one RR type in node should be signed, + * used when signing with NSEC3. + * + * \param node Node for which the check is done. + * + * \return true/false. + */ +static bool node_should_be_signed_nsec3(const zone_node_t *n) +{ + for (int i = 0; i < n->rrset_count; i++) { + knot_rrset_t rrset = node_rrset_at(n, i); + if (rrset.type == KNOT_RRTYPE_NSEC || + rrset.type == KNOT_RRTYPE_RRSIG) { + continue; + } + + if (knot_zone_sign_rr_should_be_signed(n, &rrset)) { + return true; + } + } + + return false; +} + +/* - RRSIGs handling for NSEC3 ---------------------------------------------- */ + +/*! + * \brief Shallow copy NSEC3 signatures from the one node to the second one. + * Just sets the pointer, needed only for comparison. + */ +static int shallow_copy_signature(const zone_node_t *from, zone_node_t *to) +{ + assert(valid_nsec3_node(from)); + assert(valid_nsec3_node(to)); + + knot_rrset_t from_sig = node_rrset(from, KNOT_RRTYPE_RRSIG); + if (knot_rrset_empty(&from_sig)) { + return KNOT_EOK; + } + return node_add_rrset(to, &from_sig, NULL); +} + +/*! + * \brief Reuse signatatures by shallow copying them from one tree to another. + */ +static int copy_signatures(zone_tree_t *from, zone_tree_t *to) +{ + if (zone_tree_is_empty(from)) { + return KNOT_EOK; + } + + assert(to); + + trie_it_t *it = trie_it_begin(from); + + for (/* NOP */; !trie_it_finished(it); trie_it_next(it)) { + zone_node_t *node_from = (zone_node_t *)*trie_it_val(it); + + zone_node_t *node_to = zone_tree_get(to, node_from->owner); + if (node_to == NULL) { + continue; + } + + if (!are_nsec3_nodes_equal(node_from, node_to)) { + continue; + } + + int ret = shallow_copy_signature(node_from, node_to); + if (ret != KNOT_EOK) { + trie_it_free(it); + return ret; + } + } + + trie_it_free(it); + return KNOT_EOK; +} + +/*! + * \brief Custom NSEC3 tree free function. + * + */ +static void free_nsec3_tree(zone_tree_t *nodes) +{ + assert(nodes); + + trie_it_t *it = trie_it_begin(nodes); + for (/* NOP */; !trie_it_finished(it); trie_it_next(it)) { + zone_node_t *node = (zone_node_t *)*trie_it_val(it); + // newly allocated NSEC3 nodes + knot_rdataset_t *nsec3 = node_rdataset(node, KNOT_RRTYPE_NSEC3); + knot_rdataset_t *rrsig = node_rdataset(node, KNOT_RRTYPE_RRSIG); + knot_rdataset_clear(nsec3, NULL); + knot_rdataset_clear(rrsig, NULL); + node_free(node, NULL); + } + + trie_it_free(it); + zone_tree_free(&nodes); +} + +/* - NSEC3 nodes construction ----------------------------------------------- */ + +/*! + * \brief Get NSEC3 RDATA size. + */ +static size_t nsec3_rdata_size(const dnssec_nsec3_params_t *params, + const dnssec_nsec_bitmap_t *rr_types) +{ + assert(params); + assert(rr_types); + + return 6 + params->salt.size + + dnssec_nsec3_hash_length(params->algorithm) + + dnssec_nsec_bitmap_size(rr_types); +} + +/*! + * \brief Fill NSEC3 RDATA. + * + * \note Content of next hash field is not changed. + */ +static int nsec3_fill_rdata(uint8_t *rdata, size_t rdata_len, + const dnssec_nsec3_params_t *params, + const dnssec_nsec_bitmap_t *rr_types, + const uint8_t *next_hashed) +{ + assert(rdata); + assert(params); + assert(rr_types); + + uint8_t hash_length = dnssec_nsec3_hash_length(params->algorithm); + + wire_ctx_t wire = wire_ctx_init(rdata, rdata_len); + + wire_ctx_write_u8(&wire, params->algorithm); + wire_ctx_write_u8(&wire, params->flags); + wire_ctx_write_u16(&wire, params->iterations); + wire_ctx_write_u8(&wire, params->salt.size); + wire_ctx_write(&wire, params->salt.data, params->salt.size); + wire_ctx_write_u8(&wire, hash_length); + + if (next_hashed != NULL) { + wire_ctx_write(&wire, next_hashed, hash_length); + } else { + wire_ctx_skip(&wire, hash_length); + } + + if (wire.error != KNOT_EOK) { + return wire.error; + } + + dnssec_nsec_bitmap_write(rr_types, wire.position); + + return KNOT_EOK; +} + +/*! + * \brief Creates NSEC3 RRSet. + * + * \param owner Owner for the RRSet. + * \param params Parsed NSEC3PARAM. + * \param rr_types Bitmap. + * \param next_hashed Next hashed. + * \param ttl TTL for the RRSet. + * + * \return Pointer to created RRSet on success, NULL on errors. + */ +static int create_nsec3_rrset(knot_rrset_t *rrset, + const knot_dname_t *owner, + const dnssec_nsec3_params_t *params, + const dnssec_nsec_bitmap_t *rr_types, + const uint8_t *next_hashed, + uint32_t ttl) +{ + assert(rrset); + assert(owner); + assert(params); + assert(rr_types); + + knot_dname_t *owner_copy = knot_dname_copy(owner, NULL); + if (owner_copy == NULL) { + return KNOT_ENOMEM; + } + knot_rrset_init(rrset, owner_copy, KNOT_RRTYPE_NSEC3, KNOT_CLASS_IN, ttl); + + size_t rdata_size = nsec3_rdata_size(params, rr_types); + uint8_t rdata[rdata_size]; + memset(rdata, 0, rdata_size); + int ret = nsec3_fill_rdata(rdata, rdata_size, params, rr_types, + next_hashed); + if (ret != KNOT_EOK) { + knot_dname_free(owner_copy, NULL); + return ret; + } + + ret = knot_rrset_add_rdata(rrset, rdata, rdata_size, NULL); + if (ret != KNOT_EOK) { + knot_dname_free(owner_copy, NULL); + return ret; + } + + return KNOT_EOK; +} + +/*! + * \brief Create NSEC3 node. + */ +static zone_node_t *create_nsec3_node(const knot_dname_t *owner, + const dnssec_nsec3_params_t *nsec3_params, + zone_node_t *apex_node, + const dnssec_nsec_bitmap_t *rr_types, + uint32_t ttl) +{ + assert(owner); + assert(nsec3_params); + assert(apex_node); + assert(rr_types); + + zone_node_t *new_node = node_new(owner, NULL); + if (!new_node) { + return NULL; + } + + node_set_parent(new_node, apex_node); + + knot_rrset_t nsec3_rrset; + int ret = create_nsec3_rrset(&nsec3_rrset, owner, nsec3_params, + rr_types, NULL, ttl); + if (ret != KNOT_EOK) { + node_free(new_node, NULL); + return NULL; + } + + ret = node_add_rrset(new_node, &nsec3_rrset, NULL); + knot_rrset_clear(&nsec3_rrset, NULL); + if (ret != KNOT_EOK) { + node_free(new_node, NULL); + return NULL; + } + + return new_node; +} + +/*! + * \brief Create new NSEC3 node for given regular node. + * + * \param node Node for which the NSEC3 node is created. + * \param apex Zone apex node. + * \param params NSEC3 hash function parameters. + * \param ttl TTL of the new NSEC3 node. + * \param apex_cds Hint to guess apex node type bitmap: false=just DNSKEY, true=DNSKEY,CDS,CDNSKEY. + * + * \return Error code, KNOT_EOK if successful. + */ +static zone_node_t *create_nsec3_node_for_node(const zone_node_t *node, + zone_node_t *apex, + const dnssec_nsec3_params_t *params, + uint32_t ttl) +{ + assert(node); + assert(apex); + assert(params); + + uint8_t nsec3_owner[KNOT_DNAME_MAXLEN]; + int ret = knot_create_nsec3_owner(nsec3_owner, sizeof(nsec3_owner), + node->owner, apex->owner, params); + if (ret != KNOT_EOK) { + return NULL; + } + + dnssec_nsec_bitmap_t *rr_types = dnssec_nsec_bitmap_new(); + if (!rr_types) { + return NULL; + } + + bitmap_add_node_rrsets(rr_types, KNOT_RRTYPE_NSEC3, node); + if (node->rrset_count > 0 && node_should_be_signed_nsec3(node)) { + dnssec_nsec_bitmap_add(rr_types, KNOT_RRTYPE_RRSIG); + } + if (node == apex) { + dnssec_nsec_bitmap_add(rr_types, KNOT_RRTYPE_NSEC3PARAM); + } + + zone_node_t *nsec3_node = create_nsec3_node(nsec3_owner, params, apex, + rr_types, ttl); + dnssec_nsec_bitmap_free(rr_types); + + return nsec3_node; +} + +/* - NSEC3 chain creation --------------------------------------------------- */ + +// see connect_nsec3_nodes() for what this function does +static int connect_nsec3_base(knot_rdataset_t *a_rrs, const knot_dname_t *b_name) +{ + assert(a_rrs); + uint8_t algorithm = knot_nsec3_alg(a_rrs->rdata); + if (algorithm == 0) { + return KNOT_EINVAL; + } + + uint8_t raw_length = knot_nsec3_next_len(a_rrs->rdata); + uint8_t *raw_hash = (uint8_t *)knot_nsec3_next(a_rrs->rdata); + if (raw_hash == NULL) { + return KNOT_EINVAL; + } + + assert(raw_length == dnssec_nsec3_hash_length(algorithm)); + + char *b32_hash = knot_dname_to_str_alloc(b_name); + if (!b32_hash) { + return KNOT_ENOMEM; + } + + char *b32_end = strchr(b32_hash, '.'); + if (!b32_end) { + free(b32_hash); + return KNOT_EINVAL; + } + + size_t b32_length = b32_end - b32_hash; + int32_t written = base32hex_decode((uint8_t *)b32_hash, b32_length, + raw_hash, raw_length); + + free(b32_hash); + + if (written != raw_length) { + return KNOT_EINVAL; + } + + return KNOT_EOK; +} + +/*! + * \brief Connect two nodes by filling 'hash' field of NSEC3 RDATA of the first node. + * + * \param a First node. Gets modified in-place! + * \param b Second node (immediate follower of a). + * \param data Unused parameter. + * + * \return Error code, KNOT_EOK if successful. + */ +static int connect_nsec3_nodes(zone_node_t *a, zone_node_t *b, + nsec_chain_iterate_data_t *data) +{ + assert(a); + assert(b); + UNUSED(data); + + assert(a->rrset_count == 1); + + return connect_nsec3_base(node_rdataset(a, KNOT_RRTYPE_NSEC3), b->owner); +} + +/*! + * \brief Connect two nodes by updating the changeset. + * + * \param a First node. + * \param b Second node. + * \param data Contains the changeset to be updated. + * + * \return Error code, KNOT_EOK if successful. + */ +static int connect_nsec3_nodes2(zone_node_t *a, zone_node_t *b, + nsec_chain_iterate_data_t *data) +{ + assert(data); + + // check if the NSEC3 rrset has not been updated in changeset + knot_rrset_t aorig = node_rrset(a, KNOT_RRTYPE_NSEC3); + const zone_node_t *ch_a = zone_contents_find_nsec3_node(data->changeset->add, a->owner); + if (node_rrtype_exists(ch_a, KNOT_RRTYPE_NSEC3)) { + aorig = node_rrset(ch_a, KNOT_RRTYPE_NSEC3); + } + + // prepare a copy of NSEC3 rrsets in question + knot_rrset_t *acopy = knot_rrset_copy(&aorig, NULL); + if (acopy == NULL) { + return KNOT_ENOMEM; + } + + // connect the copied rrset + int ret = connect_nsec3_base(&acopy->rrs, b->owner); + if (ret != KNOT_EOK || knot_rrset_equal(&aorig, acopy, KNOT_RRSET_COMPARE_WHOLE)) { + knot_rrset_free(acopy, NULL); + return ret; + } + + // add the removed original and the updated copy to changeset + if (node_rrtype_exists(ch_a, KNOT_RRTYPE_NSEC3)) { + ret = changeset_remove_addition(data->changeset, &aorig); + } else { + ret = changeset_add_removal(data->changeset, &aorig, 0); + } + if (ret == KNOT_EOK) { + ret = changeset_add_addition(data->changeset, acopy, CHANGESET_CHECK | CHANGESET_CHECK_CANCELOUT); + } + knot_rrset_free(acopy, NULL); + return ret; +} + +/*! + * \brief Create NSEC3 node for each regular node in the zone. + * + * \param zone Zone. + * \param params NSEC3 params. + * \param ttl TTL for the created NSEC records. + * \param cds_in_apex Hint to guess apex node type bitmap: false=just DNSKEY, true=DNSKEY,CDS,CDNSKEY. + * \param nsec3_nodes Tree whereto new NSEC3 nodes will be added. + * \param chgset Changeset used for possible NSEC removals + * + * \return Error code, KNOT_EOK if successful. + */ +static int create_nsec3_nodes(const zone_contents_t *zone, + const dnssec_nsec3_params_t *params, + uint32_t ttl, + zone_tree_t *nsec3_nodes, + changeset_t *chgset) +{ + assert(zone); + assert(nsec3_nodes); + assert(chgset); + + int result = KNOT_EOK; + + trie_it_t *it = trie_it_begin(zone->nodes); + while (!trie_it_finished(it)) { + zone_node_t *node = (zone_node_t *)*trie_it_val(it); + + /*! + * Remove possible NSEC from the node. (Do not allow both NSEC + * and NSEC3 in the zone at once.) + */ + result = knot_nsec_changeset_remove(node, chgset); + if (result != KNOT_EOK) { + break; + } + if (node->flags & NODE_FLAGS_NONAUTH || node->flags & NODE_FLAGS_EMPTY) { + trie_it_next(it); + continue; + } + + zone_node_t *nsec3_node; + nsec3_node = create_nsec3_node_for_node(node, zone->apex, + params, ttl); + if (!nsec3_node) { + result = KNOT_ENOMEM; + break; + } + + result = zone_tree_insert(nsec3_nodes, nsec3_node); + if (result != KNOT_EOK) { + break; + } + + trie_it_next(it); + } + + trie_it_free(it); + + return result; +} + +/*! + * \brief For given dname, check if anything changed in zone_update, and recreate (possibly unconnected) NSEC3 nodes appropriately. + * + * The removed/added/modified NSEC3 records are stored in two ways depending of their nature: + * a) Those NSEC3 records pre-created with (probably) empty "next dname", waiting to be connected with 2nd round, are put directly into the zone_update_t structure. + * b) Those with just recreated bitmap are just added into the changeset. We must note them in the changeset anyway when reconnecting! + * The reason is, that the (a) type of records are not going to be signed, but they are needed for proper connecting the NSEC3 chain. + * On the other hand, the (b) type of records need to be signed and they have no influence on the chain structure. + * + * \param update Zone update structure holding zone contents changes. + * \param params NSEC3 params. + * \param ttl TTL for newly created NSEC3 records. + * \param chgset Changeset to hold the changes. + * \param for_node Domain name of the node in question. + * + * \retval KNOT_ENORECORD if the NSEC3 chain shall be rather recreated completely. + * \return KNOT_EOK, KNOT_E* if any error. + */ +static int fix_nsec3_for_node(zone_update_t *update, const dnssec_nsec3_params_t *params, + uint32_t ttl, bool opt_out, changeset_t *chgset, const knot_dname_t *for_node) +{ + // check if we need to do something + const zone_node_t *old_n = zone_contents_find_node(update->zone->contents, for_node); + const zone_node_t *new_n = zone_contents_find_node(update->new_cont, for_node); + if (node_bitmap_equal(old_n, new_n)) { + return KNOT_EOK; + } + + if ((new_n != NULL && knot_nsec_empty_nsec_and_rrsigs_in_node(new_n) && new_n->children > 0) || + (old_n != NULL && knot_nsec_empty_nsec_and_rrsigs_in_node(old_n) && old_n->children > 0)) { + // handling empty non-terminal creation and downfall is too difficult, recreate NSEC3 from scratch + return KNOT_ENORECORD; + } + + uint8_t for_node_hashed[KNOT_DNAME_MAXLEN]; + int ret = knot_create_nsec3_owner(for_node_hashed, sizeof(for_node_hashed), + for_node, update->new_cont->apex->owner, params); + if (ret != KNOT_EOK) { + return ret; + } + + // saved hash of next node + uint8_t *next_hash = NULL; + uint8_t next_length = 0; + + bool add_nsec3 = (new_n != NULL && !node_empty(new_n) && !(new_n->flags & NODE_FLAGS_NONAUTH) && + !nsec3_opt_out(new_n, opt_out)); + + // remove (all) existing NSEC3 + const zone_node_t *old_nsec3_n = zone_contents_find_nsec3_node(update->zone->contents, for_node_hashed); + if (old_nsec3_n != NULL) { + knot_rrset_t rem_nsec3 = node_rrset(old_nsec3_n, KNOT_RRTYPE_NSEC3); + if (!knot_rrset_empty(&rem_nsec3)) { + knot_rrset_t rem_rrsig = node_rrset(old_nsec3_n, KNOT_RRTYPE_RRSIG); + if (!add_nsec3) { + ret = zone_update_remove(update, &rem_nsec3); + if (ret == KNOT_EOK && !knot_rrset_empty(&rem_rrsig)) { + ret = zone_update_remove(update, &rem_rrsig); + } + } else { + ret = changeset_add_removal(chgset, &rem_nsec3, CHANGESET_CHECK | CHANGESET_CHECK_CANCELOUT); + if (ret == KNOT_EOK && !knot_rrset_empty(&rem_rrsig)) { + ret = changeset_add_removal(chgset, &rem_rrsig, 0); + } + } + next_hash = (uint8_t *)knot_nsec3_next(rem_nsec3.rrs.rdata); + next_length = knot_nsec3_next_len(rem_nsec3.rrs.rdata); + } + } + + // add NSEC3 with correct bitmap + if (add_nsec3 && ret == KNOT_EOK) { + zone_node_t *new_nsec3_n = create_nsec3_node_for_node(new_n, update->new_cont->apex, params, ttl); + if (new_nsec3_n == NULL) { + return KNOT_ENOMEM; + } + knot_rrset_t add_nsec3 = node_rrset(new_nsec3_n, KNOT_RRTYPE_NSEC3); + assert(!knot_rrset_empty(&add_nsec3)); + + // copy hash of next element from removed record + if (next_hash != NULL) { + uint8_t *raw_hash = (uint8_t *)knot_nsec3_next(add_nsec3.rrs.rdata); + uint8_t raw_length = knot_nsec3_next_len(add_nsec3.rrs.rdata); + assert(raw_hash != NULL); + if (raw_length != next_length) { + ret = KNOT_EMALF; + } else { + memcpy(raw_hash, next_hash, raw_length); + } + } + if (ret == KNOT_EOK) { + if (next_hash == NULL) { + ret = zone_update_add(update, &add_nsec3); + } else { + ret = changeset_add_addition(chgset, &add_nsec3, CHANGESET_CHECK | CHANGESET_CHECK_CANCELOUT); + } + } + node_free_rrsets(new_nsec3_n, NULL); + node_free(new_nsec3_n, NULL); + } + + return ret; +} + +static int fix_nsec3_nodes(zone_update_t *update, const dnssec_nsec3_params_t *params, + uint32_t ttl, bool opt_out, changeset_t *chgset) +{ + assert(update); + + int ret = KNOT_EOK; + + trie_it_t *rem_it = trie_it_begin(update->change.remove->nodes); + while (!trie_it_finished(rem_it) && ret == KNOT_EOK) { + zone_node_t *n = (zone_node_t *)*trie_it_val(rem_it); + ret = fix_nsec3_for_node(update, params, ttl, opt_out, chgset, n->owner); + trie_it_next(rem_it); + } + trie_it_free(rem_it); + + trie_it_t *add_it = trie_it_begin(update->change.add->nodes); + while (!trie_it_finished(add_it) && ret == KNOT_EOK) { + zone_node_t *n = (zone_node_t *)*trie_it_val(add_it); + ret = fix_nsec3_for_node(update, params, ttl, opt_out, chgset, n->owner); + trie_it_next(add_it); + } + trie_it_free(add_it); + + return ret; +} + +/*! + * \brief Checks if NSEC3 should be generated for this node. + * + * \retval true if the node has no children and contains no RRSets or only + * RRSIGs and NSECs. + * \retval false otherwise. + */ +static bool nsec3_is_empty(zone_node_t *node, bool opt_out) +{ + if (node->children > 0) { + return false; + } + + return knot_nsec_empty_nsec_and_rrsigs_in_node(node) || nsec3_opt_out(node, opt_out); +} + +/*! + * \brief Marks node and its parents as empty if NSEC3 should not be generated + * for them. + * + * It also lowers the children count for the parent of marked node. This must be + * fixed before further operations on the zone. + */ +static int nsec3_mark_empty(zone_node_t **node_p, void *data) +{ + zone_node_t *node = *node_p; + + if (!(node->flags & NODE_FLAGS_EMPTY) && nsec3_is_empty(node, (data != NULL))) { + /*! + * Mark this node and all parent nodes that meet the same + * criteria as empty. + */ + node->flags |= NODE_FLAGS_EMPTY; + + if (node->parent) { + /* We must decrease the parent's children count, + * but only temporarily! It must be set back right after + * the operation + */ + node->parent->children--; + /* Recurse using the parent node */ + return nsec3_mark_empty(&node->parent, data); + } + } + + return KNOT_EOK; +} + +/*! + * \brief Resets the empty flag in the node and increases its parent's children + * count if the node was marked as empty. + * + * The children count of node's parent is increased if this node was marked as + * empty, as it was previously decreased in the \a nsec3_mark_empty() function. + */ +static int nsec3_reset(zone_node_t **node_p, void *data) +{ + UNUSED(data); + zone_node_t *node = *node_p; + + if (node->flags & NODE_FLAGS_EMPTY) { + /* If node was marked as empty, increase its parent's children + * count. + */ + node->parent->children++; + /* Clear the 'empty' flag. */ + node->flags &= ~NODE_FLAGS_EMPTY; + } + + return KNOT_EOK; +} + +/* - Public API ------------------------------------------------------------- */ + +/*! + * \brief Create new NSEC3 chain, add differences from current into a changeset. + */ +int knot_nsec3_create_chain(const zone_contents_t *zone, + const dnssec_nsec3_params_t *params, + uint32_t ttl, + bool opt_out, + changeset_t *changeset) +{ + assert(zone); + assert(params); + assert(changeset); + + int result; + + zone_tree_t *nsec3_nodes = zone_tree_create(); + if (!nsec3_nodes) { + return KNOT_ENOMEM; + } + + /* Before creating NSEC3 nodes, we must temporarily mark those nodes + * that may still be in the zone, but for which the NSEC3s should not + * be created. I.e. nodes with only RRSIG (or NSEC+RRSIG) and their + * predecessors if they are empty. + * + * The flag will be removed when the node is encountered during NSEC3 + * creation procedure. + */ + result = zone_tree_apply(zone->nodes, nsec3_mark_empty, (opt_out ? (void *)zone : NULL)); + if (result != KNOT_EOK) { + free_nsec3_tree(nsec3_nodes); + return result; + } + + result = create_nsec3_nodes(zone, params, ttl, nsec3_nodes, changeset); + if (result != KNOT_EOK) { + free_nsec3_tree(nsec3_nodes); + return result; + } + + /* Resets empty node flag and children count in nodes that were + * previously marked as empty. Must be called after NSEC3 generation, + * so that flags and children count are back to normal before further + * processing. + */ + result = zone_tree_apply(zone->nodes, nsec3_reset, NULL); + if (result != KNOT_EOK) { + free_nsec3_tree(nsec3_nodes); + return result; + } + + result = knot_nsec_chain_iterate_create(nsec3_nodes, + connect_nsec3_nodes, NULL); + if (result != KNOT_EOK) { + free_nsec3_tree(nsec3_nodes); + return result; + } + + copy_signatures(zone->nsec3_nodes, nsec3_nodes); + + result = zone_tree_add_diff(zone->nsec3_nodes, nsec3_nodes, changeset); + + free_nsec3_tree(nsec3_nodes); + + return result; +} + +int knot_nsec3_fix_chain(zone_update_t *update, + const dnssec_nsec3_params_t *params, + uint32_t ttl, + bool opt_out, + changeset_t *changeset) +{ + + int ret = fix_nsec3_nodes(update, params, ttl, opt_out, changeset); + if (ret != KNOT_EOK) { + return ret; + } + + nsec_chain_iterate_data_t data = { ttl, changeset, update->new_cont }; + + ret = knot_nsec_chain_iterate_fix(update->zone->contents->nsec3_nodes, + update->new_cont->nsec3_nodes, + connect_nsec3_nodes2, &data); + + return ret; +} diff --git a/src/knot/dnssec/nsec3-chain.h b/src/knot/dnssec/nsec3-chain.h new file mode 100644 index 0000000..6dafc88 --- /dev/null +++ b/src/knot/dnssec/nsec3-chain.h @@ -0,0 +1,57 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <stdint.h> +#include "libdnssec/nsec.h" +#include "knot/updates/changesets.h" +#include "knot/updates/zone-update.h" +#include "knot/zone/contents.h" + +/*! + * \brief Creates new NSEC3 chain, add differences from current into a changeset. + * + * \param zone Zone to be checked. + * \param params NSEC3 parameters. + * \param ttl TTL for new records. + * \param opt_out NSEC3 opt-out enabled for insecure delegations. + * \param changeset Changeset to store changes into. + * + * \return KNOT_E* + */ +int knot_nsec3_create_chain(const zone_contents_t *zone, + const dnssec_nsec3_params_t *params, + uint32_t ttl, + bool opt_out, + changeset_t *changeset); + +/*! + * \brief Updates zone's NSEC3 chain to follow the differences in zone update. + * + * \param update Zone Update structure holding the zone and its update. Also modified! + * \param params NSEC3 parameters. + * \param ttl TTL for new records. + * \param opt_out NSEC3 opt-out enabled for insecure delegations. + * \param changeset Changeset to store changes into. Some changes are pushed directly to update. + * + * \retval KNOT_ENORECORD if the chain must be recreated from scratch. + * \return KNOT_E* + */ +int knot_nsec3_fix_chain(zone_update_t *update, + const dnssec_nsec3_params_t *params, + uint32_t ttl, bool opt_out, + changeset_t *changeset); diff --git a/src/knot/dnssec/policy.c b/src/knot/dnssec/policy.c new file mode 100644 index 0000000..fdafb4c --- /dev/null +++ b/src/knot/dnssec/policy.c @@ -0,0 +1,47 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <assert.h> + +#include "knot/dnssec/policy.h" +#include "libknot/rrtype/soa.h" + +static uint32_t zone_soa_min_ttl(const zone_contents_t *zone) +{ + knot_rrset_t soa = node_rrset(zone->apex, KNOT_RRTYPE_SOA); + return knot_soa_minimum(soa.rrs.rdata); +} + +static uint32_t zone_soa_ttl(const zone_contents_t *zone) +{ + knot_rrset_t soa = node_rrset(zone->apex, KNOT_RRTYPE_SOA); + return soa.ttl; +} + +void update_policy_from_zone(knot_kasp_policy_t *policy, + const zone_contents_t *zone) +{ + assert(policy); + assert(zone); + + // Use SOA TTL if not configured. + if (policy->dnskey_ttl == UINT32_MAX) { + policy->dnskey_ttl = zone_soa_ttl(zone); + } + + policy->soa_minimal_ttl = zone_soa_min_ttl(zone); + policy->zone_maximal_ttl = zone->max_ttl; +} diff --git a/src/knot/dnssec/policy.h b/src/knot/dnssec/policy.h new file mode 100644 index 0000000..b6ba4d1 --- /dev/null +++ b/src/knot/dnssec/policy.h @@ -0,0 +1,26 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include "knot/dnssec/context.h" +#include "knot/zone/contents.h" + +/*! + * \brief Update policy parameters depending on zone content. + */ +void update_policy_from_zone(knot_kasp_policy_t *policy, + const zone_contents_t *zone); diff --git a/src/knot/dnssec/rrset-sign.c b/src/knot/dnssec/rrset-sign.c new file mode 100644 index 0000000..6004ab9 --- /dev/null +++ b/src/knot/dnssec/rrset-sign.c @@ -0,0 +1,395 @@ +/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> + +#include "contrib/wire_ctx.h" +#include "libdnssec/error.h" +#include "knot/dnssec/rrset-sign.h" +#include "knot/dnssec/zone-sign.h" +#include "libknot/libknot.h" + +#define RRSIG_RDATA_SIGNER_OFFSET 18 + +#define RRSIG_INCEPT_IN_PAST (90 * 60) + +/*- Creating of RRSIGs -------------------------------------------------------*/ + +/*! + * \brief Get size of RRSIG RDATA for a given key without signature. + */ +static size_t rrsig_rdata_header_size(const dnssec_key_t *key) +{ + if (!key) { + return 0; + } + + size_t size; + + // static part + + size = sizeof(uint16_t) // type covered + + sizeof(uint8_t) // algorithm + + sizeof(uint8_t) // labels + + sizeof(uint32_t) // original TTL + + sizeof(uint32_t) // signature expiration + + sizeof(uint32_t) // signature inception + + sizeof(uint16_t); // key tag (footprint) + + assert(size == RRSIG_RDATA_SIGNER_OFFSET); + + // variable part + + size += knot_dname_size(dnssec_key_get_dname(key)); + + return size; +} + +/*! + * \brief Write RRSIG RDATA except signature. + * + * \note This can be also used for SIG(0) if proper parameters are supplied. + * + * \param rdata_len Length of RDATA. + * \param rdata Pointer to RDATA. + * \param key Key used for signing. + * \param covered_type Type of the covered RR. + * \param owner_labels Number of labels covered by the signature. + * \param sig_incepted Timestamp of signature inception. + * \param sig_expires Timestamp of signature expiration. + */ +static int rrsig_write_rdata(uint8_t *rdata, size_t rdata_len, + const dnssec_key_t *key, + uint16_t covered_type, uint8_t owner_labels, + uint32_t owner_ttl, uint32_t sig_incepted, + uint32_t sig_expires) +{ + if (!rdata || !key || sig_incepted >= sig_expires) { + return KNOT_EINVAL; + } + + uint8_t algorithm = dnssec_key_get_algorithm(key); + uint16_t keytag = dnssec_key_get_keytag(key); + const uint8_t *signer = dnssec_key_get_dname(key); + assert(signer); + + wire_ctx_t wire = wire_ctx_init(rdata, rdata_len); + + wire_ctx_write_u16(&wire, covered_type); // type covered + wire_ctx_write_u8(&wire, algorithm); // algorithm + wire_ctx_write_u8(&wire, owner_labels); // labels + wire_ctx_write_u32(&wire, owner_ttl); // original TTL + wire_ctx_write_u32(&wire, sig_expires); // signature expiration + wire_ctx_write_u32(&wire, sig_incepted); // signature inception + wire_ctx_write_u16(&wire, keytag); // key fingerprint + assert(wire_ctx_offset(&wire) == RRSIG_RDATA_SIGNER_OFFSET); + wire_ctx_write(&wire, signer, knot_dname_size(signer)); // signer + + return wire.error; +} + +/*- Computation of signatures ------------------------------------------------*/ + +/*! + * \brief Add RRSIG RDATA without signature to signing context. + * + * Requires signer name in RDATA in canonical form. + * + * \param ctx Signing context. + * \param rdata Pointer to RRSIG RDATA. + * + * \return Error code, KNOT_EOK if successful. + */ +static int sign_ctx_add_self(dnssec_sign_ctx_t *ctx, const uint8_t *rdata) +{ + assert(ctx); + assert(rdata); + + int result; + + // static header + + dnssec_binary_t header = { 0 }; + header.data = (uint8_t *)rdata; + header.size = RRSIG_RDATA_SIGNER_OFFSET; + + result = dnssec_sign_add(ctx, &header); + if (result != DNSSEC_EOK) { + return result; + } + + // signer name + + const uint8_t *rdata_signer = rdata + RRSIG_RDATA_SIGNER_OFFSET; + dnssec_binary_t signer = { 0 }; + signer.data = knot_dname_copy(rdata_signer, NULL); + signer.size = knot_dname_size(signer.data); + + result = dnssec_sign_add(ctx, &signer); + free(signer.data); + + return result; +} + +/*! + * \brief Add covered RRs to signing context. + * + * Requires all DNAMEs in canonical form and all RRs ordered canonically. + * + * \param ctx Signing context. + * \param covered Covered RRs. + * + * \return Error code, KNOT_EOK if successful. + */ +static int sign_ctx_add_records(dnssec_sign_ctx_t *ctx, const knot_rrset_t *covered) +{ + // huge block of rrsets can be optionally created + uint8_t *rrwf = malloc(KNOT_WIRE_MAX_PKTSIZE); + if (!rrwf) { + return KNOT_ENOMEM; + } + + int written = knot_rrset_to_wire(covered, rrwf, KNOT_WIRE_MAX_PKTSIZE, NULL); + if (written < 0) { + free(rrwf); + return written; + } + + dnssec_binary_t rrset_wire = { 0 }; + rrset_wire.size = written; + rrset_wire.data = rrwf; + int result = dnssec_sign_add(ctx, &rrset_wire); + free(rrwf); + + return result; +} + +int knot_sign_ctx_add_data(dnssec_sign_ctx_t *ctx, + const uint8_t *rrsig_rdata, + const knot_rrset_t *covered) +{ + if (!ctx || !rrsig_rdata || knot_rrset_empty(covered)) { + return KNOT_EINVAL; + } + + int result = sign_ctx_add_self(ctx, rrsig_rdata); + if (result != KNOT_EOK) { + return result; + } + + return sign_ctx_add_records(ctx, covered); +} + +/*! + * \brief Create RRSIG RDATA. + * + * \param[in] rrsigs RR set with RRSIGS. + * \param[in] ctx DNSSEC signing context. + * \param[in] covered RR covered by the signature. + * \param[in] key Key used for signing. + * \param[in] sig_incepted Timestamp of signature inception. + * \param[in] sig_expires Timestamp of signature expiration. + * + * \return Error code, KNOT_EOK if successful. + */ +static int rrsigs_create_rdata(knot_rrset_t *rrsigs, dnssec_sign_ctx_t *ctx, + const knot_rrset_t *covered, + const dnssec_key_t *key, + uint32_t sig_incepted, uint32_t sig_expires, + knot_mm_t *mm) +{ + assert(rrsigs); + assert(rrsigs->type == KNOT_RRTYPE_RRSIG); + assert(!knot_rrset_empty(covered)); + assert(key); + + size_t header_size = rrsig_rdata_header_size(key); + assert(header_size != 0); + + uint8_t owner_labels = knot_dname_labels(covered->owner, NULL); + if (knot_dname_is_wildcard(covered->owner)) { + owner_labels -= 1; + } + + uint8_t header[header_size]; + int res = rrsig_write_rdata(header, header_size, + key, covered->type, owner_labels, + covered->ttl, sig_incepted, sig_expires); + assert(res == KNOT_EOK); + + res = dnssec_sign_init(ctx); + if (res != KNOT_EOK) { + return res; + } + + res = knot_sign_ctx_add_data(ctx, header, covered); + if (res != KNOT_EOK) { + return res; + } + + dnssec_binary_t signature = { 0 }; + res = dnssec_sign_write(ctx, &signature); + if (res != DNSSEC_EOK) { + return res; + } + assert(signature.size > 0); + + size_t rrsig_size = header_size + signature.size; + uint8_t rrsig[rrsig_size]; + memcpy(rrsig, header, header_size); + memcpy(rrsig + header_size, signature.data, signature.size); + + dnssec_binary_free(&signature); + + return knot_rrset_add_rdata(rrsigs, rrsig, rrsig_size, mm); +} + +int knot_sign_rrset(knot_rrset_t *rrsigs, const knot_rrset_t *covered, + const dnssec_key_t *key, dnssec_sign_ctx_t *sign_ctx, + const kdnssec_ctx_t *dnssec_ctx, knot_mm_t *mm, knot_time_t *expires) +{ + if (knot_rrset_empty(covered) || !key || !sign_ctx || !dnssec_ctx || + rrsigs->type != KNOT_RRTYPE_RRSIG || + !knot_dname_is_equal(rrsigs->owner, covered->owner) + ) { + return KNOT_EINVAL; + } + + uint32_t sig_incept = dnssec_ctx->now - RRSIG_INCEPT_IN_PAST; + uint32_t sig_expire = dnssec_ctx->now + dnssec_ctx->policy->rrsig_lifetime; + + int ret = rrsigs_create_rdata(rrsigs, sign_ctx, covered, key, sig_incept, + sig_expire, mm); + if (ret == KNOT_EOK && expires != NULL) { + *expires = knot_time_min(*expires, sig_expire); + } + return ret; +} + +int knot_sign_rrset2(knot_rrset_t *rrsigs, const knot_rrset_t *rrset, + zone_sign_ctx_t *sign_ctx, knot_mm_t *mm) +{ + if (rrsigs == NULL || rrset == NULL || sign_ctx == NULL) { + return KNOT_EINVAL; + } + + for (size_t i = 0; i < sign_ctx->count; i++) { + zone_key_t *key = &sign_ctx->keys[i]; + + if (!knot_zone_sign_use_key(key, rrset)) { + continue; + } + + int ret = knot_sign_rrset(rrsigs, rrset, key->key, sign_ctx->sign_ctxs[i], + sign_ctx->dnssec_ctx, mm, NULL); + if (ret != KNOT_EOK) { + return ret; + } + } + + return KNOT_EOK; +} + +int knot_synth_rrsig(uint16_t type, const knot_rdataset_t *rrsig_rrs, + knot_rdataset_t *out_sig, knot_mm_t *mm) +{ + if (rrsig_rrs == NULL) { + return KNOT_ENOENT; + } + + if (out_sig == NULL || out_sig->count > 0) { + return KNOT_EINVAL; + } + + knot_rdata_t *rr_to_copy = rrsig_rrs->rdata; + for (int i = 0; i < rrsig_rrs->count; ++i) { + if (type == knot_rrsig_type_covered(rr_to_copy)) { + int ret = knot_rdataset_add(out_sig, rr_to_copy, mm); + if (ret != KNOT_EOK) { + knot_rdataset_clear(out_sig, mm); + return ret; + } + } + rr_to_copy = knot_rdataset_next(rr_to_copy); + } + + return out_sig->count > 0 ? KNOT_EOK : KNOT_ENOENT; +} + +/*- Verification of signatures -----------------------------------------------*/ + +/*! + * \brief Check if the signature is expired. + * + * \param rrsig RRSIG rdata. + * \param policy DNSSEC policy. + * + * \return Signature is expired or should be replaced soon. + */ +static bool is_expired_signature(const knot_rdata_t *rrsig, uint32_t now, + uint32_t refresh_before) +{ + assert(rrsig); + + uint32_t expire_at = knot_rrsig_sig_expiration(rrsig); + uint32_t expire_in = expire_at > now ? expire_at - now : 0; + + return expire_in <= refresh_before; +} + +int knot_check_signature(const knot_rrset_t *covered, + const knot_rrset_t *rrsigs, size_t pos, + const dnssec_key_t *key, + dnssec_sign_ctx_t *sign_ctx, + const kdnssec_ctx_t *dnssec_ctx) +{ + if (knot_rrset_empty(covered) || knot_rrset_empty(rrsigs) || !key || + !sign_ctx || !dnssec_ctx) { + return KNOT_EINVAL; + } + + knot_rdata_t *rrsig = knot_rdataset_at(&rrsigs->rrs, pos); + assert(rrsig); + + if (is_expired_signature(rrsig, dnssec_ctx->now, + dnssec_ctx->policy->rrsig_refresh_before)) { + return DNSSEC_INVALID_SIGNATURE; + } + + // identify fields in the signature being validated + + dnssec_binary_t signature = { + .size = knot_rrsig_signature_len(rrsig), + .data = (uint8_t *)knot_rrsig_signature(rrsig) + }; + if (signature.data == NULL) { + return KNOT_EINVAL; + } + + // perform the validation + + int result = dnssec_sign_init(sign_ctx); + if (result != KNOT_EOK) { + return result; + } + + result = knot_sign_ctx_add_data(sign_ctx, rrsig->data, covered); + if (result != KNOT_EOK) { + return result; + } + + return dnssec_sign_verify(sign_ctx, &signature); +} diff --git a/src/knot/dnssec/rrset-sign.h b/src/knot/dnssec/rrset-sign.h new file mode 100644 index 0000000..b1dd74c --- /dev/null +++ b/src/knot/dnssec/rrset-sign.h @@ -0,0 +1,112 @@ +/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "libdnssec/key.h" +#include "libdnssec/sign.h" +#include "knot/dnssec/context.h" +#include "knot/dnssec/zone-keys.h" +#include "libknot/rrset.h" + +/*! + * \brief Create RRSIG RR for given RR set. + * + * \param rrsigs RR set with RRSIGs into which the result will be added. + * \param covered RR set to create a new signature for. + * \param key Signing key. + * \param sign_ctx Signing context. + * \param dnssec_ctx DNSSEC context. + * \param mm Memory context. + * \param expires Out: When will the new RRSIG expire. + * + * \return Error code, KNOT_EOK if successful. + */ +int knot_sign_rrset(knot_rrset_t *rrsigs, + const knot_rrset_t *covered, + const dnssec_key_t *key, + dnssec_sign_ctx_t *sign_ctx, + const kdnssec_ctx_t *dnssec_ctx, + knot_mm_t *mm, + knot_time_t *expires); + +/*! + * \brief Create RRSIG RR for given RR set, choose which key to use. + * + * \param rrsigs RR set with RRSIGs into which the result will be added. + * \param rrset RR set to create a new signature for. + * \param sign_ctx Zone signing context. + * \param mm Memory context. + * + * \return Error code, KNOT_EOK if successful. + */ +int knot_sign_rrset2(knot_rrset_t *rrsigs, + const knot_rrset_t *rrset, + zone_sign_ctx_t *sign_ctx, + knot_mm_t *mm); + +/*! + * \brief Add all data covered by signature into signing context. + * + * RFC 4034: The signature covers RRSIG RDATA field (excluding the signature) + * and all matching RR records, which are ordered canonically. + * + * Requires all DNAMEs in canonical form and all RRs ordered canonically. + * + * \param ctx Signing context. + * \param rrsig_rdata RRSIG RDATA with populated fields except signature. + * \param covered Covered RRs. + * + * \return Error code, KNOT_EOK if successful. + */ +int knot_sign_ctx_add_data(dnssec_sign_ctx_t *ctx, + const uint8_t *rrsig_rdata, + const knot_rrset_t *covered); + +/*! + * \brief Creates new RRS using \a rrsig_rrs as a source. Only those RRs that + * cover given \a type are copied into \a out_sig + * + * \param type Covered type. + * \param rrsig_rrs Source RRS. + * \param out_sig Output RRS. + * \param mm Memory context. + * + * \retval KNOT_EOK if some RRSIG was found. + * \retval KNOT_EINVAL if no RRSIGs were found. + * \retval Error code other than EINVAL on error. + */ +int knot_synth_rrsig(uint16_t type, const knot_rdataset_t *rrsig_rrs, + knot_rdataset_t *out_sig, knot_mm_t *mm); + +/*! + * \brief Check if RRSIG signature is valid. + * + * \param covered RRs covered by the signature. + * \param rrsigs RR set with RRSIGs. + * \param pos Number of RRSIG RR in 'rrsigs' to be validated. + * \param key Signing key. + * \param sign_ctx Signing context. + * \param dnssec_ctx DNSSEC context. + * + * \return Error code, KNOT_EOK if successful and the signature is valid. + * \retval KNOT_DNSSEC_EINVALID_SIGNATURE The signature is invalid. + */ +int knot_check_signature(const knot_rrset_t *covered, + const knot_rrset_t *rrsigs, size_t pos, + const dnssec_key_t *key, + dnssec_sign_ctx_t *sign_ctx, + const kdnssec_ctx_t *dnssec_ctx); diff --git a/src/knot/dnssec/zone-events.c b/src/knot/dnssec/zone-events.c new file mode 100644 index 0000000..b919ee9 --- /dev/null +++ b/src/knot/dnssec/zone-events.c @@ -0,0 +1,312 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> + +#include "libdnssec/error.h" +#include "libdnssec/random.h" +#include "libknot/libknot.h" +#include "knot/conf/conf.h" +#include "knot/common/log.h" +#include "knot/dnssec/key-events.h" +#include "knot/dnssec/policy.h" +#include "knot/dnssec/zone-events.h" +#include "knot/dnssec/zone-keys.h" +#include "knot/dnssec/zone-nsec.h" +#include "knot/dnssec/zone-sign.h" + +static int sign_init(zone_contents_t *zone, zone_sign_flags_t flags, + kdnssec_ctx_t *ctx, zone_sign_reschedule_t *reschedule) +{ + assert(zone); + assert(ctx); + + const knot_dname_t *zone_name = zone->apex->owner; + + int r = kdnssec_ctx_init(conf(), ctx, zone_name, NULL); + if (r != KNOT_EOK) { + return r; + } + + // perform nsec3resalt if pending + + if (reschedule->allow_nsec3resalt) { + r = knot_dnssec_nsec3resalt(ctx, &reschedule->allow_nsec3resalt, &reschedule->next_nsec3resalt); + if (r != KNOT_EOK) { + return r; + } + } + + r = zone_contents_adjust_full(zone); + if (r != KNOT_EOK) { + return r; + } + + // update policy based on the zone content + + update_policy_from_zone(ctx->policy, zone); + + // perform key rollover if needed + + if (reschedule->allow_rollover) { + r = knot_dnssec_key_rollover(ctx, reschedule); + } + if (r != KNOT_EOK) { + return r; + } + + // RRSIG handling + + ctx->rrsig_drop_existing = flags & ZONE_SIGN_DROP_SIGNATURES; + + return KNOT_EOK; +} + +static knot_time_t schedule_next(kdnssec_ctx_t *kctx, const zone_keyset_t *keyset, + knot_time_t zone_expire) +{ + knot_time_t zone_refresh = knot_time_add(zone_expire, -(knot_timediff_t)kctx->policy->rrsig_refresh_before); + + knot_time_t dnskey_update = knot_get_next_zone_key_event(keyset); + knot_time_t next = knot_time_min(zone_refresh, dnskey_update); + + return next; +} + +static int generate_salt(dnssec_binary_t *salt, uint16_t length) +{ + assert(salt); + dnssec_binary_t new_salt = { 0 }; + + if (length > 0) { + int r = dnssec_binary_alloc(&new_salt, length); + if (r != KNOT_EOK) { + return knot_error_from_libdnssec(r); + } + + r = dnssec_random_binary(&new_salt); + if (r != KNOT_EOK) { + dnssec_binary_free(&new_salt); + return knot_error_from_libdnssec(r); + } + } + + dnssec_binary_free(salt); + *salt = new_salt; + + return KNOT_EOK; +} + +// TODO preserve the resalt timeout in timers-db instead of kasp_db + +int knot_dnssec_nsec3resalt(kdnssec_ctx_t *ctx, bool *salt_changed, knot_time_t *when_resalt) +{ + int ret = KNOT_EOK; + + if (!ctx->policy->nsec3_enabled || ctx->policy->nsec3_salt_length == 0) { + return KNOT_EOK; + } + + if (ctx->policy->manual) { + return KNOT_EOK; + } + + if (ctx->zone->nsec3_salt.size != ctx->policy->nsec3_salt_length) { + *when_resalt = ctx->now; + } else if (knot_time_cmp(ctx->now, ctx->zone->nsec3_salt_created) < 0) { + return KNOT_EINVAL; + } else { + *when_resalt = ctx->zone->nsec3_salt_created + ctx->policy->nsec3_salt_lifetime; + } + + if (knot_time_cmp(*when_resalt, ctx->now) <= 0) { + ret = generate_salt(&ctx->zone->nsec3_salt, ctx->policy->nsec3_salt_length); + if (ret == KNOT_EOK) { + ctx->zone->nsec3_salt_created = ctx->now; + ret = kdnssec_ctx_commit(ctx); + *salt_changed = true; + } + // continue to planning next resalt even if NOK + *when_resalt = knot_time_add(ctx->now, ctx->policy->nsec3_salt_lifetime); + } + + return ret; +} + +int knot_dnssec_zone_sign(zone_update_t *update, + zone_sign_flags_t flags, + zone_sign_reschedule_t *reschedule) +{ + if (!update || !reschedule) { + return KNOT_EINVAL; + } + + int result = KNOT_ERROR; + const knot_dname_t *zone_name = update->new_cont->apex->owner; + kdnssec_ctx_t ctx = { 0 }; + zone_keyset_t keyset = { 0 }; + + // signing pipeline + + result = sign_init(update->new_cont, flags, &ctx, reschedule); + if (result != KNOT_EOK) { + log_zone_error(zone_name, "DNSSEC, failed to initialize (%s)", + knot_strerror(result)); + goto done; + } + + result = load_zone_keys(&ctx, &keyset, true); + if (result != KNOT_EOK) { + log_zone_error(zone_name, "DNSSEC, failed to load keys (%s)", + knot_strerror(result)); + goto done; + } + + log_zone_info(zone_name, "DNSSEC, signing started"); + + result = knot_zone_sign_update_dnskeys(update, &keyset, &ctx); + if (result != KNOT_EOK) { + log_zone_error(zone_name, "DNSSEC, failed to update DNSKEY records (%s)", + knot_strerror(result)); + goto done; + } + + result = knot_zone_create_nsec_chain(update, &keyset, &ctx, false); + if (result != KNOT_EOK) { + log_zone_error(zone_name, "DNSSEC, failed to create NSEC%s chain (%s)", + ctx.policy->nsec3_enabled ? "3" : "", + knot_strerror(result)); + goto done; + } + + knot_time_t zone_expire = 0; + result = knot_zone_sign(update, &keyset, &ctx, &zone_expire); + if (result != KNOT_EOK) { + log_zone_error(zone_name, "DNSSEC, failed to sign zone content (%s)", + knot_strerror(result)); + goto done; + } + + // SOA finishing + + if (zone_update_no_change(update) && + !knot_zone_sign_soa_expired(update->new_cont, &keyset, &ctx)) { + log_zone_info(zone_name, "DNSSEC, zone is up-to-date"); + goto done; + } + + if (!(flags & ZONE_SIGN_KEEP_SERIAL) && zone_update_to(update) == NULL) { + result = zone_update_increment_soa(update, conf()); + if (result == KNOT_EOK) { + result = knot_zone_sign_soa(update, &keyset, &ctx); + } + if (result != KNOT_EOK) { + log_zone_error(zone_name, "DNSSEC, failed to update SOA record (%s)", + knot_strerror(result)); + goto done; + } + } + + log_zone_info(zone_name, "DNSSEC, successfully signed"); + +done: + if (result == KNOT_EOK) { + reschedule->next_sign = schedule_next(&ctx, &keyset, zone_expire); + } + + free_zone_keys(&keyset); + kdnssec_ctx_deinit(&ctx); + + return result; +} + +int knot_dnssec_sign_update(zone_update_t *update, zone_sign_reschedule_t *reschedule) +{ + if (update == NULL || reschedule == NULL) { + return KNOT_EINVAL; + } + + int result = KNOT_ERROR; + const knot_dname_t *zone_name = update->new_cont->apex->owner; + kdnssec_ctx_t ctx = { 0 }; + zone_keyset_t keyset = { 0 }; + + // signing pipeline + + result = sign_init(update->new_cont, 0, &ctx, reschedule); + if (result != KNOT_EOK) { + log_zone_error(zone_name, "DNSSEC, failed to initialize (%s)", + knot_strerror(result)); + goto done; + } + + result = load_zone_keys(&ctx, &keyset, false); + if (result != KNOT_EOK) { + log_zone_error(zone_name, "DNSSEC, failed to load keys (%s)", + knot_strerror(result)); + goto done; + } + + knot_time_t expire_at = 0; + result = knot_zone_sign_update(update, &keyset, &ctx, &expire_at); + if (result != KNOT_EOK) { + log_zone_error(zone_name, "DNSSEC, failed to sign changeset (%s)", + knot_strerror(result)); + goto done; + } + + result = knot_zone_fix_nsec_chain(update, &keyset, &ctx, true); + if (result != KNOT_EOK) { + log_zone_error(zone_name, "DNSSEC, failed to fix NSEC%s chain (%s)", + ctx.policy->nsec3_enabled ? "3" : "", + knot_strerror(result)); + goto done; + } + + bool soa_changed = (knot_soa_serial(node_rdataset(update->zone->contents->apex, KNOT_RRTYPE_SOA)->rdata) != + knot_soa_serial(node_rdataset(update->new_cont->apex, KNOT_RRTYPE_SOA)->rdata)); + + if (zone_update_no_change(update) && !soa_changed && + !knot_zone_sign_soa_expired(update->new_cont, &keyset, &ctx)) { + log_zone_info(zone_name, "DNSSEC, zone is up-to-date"); + goto done; + } + + if (!soa_changed) { + // incrementing SOA just of it has not been modified by the update + result = zone_update_increment_soa(update, conf()); + } + if (result == KNOT_EOK) { + result = knot_zone_sign_soa(update, &keyset, &ctx); + } + if (result != KNOT_EOK) { + log_zone_error(zone_name, "DNSSEC, failed to update SOA record (%s)", + knot_strerror(result)); + goto done; + } + + log_zone_info(zone_name, "DNSSEC, successfully signed"); + +done: + if (result == KNOT_EOK) { + reschedule->next_sign = schedule_next(&ctx, &keyset, expire_at); + } + + free_zone_keys(&keyset); + kdnssec_ctx_deinit(&ctx); + + return result; +} diff --git a/src/knot/dnssec/zone-events.h b/src/knot/dnssec/zone-events.h new file mode 100644 index 0000000..3bfd20a --- /dev/null +++ b/src/knot/dnssec/zone-events.h @@ -0,0 +1,95 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <time.h> + +#include "knot/zone/zone.h" +#include "knot/updates/changesets.h" +#include "knot/updates/zone-update.h" +#include "knot/dnssec/context.h" + +enum zone_sign_flags { + ZONE_SIGN_NONE = 0, + ZONE_SIGN_DROP_SIGNATURES = (1 << 0), + ZONE_SIGN_KEEP_SERIAL = (1 << 1), +}; + +typedef enum zone_sign_flags zone_sign_flags_t; + +typedef struct { + knot_time_t next_sign; + knot_time_t next_rollover; + knot_time_t next_nsec3resalt; + bool keys_changed; + bool plan_ds_query; + bool allow_rollover; // this one is set by the caller + bool allow_nsec3resalt; // this one is set by the caller and modified by the salter +} zone_sign_reschedule_t; + +/*! + * \brief Generate/rollover keys in keystore as needed. + * + * \param kctx Pointers to the keytore, policy, etc. + * \param zone_name Zone name. + * + * \return Error code, KNOT_EOK if successful. + */ +int knot_dnssec_sign_process_events(const kdnssec_ctx_t *kctx, + const knot_dname_t *zone_name); + +/*! + * \brief DNSSEC re-sign zone, store new records into changeset. Valid signatures + * and NSEC(3) records will not be changed. + * + * \param update Zone Update structure with current zone contents to be updated by signing. + * \param flags Zone signing flags. + * \param reschedule Signature refresh time of the oldest signature in zone. + * + * \return Error code, KNOT_EOK if successful. + */ +int knot_dnssec_zone_sign(zone_update_t *update, + zone_sign_flags_t flags, + zone_sign_reschedule_t *reschedule); + +/*! + * \brief Sign changeset (inside incremental Zone Update) created by DDNS or so... + * + * \param update Zone Update structure with current zone contents, changes to be signed and to be updated with signatures. + * \param reschedule Signature refresh time of the new signatures. + * + * \return Error code, KNOT_EOK if successful. + */ +int knot_dnssec_sign_update(zone_update_t *update, zone_sign_reschedule_t *reschedule); + +/*! + * \brief Create new NCES3 salt if the old one is too old, and plan next resalt. + * + * For given zone, check NSEC3 salt in KASP db and decide if it shall be recreated + * and tell the user the next time it shall be called. + * + * This function is optimized to be called from NSEC3RESALT_EVENT, + * but also during zone load so that the zone gets loaded already with + * proper DNSSEC chain. + * + * \param ctx zone signing context + * \param salt_changed output if KNOT_EOK: was the salt changed ? (if so, please re-sign) + * \param when_resalt output: tmestamp when next resalt takes place + * + * \return KNOT_E* + */ +int knot_dnssec_nsec3resalt(kdnssec_ctx_t *ctx, bool *salt_changed, knot_time_t *when_resalt); diff --git a/src/knot/dnssec/zone-keys.c b/src/knot/dnssec/zone-keys.c new file mode 100644 index 0000000..94c0732 --- /dev/null +++ b/src/knot/dnssec/zone-keys.c @@ -0,0 +1,529 @@ +/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> +#include <limits.h> +#include <stdio.h> + +#include "libdnssec/error.h" +#include "knot/common/log.h" +#include "knot/dnssec/zone-keys.h" +#include "libknot/libknot.h" + +#define MAX_KEY_INFO 128 + +dynarray_define(keyptr, zone_key_t *, DYNARRAY_VISIBILITY_PUBLIC) + +void normalize_generate_flags(kdnssec_generate_flags_t *flags) +{ + if (!(*flags & DNSKEY_GENERATE_KSK) && !(*flags & DNSKEY_GENERATE_ZSK)) { + *flags |= DNSKEY_GENERATE_ZSK; + } + if (!(*flags & DNSKEY_GENERATE_SEP_SPEC)) { + if ((*flags & DNSKEY_GENERATE_KSK)) { + *flags |= DNSKEY_GENERATE_SEP_ON; + } else { + *flags &= ~DNSKEY_GENERATE_SEP_ON; + } + } +} + +int kdnssec_generate_key(kdnssec_ctx_t *ctx, kdnssec_generate_flags_t flags, + knot_kasp_key_t **key_ptr) +{ + assert(ctx); + assert(ctx->zone); + assert(ctx->keystore); + assert(ctx->policy); + + normalize_generate_flags(&flags); + + dnssec_key_algorithm_t algorithm = ctx->policy->algorithm; + unsigned size = (flags & DNSKEY_GENERATE_KSK) ? ctx->policy->ksk_size : ctx->policy->zsk_size; + + // generate key in the keystore + + char *id = NULL; + int r = dnssec_keystore_generate_key(ctx->keystore, algorithm, size, &id); + if (r != KNOT_EOK) { + return r; + } + + // create KASP key + + dnssec_key_t *dnskey = NULL; + r = dnssec_key_new(&dnskey); + if (r != KNOT_EOK) { + free(id); + return r; + } + + r = dnssec_key_set_dname(dnskey, ctx->zone->dname); + if (r != KNOT_EOK) { + dnssec_key_free(dnskey); + free(id); + return r; + } + + dnssec_key_set_flags(dnskey, dnskey_flags(flags & DNSKEY_GENERATE_SEP_ON)); + dnssec_key_set_algorithm(dnskey, algorithm); + + r = dnssec_key_import_keystore(dnskey, ctx->keystore, id); + if (r != KNOT_EOK) { + dnssec_key_free(dnskey); + free(id); + return r; + } + + knot_kasp_key_t *key = calloc(1, sizeof(*key)); + if (!key) { + dnssec_key_free(dnskey); + free(id); + return KNOT_ENOMEM; + } + + key->id = id; + key->key = dnskey; + key->is_ksk = (flags & DNSKEY_GENERATE_KSK); + key->is_zsk = (flags & DNSKEY_GENERATE_ZSK); + key->timing.created = ctx->now; + + r = kasp_zone_append(ctx->zone, key); + free(key); + if (r != KNOT_EOK) { + dnssec_key_free(dnskey); + free(id); + return r; + } + + if (key_ptr) { + *key_ptr = &ctx->zone->keys[ctx->zone->num_keys - 1]; + } + + return KNOT_EOK; +} + +int kdnssec_share_key(kdnssec_ctx_t *ctx, const knot_dname_t *from_zone, const char *key_id) +{ + knot_dname_t *to_zone = knot_dname_copy(ctx->zone->dname, NULL); + if (to_zone == NULL) { + return KNOT_ENOMEM; + } + + int ret = kdnssec_ctx_commit(ctx); + if (ret != KNOT_EOK) { + free(to_zone); + return ret; + } + + ret = kasp_db_share_key(*ctx->kasp_db, from_zone, ctx->zone->dname, key_id); + if (ret != KNOT_EOK) { + free(to_zone); + return ret; + } + + kasp_zone_clear(ctx->zone); + ret = kasp_zone_load(ctx->zone, to_zone, *ctx->kasp_db); + free(to_zone); + return ret; +} + +int kdnssec_delete_key(kdnssec_ctx_t *ctx, knot_kasp_key_t *key_ptr) +{ + assert(ctx); + assert(ctx->zone); + assert(ctx->keystore); + assert(ctx->policy); + + ssize_t key_index = key_ptr - ctx->zone->keys; + + if (key_index < 0 || key_index >= ctx->zone->num_keys) { + return KNOT_EINVAL; + } + + bool key_still_used_in_keystore = false; + int ret = kasp_db_delete_key(*ctx->kasp_db, ctx->zone->dname, key_ptr->id, &key_still_used_in_keystore); + if (ret != KNOT_EOK) { + return ret; + } + + if (!key_still_used_in_keystore && !key_ptr->is_pub_only) { + ret = dnssec_keystore_remove_key(ctx->keystore, key_ptr->id); + if (ret != KNOT_EOK) { + return ret; + } + } + + dnssec_key_free(key_ptr->key); + free(key_ptr->id); + memmove(key_ptr, key_ptr + 1, (ctx->zone->num_keys - key_index - 1) * sizeof(*key_ptr)); + ctx->zone->num_keys--; + return KNOT_EOK; +} + +/*! + * \brief Get key feature flags from key parameters. + */ +static void set_key(knot_kasp_key_t *kasp_key, knot_time_t now, zone_key_t *zone_key) +{ + assert(kasp_key); + assert(zone_key); + + knot_kasp_key_timing_t *timing = &kasp_key->timing; + + zone_key->id = kasp_key->id; + zone_key->key = kasp_key->key; + + // next event computation + + knot_time_t next = 0; + knot_time_t timestamps[] = { + timing->pre_active, + timing->publish, + timing->ready, + timing->active, + timing->retire_active, + timing->retire, + timing->post_active, + timing->remove, + }; + + for (int i = 0; i < sizeof(timestamps) / sizeof(knot_time_t); i++) { + knot_time_t ts = timestamps[i]; + if (knot_time_cmp(now, ts) < 0 && knot_time_cmp(ts, next) < 0) { + next = ts; + } + } + + zone_key->next_event = next; + + zone_key->is_ksk = kasp_key->is_ksk; + zone_key->is_zsk = kasp_key->is_zsk; + + zone_key->is_public = (knot_time_cmp(timing->publish, now) <= 0 && + knot_time_cmp(timing->post_active, now) > 0 && + knot_time_cmp(timing->remove, now) > 0); + + zone_key->is_active = (((zone_key->is_zsk && knot_time_cmp(timing->pre_active, now) <= 0) || + (knot_time_cmp(timing->pre_active, now) <= 0 && knot_time_cmp(timing->publish, now) <= 0) || + knot_time_cmp(timing->ready, now) <= 0 || + knot_time_cmp(timing->active, now) <= 0) && + knot_time_cmp(timing->retire, now) > 0 && + (zone_key->is_zsk || knot_time_cmp(timing->post_active, now) > 0) && + knot_time_cmp(timing->remove, now) > 0); + + zone_key->cds_priority = (knot_time_cmp(timing->ready, now) <= 0 ? ( + (knot_time_cmp(timing->active, now) <= 0) ? ( + (knot_time_cmp(timing->retire_active, now) <= 0 || + knot_time_cmp(timing->retire, now) <= 0) ? 0 : 1) : 2) : 0); +} + +/*! + * \brief Check if algorithm is allowed with NSEC3. + */ +static bool is_nsec3_allowed(uint8_t algorithm) +{ + switch (algorithm) { + case DNSSEC_KEY_ALGORITHM_RSA_SHA1: + return false; + default: + return true; + } +} + +static void ksk2csk(kdnssec_ctx_t *ctx, zone_keyset_t *keyset, uint8_t alg) +{ + for (size_t j = 0; j < keyset->count; j++) { + zone_key_t *key = &keyset->keys[j]; + if (dnssec_key_get_algorithm(key->key) == alg) { + assert(key->is_ksk); + key->is_zsk = true; + } + } + + for (size_t i = 0; i < ctx->zone->num_keys; i++) { + knot_kasp_key_t *key = &ctx->zone->keys[i]; + if (dnssec_key_get_algorithm(key->key) == alg) { + assert(key->is_ksk); + key->is_zsk = true; + } + } + + log_zone_info(ctx->zone->dname, "DNSSEC, Single-Type Signing " + "Scheme enabled"); +} + +static int walk_algorithms(kdnssec_ctx_t *ctx, zone_keyset_t *keyset) +{ + uint8_t alg_usage[256] = { 0 }; + bool keys_changed = false, have_active_alg = false; + + for (size_t i = 0; i < keyset->count; i++) { + zone_key_t *key = &keyset->keys[i]; + uint8_t alg = dnssec_key_get_algorithm(key->key); + + if (ctx->policy->nsec3_enabled && !is_nsec3_allowed(alg)) { + log_zone_warning(ctx->zone->dname, "DNSSEC, key %d " + "cannot be used with NSEC3", + dnssec_key_get_keytag(key->key)); + key->is_public = false; + key->is_active = false; + key->cds_priority = 0; + continue; + } + + if (key->is_ksk && key->is_public) { alg_usage[alg] |= 1; } + if (key->is_zsk && key->is_public) { alg_usage[alg] |= 2; } + if (key->is_ksk && key->is_active) { alg_usage[alg] |= 4; } + if (key->is_zsk && key->is_active) { alg_usage[alg] |= 8; } + } + + for (size_t i = 0; i < sizeof(alg_usage); i++) { + if (!(alg_usage[i] & 3)) { + continue; // no public keys, ignore + } + switch (alg_usage[i]) { + case 5: // because migrating from older version OR from manual setup + ksk2csk(ctx, keyset, i); + alg_usage[i] |= 10; + keys_changed = true; + // FALLTHROUGH + case 15: // all keys ready for signing + have_active_alg = true; + break; + default: + return KNOT_DNSSEC_EMISSINGKEYTYPE; + } + } + + if (!have_active_alg) { + return KNOT_DNSSEC_ENOKEY; + } + + if (keys_changed) { + return kdnssec_ctx_commit(ctx); + } + + return KNOT_EOK; +} + +/*! + * \brief Load private keys for active keys. + */ +static int load_private_keys(dnssec_keystore_t *keystore, zone_keyset_t *keyset) +{ + assert(keystore); + assert(keyset); + + for (size_t i = 0; i < keyset->count; i++) { + if (!keyset->keys[i].is_active) { + continue; + } + + zone_key_t *key = &keyset->keys[i]; + int r = dnssec_key_import_keystore(key->key, keystore, key->id); + if (r != DNSSEC_EOK && r != DNSSEC_KEY_ALREADY_PRESENT) { + return r; + } + } + + return DNSSEC_EOK; +} + +/*! + * \brief Log information about zone keys. + */ +static void log_key_info(const zone_key_t *key, char *out, size_t out_len) +{ + assert(key); + assert(out); + + uint8_t alg_code = dnssec_key_get_algorithm(key->key); + const knot_lookup_t *alg = knot_lookup_by_id(knot_dnssec_alg_names, alg_code); + + char alg_code_str[8] = ""; + if (alg == NULL) { + (void)snprintf(alg_code_str, sizeof(alg_code_str), "%d", alg_code); + } + + (void)snprintf(out, out_len, "DNSSEC, key, tag %5d, algorithm %s%s%s%s%s", + dnssec_key_get_keytag(key->key), + (alg != NULL ? alg->name : alg_code_str), + (key->is_ksk ? (key->is_zsk ? ", CSK" : ", KSK") : ""), + (key->is_public ? ", public" : ""), + (key->cds_priority > 1 ? ", ready" : ""), + (key->is_active ? ", active" : "")); +} + +int log_key_sort(const void *a, const void *b) +{ + const char *alg_a = strstr(a, "alg"); + const char *alg_b = strstr(b, "alg"); + assert(alg_a != NULL && alg_b != NULL); + + return strcmp(alg_a, alg_b); +} + +/*! + * \brief Load zone keys and init cryptographic context. + */ +int load_zone_keys(kdnssec_ctx_t *ctx, zone_keyset_t *keyset_ptr, bool verbose) +{ + if (!ctx || !keyset_ptr) { + return KNOT_EINVAL; + } + + zone_keyset_t keyset = { 0 }; + + if (ctx->zone->num_keys < 1) { + log_zone_error(ctx->zone->dname, "DNSSEC, no keys are available"); + return KNOT_DNSSEC_ENOKEY; + } + + keyset.count = ctx->zone->num_keys; + keyset.keys = calloc(keyset.count, sizeof(zone_key_t)); + if (!keyset.keys) { + free_zone_keys(&keyset); + return KNOT_ENOMEM; + } + + char key_info[ctx->zone->num_keys][MAX_KEY_INFO]; + for (size_t i = 0; i < ctx->zone->num_keys; i++) { + knot_kasp_key_t *kasp_key = &ctx->zone->keys[i]; + set_key(kasp_key, ctx->now, &keyset.keys[i]); + if (verbose) { + log_key_info(&keyset.keys[i], key_info[i], MAX_KEY_INFO); + } + } + + // Sort the keys by algorithm name. + if (verbose) { + qsort(key_info, ctx->zone->num_keys, MAX_KEY_INFO, log_key_sort); + for (size_t i = 0; i < ctx->zone->num_keys; i++) { + log_zone_info(ctx->zone->dname, "%s", key_info[i]); + } + } + + int ret = walk_algorithms(ctx, &keyset); + if (ret != KNOT_EOK) { + log_zone_error(ctx->zone->dname, "DNSSEC, keys validation failed (%s)", + knot_strerror(ret)); + free_zone_keys(&keyset); + return ret; + } + + ret = load_private_keys(ctx->keystore, &keyset); + ret = knot_error_from_libdnssec(ret); + if (ret != KNOT_EOK) { + log_zone_error(ctx->zone->dname, "DNSSEC, failed to load private " + "keys (%s)", knot_strerror(ret)); + free_zone_keys(&keyset); + return ret; + } + + *keyset_ptr = keyset; + + return KNOT_EOK; +} + +/*! + * \brief Free structure with zone keys and associated DNSSEC contexts. + */ +void free_zone_keys(zone_keyset_t *keyset) +{ + if (!keyset) { + return; + } + + for (size_t i = 0; i < keyset->count; i++) { + dnssec_binary_free(&keyset->keys[i].precomputed_ds); + } + + free(keyset->keys); + + memset(keyset, '\0', sizeof(*keyset)); +} + +/*! + * \brief Get timestamp of next key event. + */ +knot_time_t knot_get_next_zone_key_event(const zone_keyset_t *keyset) +{ + assert(keyset); + + knot_time_t result = 0; + + for (size_t i = 0; i < keyset->count; i++) { + zone_key_t *key = &keyset->keys[i]; + if (knot_time_cmp(key->next_event, result) < 0) { + result = key->next_event; + } + } + + return result; +} + +/*! + * \brief Compute DS record rdata from key + cache it. + */ +int zone_key_calculate_ds(zone_key_t *for_key, dnssec_binary_t *out_donotfree) +{ + assert(for_key); + assert(out_donotfree); + + int ret = KNOT_EOK; + + if (for_key->precomputed_ds.data == NULL) { + dnssec_key_digest_t digesttype = DNSSEC_KEY_DIGEST_SHA256; // TODO ! + ret = dnssec_key_create_ds(for_key->key, digesttype, &for_key->precomputed_ds); + ret = knot_error_from_libdnssec(ret); + } + + *out_donotfree = for_key->precomputed_ds; + return ret; +} + +zone_sign_ctx_t *zone_sign_ctx(const zone_keyset_t *keyset, const kdnssec_ctx_t *dnssec_ctx) +{ + zone_sign_ctx_t *ctx = calloc(1, sizeof(*ctx) + keyset->count * sizeof(*ctx->sign_ctxs)); + if (ctx == NULL) { + return NULL; + } + + ctx->sign_ctxs = (dnssec_sign_ctx_t **)(ctx + 1); + ctx->count = keyset->count; + ctx->keys = keyset->keys; + ctx->dnssec_ctx = dnssec_ctx; + for (size_t i = 0; i < ctx->count; i++) { + int ret = dnssec_sign_new(&ctx->sign_ctxs[i], ctx->keys[i].key); + if (ret != DNSSEC_EOK) { + zone_sign_ctx_free(ctx); + return NULL; + } + } + + return ctx; +} + +void zone_sign_ctx_free(zone_sign_ctx_t *ctx) +{ + if (ctx != NULL) { + for (size_t i = 0; i < ctx->count; i++) { + dnssec_sign_free(ctx->sign_ctxs[i]); + } + free(ctx); + } +} diff --git a/src/knot/dnssec/zone-keys.h b/src/knot/dnssec/zone-keys.h new file mode 100644 index 0000000..a8ee990 --- /dev/null +++ b/src/knot/dnssec/zone-keys.h @@ -0,0 +1,176 @@ +/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "contrib/dynarray.h" +#include "libdnssec/keystore.h" +#include "libdnssec/sign.h" +#include "knot/dnssec/kasp/kasp_zone.h" +#include "knot/dnssec/kasp/policy.h" +#include "knot/dnssec/context.h" + +/*! + * \brief Zone key context used during signing. + */ +typedef struct { + const char *id; + dnssec_key_t *key; + + dnssec_binary_t precomputed_ds; + + knot_time_t next_event; + + bool is_ksk; + bool is_zsk; + bool is_active; + bool is_public; + int cds_priority; +} zone_key_t; + +dynarray_declare(keyptr, zone_key_t *, DYNARRAY_VISIBILITY_PUBLIC, 1) + +typedef struct { + size_t count; + zone_key_t *keys; +} zone_keyset_t; + +/*! + * \brief Signing context used for single signing thread. + */ +typedef struct { + size_t count; // number of keys in keyset + zone_key_t *keys; // keys in keyset + dnssec_sign_ctx_t **sign_ctxs; // signing buffers for keys in keyset + const kdnssec_ctx_t *dnssec_ctx; // dnssec context +} zone_sign_ctx_t; + +/*! + * \brief Flags determining key type + */ +enum { + DNSKEY_FLAGS_KSK = KNOT_DNSKEY_FLAG_ZONE | KNOT_DNSKEY_FLAG_SEP, + DNSKEY_FLAGS_ZSK = KNOT_DNSKEY_FLAG_ZONE, +}; + +inline static uint16_t dnskey_flags(bool is_ksk) +{ + return is_ksk ? DNSKEY_FLAGS_KSK : DNSKEY_FLAGS_ZSK; +} + +typedef enum { + DNSKEY_GENERATE_KSK = (1 << 0), // KSK flag in metadata + DNSKEY_GENERATE_ZSK = (1 << 1), // ZSK flag in metadata + DNSKEY_GENERATE_SEP_SPEC = (1 << 2), // not (SEP bit set iff KSK) + DNSKEY_GENERATE_SEP_ON = (1 << 3), // SEP bit set on +} kdnssec_generate_flags_t; + +void normalize_generate_flags(kdnssec_generate_flags_t *flags); + +/*! + * \brief Generate new key, store all details in new kasp key structure. + * + * \param ctx kasp context + * \param flags determine if to use the key as KSK and/or ZSK and SEP flag + * \param key_ptr output if KNOT_EOK: new pointer to generated key + * + * \return KNOT_E* + */ +int kdnssec_generate_key(kdnssec_ctx_t *ctx, kdnssec_generate_flags_t flags, + knot_kasp_key_t **key_ptr); + +/*! + * \brief Take a key from another zone (copying info, sharing privkey). + * + * \param ctx kasp context + * \param from_zone name of the zone to take from + * \param key_id ID of the key to take + * + * \return KNOT_E* + */ +int kdnssec_share_key(kdnssec_ctx_t *ctx, const knot_dname_t *from_zone, const char *key_id); + +/*! + * \brief Remove key from zone. + * + * Deletes the key in keystore, unlinks the key from the zone in KASP db, + * moreover if no more zones use this key in KASP db, deletes it completely there + * and deletes it also from key storage (PKCS8dir/PKCS11). + * + * \param ctx kasp context (zone, keystore, kaspdb) to be modified + * \param key_ptr pointer to key to be removed, must be inside keystore structure, NOT a copy of it! + * + * \return KNOT_E* + */ +int kdnssec_delete_key(kdnssec_ctx_t *ctx, knot_kasp_key_t *key_ptr); + +/*! + * \brief Load zone keys and init cryptographic context. + * + * \param ctx Zone signing context. + * \param keyset_ptr Resulting zone keyset. + * \param verbose Print key summary into log. + * + * \return Error code, KNOT_EOK if successful. + */ +int load_zone_keys(kdnssec_ctx_t *ctx, zone_keyset_t *keyset_ptr, bool verbose); + +/*! + * \brief Free structure with zone keys and associated DNSSEC contexts. + * + * \param keyset Zone keys. + */ +void free_zone_keys(zone_keyset_t *keyset); + +/*! + * \brief Get timestamp of next key event. + * + * \param keyset Zone keys. + * + * \return Timestamp of next key event. + */ +knot_time_t knot_get_next_zone_key_event(const zone_keyset_t *keyset); + +/*! + * \brief Returns DS record rdata for given key. + * + * This function caches the results, so caaling again with the same key returns immediately. + * + * \param for_key The key to compute DS for. + * \param out_donotfree Output: the DS record rdata. Do not call dnssec_binry_free() on this ever. + * + * \return Error code, KNOT_EOK if successful. + */ +int zone_key_calculate_ds(zone_key_t *for_key, dnssec_binary_t *out_donotfree); + +/*! + * \brief Initialize local signing context. + * + * \param keyset Key set. + * \param dnssec_ctx DNSSEC context. + * + * \return New local signing context or NULL. + */ +zone_sign_ctx_t *zone_sign_ctx(const zone_keyset_t *keyset, const kdnssec_ctx_t *dnssec_ctx); + +/*! + * \brief Free local signing context. + * + * \note This doesn't free the underlying keyset. + * + * \param ctx Local context to be freed. + */ +void zone_sign_ctx_free(zone_sign_ctx_t *ctx); diff --git a/src/knot/dnssec/zone-nsec.c b/src/knot/dnssec/zone-nsec.c new file mode 100644 index 0000000..95c1056 --- /dev/null +++ b/src/knot/dnssec/zone-nsec.c @@ -0,0 +1,409 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> + +#include "libdnssec/error.h" +#include "libknot/descriptor.h" +#include "libknot/rrtype/nsec3.h" +#include "libknot/rrtype/soa.h" +#include "knot/common/log.h" +#include "knot/dnssec/nsec-chain.h" +#include "knot/dnssec/nsec3-chain.h" +#include "knot/dnssec/key-events.h" +#include "knot/dnssec/rrset-sign.h" +#include "knot/dnssec/zone-nsec.h" +#include "knot/dnssec/zone-sign.h" +#include "knot/zone/zone-diff.h" +#include "contrib/base32hex.h" +#include "contrib/wire_ctx.h" + +/*! + * \brief Deletes NSEC3 chain if NSEC should be used. + * + * \param zone Zone to fix. + * \param changeset Changeset to be used. + */ +static int delete_nsec3_chain(const zone_contents_t *zone, changeset_t *changeset) +{ + assert(zone); + assert(changeset); + + if (zone_tree_is_empty(zone->nsec3_nodes)) { + return KNOT_EOK; + } + + zone_tree_t *empty_tree = zone_tree_create(); + if (!empty_tree) { + return KNOT_ENOMEM; + } + + int ret = zone_tree_add_diff(zone->nsec3_nodes, empty_tree, changeset); + + zone_tree_free(&empty_tree); + + return ret; +} + +int knot_nsec3_hash_to_dname(uint8_t *out, size_t out_size, const uint8_t *hash, + size_t hash_size, const knot_dname_t *zone_apex) + +{ + if (out == NULL || hash == NULL || zone_apex == NULL) { + return KNOT_EINVAL; + } + + // Encode raw hash to the first label. + uint8_t label[KNOT_DNAME_MAXLEN]; + int32_t label_size = base32hex_encode(hash, hash_size, label, sizeof(label)); + if (label_size <= 0) { + return label_size; + } + + // Write the result, which already is in lower-case. + wire_ctx_t wire = wire_ctx_init(out, out_size); + + wire_ctx_write_u8(&wire, label_size); + wire_ctx_write(&wire, label, label_size); + wire_ctx_write(&wire, zone_apex, knot_dname_size(zone_apex)); + + return wire.error; +} + +int knot_create_nsec3_owner(uint8_t *out, size_t out_size, + const knot_dname_t *owner, const knot_dname_t *zone_apex, + const dnssec_nsec3_params_t *params) +{ + if (out == NULL || owner == NULL || zone_apex == NULL || params == NULL) { + return KNOT_EINVAL; + } + + dnssec_binary_t data = { + .data = (uint8_t *)owner, + .size = knot_dname_size(owner) + }; + + dnssec_binary_t hash = { 0 }; + + int ret = dnssec_nsec3_hash(&data, params, &hash); + if (ret != DNSSEC_EOK) { + return knot_error_from_libdnssec(ret); + } + + ret = knot_nsec3_hash_to_dname(out, out_size, hash.data, hash.size, zone_apex); + + dnssec_binary_free(&hash); + + return ret; +} + +static bool nsec3param_valid(const knot_rdataset_t *rrs, + const dnssec_nsec3_params_t *params) +{ + assert(rrs); + assert(params); + + // NSEC3 disabled + if (params->algorithm == 0) { + return false; + } + + // multiple NSEC3 records + if (rrs->count != 1) { + return false; + } + + dnssec_binary_t rdata = { + .size = rrs->rdata->len, + .data = rrs->rdata->data, + }; + + dnssec_nsec3_params_t parsed = { 0 }; + int r = dnssec_nsec3_params_from_rdata(&parsed, &rdata); + if (r != DNSSEC_EOK) { + return false; + } + + bool equal = parsed.algorithm == params->algorithm && + parsed.flags == params->flags && + parsed.iterations == params->iterations && + dnssec_binary_cmp(&parsed.salt, ¶ms->salt) == 0; + + dnssec_nsec3_params_free(&parsed); + + return equal; +} + +static int remove_nsec3param(const zone_contents_t *zone, changeset_t *changeset) +{ + assert(zone); + assert(changeset); + + knot_rrset_t rrset = node_rrset(zone->apex, KNOT_RRTYPE_NSEC3PARAM); + int ret = changeset_add_removal(changeset, &rrset, 0); + if (ret != KNOT_EOK) { + return ret; + } + + rrset = node_rrset(zone->apex, KNOT_RRTYPE_RRSIG); + if (!knot_rrset_empty(&rrset)) { + knot_rrset_t rrsig; + knot_rrset_init(&rrsig, zone->apex->owner, KNOT_RRTYPE_RRSIG, + KNOT_CLASS_IN, 0); + ret = knot_synth_rrsig(KNOT_RRTYPE_NSEC3PARAM, &rrset.rrs, &rrsig.rrs, NULL); + if (ret != KNOT_EOK) { + return ret; + } + + ret = changeset_add_removal(changeset, &rrsig, 0); + knot_rdataset_clear(&rrsig.rrs, NULL); + if (ret != KNOT_EOK) { + return ret; + } + } + + return KNOT_EOK; +} + +static int set_nsec3param(knot_rrset_t *rrset, const dnssec_nsec3_params_t *params) +{ + assert(rrset); + assert(params); + + // Prepare wire rdata. + size_t rdata_len = 3 * sizeof(uint8_t) + sizeof(uint16_t) + params->salt.size; + uint8_t rdata[rdata_len]; + wire_ctx_t wire = wire_ctx_init(rdata, rdata_len); + + wire_ctx_write_u8(&wire, params->algorithm); + wire_ctx_write_u8(&wire, 0); // (RFC 5155 Section 4.1.2) + wire_ctx_write_u16(&wire, params->iterations); + wire_ctx_write_u8(&wire, params->salt.size); + wire_ctx_write(&wire, params->salt.data, params->salt.size); + + if (wire.error != KNOT_EOK) { + return wire.error; + } + + assert(wire_ctx_available(&wire) == 0); + + return knot_rrset_add_rdata(rrset, rdata, rdata_len, NULL); +} + +static int add_nsec3param(const zone_contents_t *zone, changeset_t *changeset, + const dnssec_nsec3_params_t *params) +{ + assert(zone); + assert(changeset); + assert(params); + + knot_rrset_t *rrset = NULL; + rrset = knot_rrset_new(zone->apex->owner, KNOT_RRTYPE_NSEC3PARAM, + KNOT_CLASS_IN, 0, NULL); + if (!rrset) { + return KNOT_ENOMEM; + } + + int r = set_nsec3param(rrset, params); + if (r != KNOT_EOK) { + knot_rrset_free(rrset, NULL); + return r; + } + + r = changeset_add_addition(changeset, rrset, 0); + knot_rrset_free(rrset, NULL); + return r; +} + +static int update_nsec3param(const zone_contents_t *zone, + changeset_t *changeset, + const dnssec_nsec3_params_t *params) +{ + assert(zone); + assert(changeset); + assert(params); + + knot_rdataset_t *nsec3param = node_rdataset(zone->apex, KNOT_RRTYPE_NSEC3PARAM); + bool valid = nsec3param && nsec3param_valid(nsec3param, params); + + if (nsec3param && !valid) { + int r = remove_nsec3param(zone, changeset); + if (r != KNOT_EOK) { + return r; + } + } + + if (params->algorithm != 0 && !valid) { + return add_nsec3param(zone, changeset, params); + } + + return KNOT_EOK; +} + +/*! + * \brief Initialize NSEC3PARAM based on the signing policy. + * + * \note For NSEC, the algorithm number is set to 0. + */ +static dnssec_nsec3_params_t nsec3param_init(const knot_kasp_policy_t *policy, + const knot_kasp_zone_t *zone) +{ + assert(policy); + assert(zone); + + dnssec_nsec3_params_t params = { 0 }; + if (policy->nsec3_enabled) { + params.algorithm = DNSSEC_NSEC3_ALGORITHM_SHA1; + params.iterations = policy->nsec3_iterations; + params.salt = zone->nsec3_salt; + params.flags = (policy->nsec3_opt_out ? KNOT_NSEC3_FLAG_OPT_OUT : 0); + } + + return params; +} + +int knot_zone_create_nsec_chain(zone_update_t *update, + const zone_keyset_t *zone_keys, + const kdnssec_ctx_t *ctx, + bool sign_nsec_chain) +{ + if (update == NULL || ctx == NULL) { + return KNOT_EINVAL; + } + + const knot_rdataset_t *soa = node_rdataset(update->new_cont->apex, KNOT_RRTYPE_SOA); + if (soa == NULL) { + return KNOT_EINVAL; + } + + uint32_t nsec_ttl = knot_soa_minimum(soa->rdata); + dnssec_nsec3_params_t params = nsec3param_init(ctx->policy, ctx->zone); + + changeset_t ch; + int ret = changeset_init(&ch, update->new_cont->apex->owner); + if (ret != KNOT_EOK) { + return ret; + } + + ret = update_nsec3param(update->new_cont, &ch, ¶ms); + if (ret != KNOT_EOK) { + goto cleanup; + } + + if (ctx->policy->nsec3_enabled) { + ret = knot_nsec3_create_chain(update->new_cont, ¶ms, nsec_ttl, + ctx->policy->nsec3_opt_out, &ch); + if (ret != KNOT_EOK) { + goto cleanup; + } + } else { + ret = knot_nsec_create_chain(update->new_cont, nsec_ttl, &ch); + if (ret != KNOT_EOK) { + goto cleanup; + } + + ret = delete_nsec3_chain(update->new_cont, &ch); + if (ret != KNOT_EOK) { + goto cleanup; + } + } + + if (sign_nsec_chain) { + ret = knot_zone_sign_nsecs_in_changeset(zone_keys, ctx, &ch); + } + + if (ret == KNOT_EOK) { + ret = zone_update_apply_changeset(update, &ch); + } + +cleanup: + changeset_clear(&ch); + return ret; +} + + +int knot_zone_fix_nsec_chain(zone_update_t *update, + const zone_keyset_t *zone_keys, + const kdnssec_ctx_t *ctx, + bool sign_nsec_chain) +{ + if (update == NULL || ctx == NULL) { + return KNOT_EINVAL; + } + + const knot_rdataset_t *soa_old = node_rdataset(update->zone->contents->apex, KNOT_RRTYPE_SOA); + const knot_rdataset_t *soa_new = node_rdataset(update->new_cont->apex, KNOT_RRTYPE_SOA); + if (soa_old == NULL || soa_new == NULL) { + return KNOT_EINVAL; + } + + uint32_t nsec_ttl_old = knot_soa_minimum(soa_old->rdata); + uint32_t nsec_ttl_new = knot_soa_minimum(soa_new->rdata); + dnssec_nsec3_params_t params = nsec3param_init(ctx->policy, ctx->zone); + + changeset_t ch; + int ret = changeset_init(&ch, update->new_cont->apex->owner); + if (ret != KNOT_EOK) { + return ret; + } + + if (nsec_ttl_old != nsec_ttl_new) { + ret = KNOT_ENORECORD; + } else if (ctx->policy->nsec3_enabled) { + ret = knot_nsec3_fix_chain(update, ¶ms, nsec_ttl_new, + ctx->policy->nsec3_opt_out, &ch); + } else { + ret = knot_nsec_fix_chain(update->zone->contents, update->new_cont, + nsec_ttl_new, &ch); + } + if (ret == KNOT_ENORECORD) { + log_zone_info(update->zone->name, "DNSSEC, re-creating whole NSEC%s chain", + (ctx->policy->nsec3_enabled ? "3" : "")); + changeset_clear(&ch); + ret = changeset_init(&ch, update->new_cont->apex->owner); + if (ret != KNOT_EOK) { + return ret; + } + if (ctx->policy->nsec3_enabled) { + ret = knot_nsec3_create_chain(update->new_cont, ¶ms, nsec_ttl_new, + ctx->policy->nsec3_opt_out, &ch); + } else { + ret = knot_nsec_create_chain(update->new_cont, nsec_ttl_new, &ch); + } + } + if (ret != KNOT_EOK) { + goto cleanup; + } + + if (sign_nsec_chain) { + ret = knot_zone_sign_nsecs_in_changeset(zone_keys, ctx, &ch); + } + + if (ret == KNOT_EOK) { + // Disable strict changeset application momentarily for the NSEC chain fix. + // This is important for NSEC3, since some nodes are removed from contents + // when fixing individual NSEC3 nodes and then the NSEC3 records from these nodes + // are removed again when the chain is fixed, resulting in double removal, + // forbidden in the strict changeset application. + update->a_ctx->flags &= ~APPLY_STRICT; + ret = zone_update_apply_changeset(update, &ch); + update->a_ctx->flags |= APPLY_STRICT; + } + +cleanup: + changeset_clear(&ch); + return ret; +} diff --git a/src/knot/dnssec/zone-nsec.h b/src/knot/dnssec/zone-nsec.h new file mode 100644 index 0000000..a1e469b --- /dev/null +++ b/src/knot/dnssec/zone-nsec.h @@ -0,0 +1,95 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <stdbool.h> + +#include "knot/dnssec/context.h" +#include "knot/dnssec/zone-keys.h" +#include "knot/updates/zone-update.h" +#include "knot/zone/contents.h" + +/*! + * Check if NSEC3 is enabled for the given zone. + * + * \param zone Zone to be checked. + * + * \return NSEC3 is enabled. + */ +inline static bool knot_is_nsec3_enabled(const zone_contents_t *zone) +{ + return zone != NULL && zone->nsec3_params.algorithm != 0; +} + +/*! + * \brief Create NSEC3 owner name from hash and zone apex. + * + * \param out Output buffer. + * \param out_size Size of the output buffer. + * \param hash Raw hash. + * \param hash_size Size of the hash. + * \param zone_apex Zone apex. + * + * \return Error code, KNOT_EOK if successful. + */ +int knot_nsec3_hash_to_dname(uint8_t *out, size_t out_size, const uint8_t *hash, + size_t hash_size, const knot_dname_t *zone_apex); + +/*! + * \brief Create NSEC3 owner name from regular owner name. + * + * \param out Output buffer. + * \param out_size Size of the output buffer. + * \param owner Node owner name. + * \param zone_apex Zone apex name. + * \param params Params for NSEC3 hashing function. + * + * \return Error code, KNOT_EOK if successful. + */ +int knot_create_nsec3_owner(uint8_t *out, size_t out_size, + const knot_dname_t *owner, const knot_dname_t *zone_apex, + const dnssec_nsec3_params_t *params); + +/*! + * \brief Create NSEC or NSEC3 chain in the zone. + * + * \param update Zone Update with current zone contents and to be updated with NSEC chain. + * \param zone_keys Zone keys used for NSEC(3) creation. + * \param ctx Signing context. + * \param sign_nsec_chain If true, the created NSEC(3) chain is signed at the end. + * + * \return Error code, KNOT_EOK if successful. + */ +int knot_zone_create_nsec_chain(zone_update_t *update, + const zone_keyset_t *zone_keys, + const kdnssec_ctx_t *ctx, + bool sign_nsec_chain); + +/*! + * \brief Fix NSEC or NSEC3 chain after zone was updated. + * + * \param update Zone Update with the update and to be update with NSEC chain. + * \param zone_keys Zone keys used for NSEC(3) creation. + * \param ctx Signing context. + * \param sign_nsec_chain If true, the created NSEC(3) chain is signed at the end. + * + * \return Error code, KNOT_EOK if successful. + */ +int knot_zone_fix_nsec_chain(zone_update_t *update, + const zone_keyset_t *zone_keys, + const kdnssec_ctx_t *ctx, + bool sign_nsec_chain); diff --git a/src/knot/dnssec/zone-sign.c b/src/knot/dnssec/zone-sign.c new file mode 100644 index 0000000..fbd8a91 --- /dev/null +++ b/src/knot/dnssec/zone-sign.c @@ -0,0 +1,1196 @@ +/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> +#include <sys/types.h> + +#include "libdnssec/error.h" +#include "libdnssec/key.h" +#include "libdnssec/keytag.h" +#include "libdnssec/sign.h" +#include "knot/dnssec/key-events.h" +#include "knot/dnssec/rrset-sign.h" +#include "knot/dnssec/zone-sign.h" +#include "libknot/libknot.h" +#include "contrib/macros.h" +#include "contrib/wire_ctx.h" + +typedef struct { + node_t n; + uint16_t type; +} type_node_t; + +typedef struct { + knot_dname_t *dname; + knot_dname_t *hashed_dname; + list_t *type_list; +} signed_info_t; + +/*- private API - common functions -------------------------------------------*/ + +/*! + * \brief Initializes RR set and set owner and rclass from template RR set. + */ +static knot_rrset_t rrset_init_from(const knot_rrset_t *src, uint16_t type) +{ + assert(src); + knot_rrset_t rrset; + knot_rrset_init(&rrset, src->owner, type, src->rclass, src->ttl); + return rrset; +} + +/*! + * \brief Create empty RRSIG RR set for a given RR set to be covered. + */ +static knot_rrset_t create_empty_rrsigs_for(const knot_rrset_t *covered) +{ + assert(!knot_rrset_empty(covered)); + return rrset_init_from(covered, KNOT_RRTYPE_RRSIG); +} + +static bool apex_rr_changed(const zone_node_t *old_apex, + const zone_node_t *new_apex, + uint16_t type) +{ + assert(old_apex); + assert(new_apex); + knot_rrset_t old_rr = node_rrset(old_apex, type); + knot_rrset_t new_rr = node_rrset(new_apex, type); + + return !knot_rrset_equal(&old_rr, &new_rr, KNOT_RRSET_COMPARE_WHOLE); +} + +static bool apex_dnssec_changed(zone_update_t *update) +{ + if (update->zone->contents == NULL || update->new_cont == NULL) { + return false; + } + return apex_rr_changed(update->zone->contents->apex, + update->new_cont->apex, KNOT_RRTYPE_DNSKEY) || + apex_rr_changed(update->zone->contents->apex, + update->new_cont->apex, KNOT_RRTYPE_NSEC3PARAM); +} + +/*- private API - signing of in-zone nodes -----------------------------------*/ + +/*! + * \brief Check if there is a valid signature for a given RR set and key. + * + * \param covered RR set with covered records. + * \param rrsigs RR set with RRSIGs. + * \param key Signing key. + * \param ctx Signing context. + * \param policy DNSSEC policy. + * + * \return The signature exists and is valid. + */ +static bool valid_signature_exists(const knot_rrset_t *covered, + const knot_rrset_t *rrsigs, + const dnssec_key_t *key, + dnssec_sign_ctx_t *ctx, + const kdnssec_ctx_t *dnssec_ctx) +{ + assert(key); + + if (knot_rrset_empty(rrsigs)) { + return false; + } + + uint16_t rrsigs_rdata_count = rrsigs->rrs.count; + knot_rdata_t *rdata = rrsigs->rrs.rdata; + for (uint16_t i = 0; i < rrsigs_rdata_count; i++) { + uint16_t rr_keytag = knot_rrsig_key_tag(rdata); + uint16_t rr_covered = knot_rrsig_type_covered(rdata); + rdata = knot_rdataset_next(rdata); + + uint16_t keytag = dnssec_key_get_keytag(key); + if (rr_keytag != keytag || rr_covered != covered->type) { + continue; + } + + if (knot_check_signature(covered, rrsigs, i, key, ctx, + dnssec_ctx) == KNOT_EOK) { + return true; + } + } + + return false; +} + +/*! + * \brief Check if valid signature exists for all keys for a given RR set. + * + * \param covered RR set with covered records. + * \param rrsigs RR set with RRSIGs. + * \param sign_ctx Local zone signing context. + * + * \return Valid signature exists for every key. + */ +static bool all_signatures_exist(const knot_rrset_t *covered, + const knot_rrset_t *rrsigs, + zone_sign_ctx_t *sign_ctx) +{ + assert(sign_ctx); + + for (int i = 0; i < sign_ctx->count; i++) { + zone_key_t *key = &sign_ctx->keys[i]; + if (!knot_zone_sign_use_key(key, covered)) { + continue; + } + + if (!valid_signature_exists(covered, rrsigs, key->key, + sign_ctx->sign_ctxs[i], + sign_ctx->dnssec_ctx)) { + return false; + } + } + + return true; +} + +/*! + * \brief Note earliest expiration of a signature. + * + * \param rrsig RRSIG rdata. + * \param expires_at Current earliest expiration, will be updated. + */ +static void note_earliest_expiration(const knot_rdata_t *rrsig, knot_time_t *expires_at) +{ + assert(rrsig); + assert(expires_at); + + uint32_t curr_rdata = knot_rrsig_sig_expiration(rrsig); + knot_time_t current = knot_time_from_u32(curr_rdata); + *expires_at = knot_time_min(current, *expires_at); +} + +/*! + * \brief Add expired or invalid RRSIGs into the changeset for removal. + * + * \param covered RR set with covered records. + * \param rrsigs RR set with RRSIGs. + * \param sign_ctx Local zone signing context. + * \param changeset Changeset to be updated. + * \param expires_at Earliest RRSIG expiration. + * + * \return Error code, KNOT_EOK if successful. + */ +static int remove_expired_rrsigs(const knot_rrset_t *covered, + const knot_rrset_t *rrsigs, + zone_sign_ctx_t *sign_ctx, + changeset_t *changeset, + knot_time_t *expires_at) +{ + assert(changeset); + + if (knot_rrset_empty(rrsigs)) { + return KNOT_EOK; + } + + assert(rrsigs->type == KNOT_RRTYPE_RRSIG); + + knot_rrset_t to_remove; + knot_rrset_init_empty(&to_remove); + int result = KNOT_EOK; + + knot_rrset_t synth_rrsig = rrset_init_from(rrsigs, KNOT_RRTYPE_RRSIG); + result = knot_synth_rrsig(covered->type, &rrsigs->rrs, &synth_rrsig.rrs, NULL); + if (result != KNOT_EOK) { + if (result != KNOT_ENOENT) { + return result; + } + return KNOT_EOK; + } + + uint16_t rrsig_rdata_count = synth_rrsig.rrs.count; + for (uint16_t i = 0; i < rrsig_rdata_count; i++) { + knot_rdata_t *rr = knot_rdataset_at(&synth_rrsig.rrs, i); + uint16_t keytag = knot_rrsig_key_tag(rr); + int endloop = 0; // 1 - continue; 2 - break + + for (size_t j = 0; j < sign_ctx->count; j++) { + zone_key_t *key = &sign_ctx->keys[j]; + + if (!key->is_active || dnssec_key_get_keytag(key->key) != keytag) { + continue; + } + + result = knot_check_signature(covered, &synth_rrsig, i, key->key, + sign_ctx->sign_ctxs[j], sign_ctx->dnssec_ctx); + if (result == KNOT_EOK) { + // valid signature + note_earliest_expiration(rr, expires_at); + endloop = 1; + break; + } else if (result != DNSSEC_INVALID_SIGNATURE) { + endloop = 2; + break; + } + } + + if (endloop == 2) { + break; + } else if (endloop == 1) { + continue; + } + + if (knot_rrset_empty(&to_remove)) { + to_remove = create_empty_rrsigs_for(&synth_rrsig); + } + + result = knot_rdataset_add(&to_remove.rrs, rr, NULL); + if (result != KNOT_EOK) { + break; + } + } + + if (!knot_rrset_empty(&to_remove) && result == KNOT_EOK) { + result = changeset_add_removal(changeset, &to_remove, 0); + } + + knot_rdataset_clear(&synth_rrsig.rrs, NULL); + knot_rdataset_clear(&to_remove.rrs, NULL); + + return result; +} + +/*! + * \brief Add missing RRSIGs into the changeset for adding. + * + * \param covered RR set with covered records. + * \param rrsigs RR set with RRSIGs. + * \param sign_ctx Local zone signing context. + * \param changeset Changeset to be updated. + * \param expires_at Earliest RRSIG expiration. + * + * \return Error code, KNOT_EOK if successful. + */ +static int add_missing_rrsigs(const knot_rrset_t *covered, + const knot_rrset_t *rrsigs, + zone_sign_ctx_t *sign_ctx, + changeset_t *changeset, + knot_time_t *expires_at) +{ + assert(!knot_rrset_empty(covered)); + assert(sign_ctx); + assert(changeset); + + int result = KNOT_EOK; + knot_rrset_t to_add; + knot_rrset_init_empty(&to_add); + + for (size_t i = 0; i < sign_ctx->count; i++) { + const zone_key_t *key = &sign_ctx->keys[i]; + if (!knot_zone_sign_use_key(key, covered)) { + continue; + } + + if (valid_signature_exists(covered, rrsigs, key->key, sign_ctx->sign_ctxs[i], + sign_ctx->dnssec_ctx)) { + continue; + } + + if (knot_rrset_empty(&to_add)) { + to_add = create_empty_rrsigs_for(covered); + } + + result = knot_sign_rrset(&to_add, covered, key->key, sign_ctx->sign_ctxs[i], + sign_ctx->dnssec_ctx, NULL, expires_at); + if (result != KNOT_EOK) { + break; + } + } + + if (!knot_rrset_empty(&to_add) && result == KNOT_EOK) { + result = changeset_add_addition(changeset, &to_add, 0); + } + + knot_rdataset_clear(&to_add.rrs, NULL); + + return result; +} + +/*! + * \brief Add all RRSIGs into the changeset for removal. + * + * \param covered RR set with covered records. + * \param changeset Changeset to be updated. + * + * \return Error code, KNOT_EOK if successful. + */ +static int remove_rrset_rrsigs(const knot_dname_t *owner, uint16_t type, + const knot_rrset_t *rrsigs, + changeset_t *changeset) +{ + assert(owner); + assert(changeset); + knot_rrset_t synth_rrsig; + knot_rrset_init(&synth_rrsig, (knot_dname_t *)owner, + KNOT_RRTYPE_RRSIG, rrsigs->rclass, rrsigs->ttl); + int ret = knot_synth_rrsig(type, &rrsigs->rrs, &synth_rrsig.rrs, NULL); + if (ret != KNOT_EOK) { + if (ret != KNOT_ENOENT) { + return ret; + } + return KNOT_EOK; + } + + ret = changeset_add_removal(changeset, &synth_rrsig, 0); + knot_rdataset_clear(&synth_rrsig.rrs, NULL); + + return ret; +} + +/*! + * \brief Drop all existing and create new RRSIGs for covered records. + * + * \param covered RR set with covered records. + * \param rrsigs Existing RRSIGs for covered RR set. + * \param sign_ctx Local zone signing context. + * \param changeset Changeset to be updated. + * + * \return Error code, KNOT_EOK if successful. + */ +static int force_resign_rrset(const knot_rrset_t *covered, + const knot_rrset_t *rrsigs, + zone_sign_ctx_t *sign_ctx, + changeset_t *changeset) +{ + assert(!knot_rrset_empty(covered)); + + if (!knot_rrset_empty(rrsigs)) { + int result = remove_rrset_rrsigs(covered->owner, covered->type, + rrsigs, changeset); + if (result != KNOT_EOK) { + return result; + } + } + + return add_missing_rrsigs(covered, NULL, sign_ctx, changeset, NULL); +} + +/*! + * \brief Drop all expired and create new RRSIGs for covered records. + * + * \param covered RR set with covered records. + * \param rrsigs Existing RRSIGs for covered RR set. + * \param sign_ctx Local zone signing context. + * \param changeset Changeset to be updated. + * \param expires_at Current earliest expiration, will be updated. + * + * \return Error code, KNOT_EOK if successful. + */ +static int resign_rrset(const knot_rrset_t *covered, + const knot_rrset_t *rrsigs, + zone_sign_ctx_t *sign_ctx, + changeset_t *changeset, + knot_time_t *expires_at) +{ + assert(!knot_rrset_empty(covered)); + + // TODO this function creates some signatures twice (for checking) + int result = remove_expired_rrsigs(covered, rrsigs, sign_ctx, + changeset, expires_at); + if (result != KNOT_EOK) { + return result; + } + + return add_missing_rrsigs(covered, rrsigs, sign_ctx, changeset, expires_at); +} + +static int remove_standalone_rrsigs(const zone_node_t *node, + const knot_rrset_t *rrsigs, + changeset_t *changeset) +{ + if (rrsigs == NULL) { + return KNOT_EOK; + } + + uint16_t rrsigs_rdata_count = rrsigs->rrs.count; + knot_rdata_t *rdata = rrsigs->rrs.rdata; + for (uint16_t i = 0; i < rrsigs_rdata_count; ++i) { + uint16_t type_covered = knot_rrsig_type_covered(rdata); + if (!node_rrtype_exists(node, type_covered)) { + knot_rrset_t to_remove; + knot_rrset_init(&to_remove, rrsigs->owner, rrsigs->type, + rrsigs->rclass, rrsigs->ttl); + int ret = knot_rdataset_add(&to_remove.rrs, rdata, NULL); + if (ret != KNOT_EOK) { + return ret; + } + ret = changeset_add_removal(changeset, &to_remove, 0); + knot_rdataset_clear(&to_remove.rrs, NULL); + if (ret != KNOT_EOK) { + return ret; + } + } + rdata = knot_rdataset_next(rdata); + } + + return KNOT_EOK; +} + +/*! + * \brief Update RRSIGs in a given node by updating changeset. + * + * \param node Node to be signed. + * \param sign_ctx Local zone signing context. + * \param changeset Changeset to be updated. + * \param expires_at Current earliest expiration, will be updated. + * + * \return Error code, KNOT_EOK if successful. + */ +static int sign_node_rrsets(const zone_node_t *node, + zone_sign_ctx_t *sign_ctx, + changeset_t *changeset, + knot_time_t *expires_at) +{ + assert(node); + assert(sign_ctx); + + int result = KNOT_EOK; + knot_rrset_t rrsigs = node_rrset(node, KNOT_RRTYPE_RRSIG); + + for (int i = 0; i < node->rrset_count; i++) { + knot_rrset_t rrset = node_rrset_at(node, i); + if (rrset.type == KNOT_RRTYPE_RRSIG) { + continue; + } + + if (!knot_zone_sign_rr_should_be_signed(node, &rrset)) { + continue; + } + + if (sign_ctx->dnssec_ctx->rrsig_drop_existing) { + result = force_resign_rrset(&rrset, &rrsigs, + sign_ctx, changeset); + } else { + result = resign_rrset(&rrset, &rrsigs, sign_ctx, + changeset, expires_at); + } + + if (result != KNOT_EOK) { + return result; + } + } + + return remove_standalone_rrsigs(node, &rrsigs, changeset); +} + +/*! + * \brief Struct to carry data for 'sign_data' callback function. + */ +typedef struct node_sign_args { + zone_sign_ctx_t *sign_ctx; + changeset_t *changeset; + knot_time_t expires_at; +} node_sign_args_t; + +/*! + * \brief Sign node (callback function). + * + * \param node Node to be signed. + * \param data Callback data, node_sign_args_t. + */ +static int sign_node(zone_node_t **node, void *data) +{ + assert(node && *node); + assert(data); + + node_sign_args_t *args = (node_sign_args_t *)data; + + if ((*node)->rrset_count == 0) { + return KNOT_EOK; + } + + if ((*node)->flags & NODE_FLAGS_NONAUTH) { + return KNOT_EOK; + } + + int result = sign_node_rrsets(*node, args->sign_ctx, args->changeset, + &args->expires_at); + + return result; +} + +/*! + * \brief Update RRSIGs in a given zone tree by updating changeset. + * + * \param tree Zone tree to be signed. + * \param zone_keys Zone keys. + * \param policy DNSSEC policy. + * \param changeset Changeset to be updated. + * \param expires_at Expiration time of the oldest signature in zone. + * + * \return Error code, KNOT_EOK if successful. + */ +static int zone_tree_sign(zone_tree_t *tree, + const zone_keyset_t *zone_keys, + const kdnssec_ctx_t *dnssec_ctx, + changeset_t *changeset, + knot_time_t *expires_at) +{ + assert(zone_keys); + assert(dnssec_ctx); + assert(changeset); + + node_sign_args_t args = { + .sign_ctx = zone_sign_ctx(zone_keys, dnssec_ctx), + .changeset = changeset, + .expires_at = knot_time_add(dnssec_ctx->now, dnssec_ctx->policy->rrsig_lifetime), + }; + + if (args.sign_ctx == NULL) { + return KNOT_ENOMEM; + } + + int result = zone_tree_apply(tree, sign_node, &args); + *expires_at = args.expires_at; + + zone_sign_ctx_free(args.sign_ctx); + return result; +} + +/*- private API - signing of NSEC(3) in changeset ----------------------------*/ + +/*! + * \brief Struct to carry data for changeset signing callback functions. + */ +typedef struct { + const zone_contents_t *zone; + zone_sign_ctx_t *sign_ctx; + changeset_t *changeset; + trie_t *signed_tree; +} changeset_signing_data_t; + +/*- private API - DNSKEY handling --------------------------------------------*/ + +static int rrset_add_zone_key(knot_rrset_t *rrset, zone_key_t *zone_key) +{ + assert(rrset); + assert(zone_key); + + dnssec_binary_t dnskey_rdata = { 0 }; + dnssec_key_get_rdata(zone_key->key, &dnskey_rdata); + + return knot_rrset_add_rdata(rrset, dnskey_rdata.data, dnskey_rdata.size, NULL); +} + +static int rrset_add_zone_ds(knot_rrset_t *rrset, zone_key_t *zone_key) +{ + assert(rrset); + assert(zone_key); + + dnssec_binary_t cds_rdata = { 0 }; + zone_key_calculate_ds(zone_key, &cds_rdata); + + return knot_rrset_add_rdata(rrset, cds_rdata.data, cds_rdata.size, NULL); +} + +/*! + * \brief Goes through list and looks for RRSet type there. + * + * \return True if RR type is in the list, false otherwise. + */ +static bool rr_type_in_list(const knot_rrset_t *rr, const list_t *l) +{ + if (l == NULL || EMPTY_LIST(*l)) { + return false; + } + assert(rr); + + type_node_t *n = NULL; + WALK_LIST(n, *l) { + type_node_t *type_node = (type_node_t *)n; + if (type_node->type == rr->type) { + return true; + } + }; + + return false; +} + +static int add_rr_type_to_list(const knot_rrset_t *rr, list_t *l) +{ + assert(rr); + assert(l); + + type_node_t *n = malloc(sizeof(type_node_t)); + if (n == NULL) { + return KNOT_ENOMEM; + } + n->type = rr->type; + + add_head(l, (node_t *)n); + return KNOT_EOK; +} + +/*! + * \brief Checks whether RRSet is not already in the hash table, automatically + * stores its pointer to the table if not found, but returns false in + * that case. + * + * \param rrset RRSet to be checked for. + * \param tree Tree with already signed RRs. + * \param rr_signed Set to true if RR is signed already, false otherwise. + * + * \return KNOT_E* + */ +static int rr_already_signed(const knot_rrset_t *rrset, trie_t *t, + bool *rr_signed) +{ + assert(rrset); + assert(t); + *rr_signed = false; + // Create a key = RRSet owner converted to sortable format + knot_dname_storage_t lf_storage; + uint8_t *lf = knot_dname_lf(rrset->owner, lf_storage); + assert(lf); + trie_val_t stored_info = (signed_info_t *)trie_get_try(t, (char *)lf+1, + *lf); + if (stored_info == NULL) { + // Create new info struct + signed_info_t *info = malloc(sizeof(signed_info_t)); + if (info == NULL) { + return KNOT_ENOMEM; + } + memset(info, 0, sizeof(signed_info_t)); + // Store actual dname repr + info->dname = knot_dname_copy(rrset->owner, NULL); + if (info->dname == NULL) { + free(info); + return KNOT_ENOMEM; + } + // Create new list to insert as a value + info->type_list = malloc(sizeof(list_t)); + if (info->type_list == NULL) { + free(info->dname); + free(info); + return KNOT_ENOMEM; + } + init_list(info->type_list); + // Insert type to list + int ret = add_rr_type_to_list(rrset, info->type_list); + if (ret != KNOT_EOK) { + free(info->type_list); + free(info->dname); + free(info); + return ret; + } + *trie_get_ins(t, (char *)lf+1, *lf) = info; + } else { + signed_info_t *info = *((signed_info_t **)stored_info); + assert(info->type_list); + // Check whether the type is in the list already + if (rr_type_in_list(rrset, info->type_list)) { + *rr_signed = true; + return KNOT_EOK; + } + // Just update the existing list + int ret = add_rr_type_to_list(rrset, info->type_list); + if (ret != KNOT_EOK) { + *rr_signed = false; + return KNOT_EOK; + } + } + + *rr_signed = false; + return KNOT_EOK; +} + +/*! + * \brief Wrapper function for changeset signing - to be used with changeset + * apply functions. + * + * \param chg_rrset RRSet to be signed (potentially) + * \param data Signing data + * + * \return Error code, KNOT_EOK if successful. + */ +static int sign_changeset_wrap(knot_rrset_t *chg_rrset, + changeset_signing_data_t *args, + knot_time_t *expire_at) +{ + // Find RR's node in zone, find out if we need to sign this RR + const zone_node_t *node = + zone_contents_find_node(args->zone, chg_rrset->owner); + + // If node is not in zone, all its RRSIGs were dropped - no-op + if (node) { + knot_rrset_t zone_rrset = node_rrset(node, chg_rrset->type); + knot_rrset_t rrsigs = node_rrset(node, KNOT_RRTYPE_RRSIG); + + bool should_sign = knot_zone_sign_rr_should_be_signed(node, &zone_rrset); + + // Check for RRSet in the 'already_signed' table + if (args->signed_tree && (should_sign && knot_rrset_empty(&zone_rrset))) { + bool already_signed = false; + + int ret = rr_already_signed(chg_rrset, args->signed_tree, + &already_signed); + if (ret != KNOT_EOK) { + return ret; + } + if (already_signed) { + /* Do not sign again. */ + should_sign = false; + } + } + + if (should_sign) { + return resign_rrset(&zone_rrset, &rrsigs, args->sign_ctx, + args->changeset, expire_at); + } else { + /* + * If RRSet in zone DOES have RRSIGs although we + * should not sign it, DDNS-caused change to node/rr + * occurred and we have to drop all RRSIGs. + * + * OR + * + * The whole RRSet was removed, but RRSIGs remained in + * the zone. We need to drop them as well. + */ + return remove_rrset_rrsigs(chg_rrset->owner, + chg_rrset->type, &rrsigs, + args->changeset); + } + } + + return KNOT_EOK; +} + +/*! + * \brief Frees info node about update signing. + * + * \param val Node to free. + * \param d Unused. + */ +static int free_helper_trie_node(trie_val_t *val, void *d) +{ + UNUSED(d); + signed_info_t *info = (signed_info_t *)*val; + if (info->type_list && !EMPTY_LIST(*(info->type_list))) { + WALK_LIST_FREE(*(info->type_list)); + } + free(info->type_list); + knot_dname_free(info->dname, NULL); + knot_dname_free(info->hashed_dname, NULL); + free(info); + return KNOT_EOK; +} + +/*! + * \brief Clears trie with info about update signing. + * + * \param t Trie to clear. + */ +static void knot_zone_clear_sorted_changes(trie_t *t) +{ + if (t) { + trie_apply(t, free_helper_trie_node, NULL); + } +} + +/*- public API ---------------------------------------------------------------*/ + +int knot_zone_sign(zone_update_t *update, + zone_keyset_t *zone_keys, + const kdnssec_ctx_t *dnssec_ctx, + knot_time_t *expire_at) +{ + if (!update || !zone_keys || !dnssec_ctx || !expire_at) { + return KNOT_EINVAL; + } + + int result; + + changeset_t ch; + result = changeset_init(&ch, update->new_cont->apex->owner); + if (result != KNOT_EOK) { + return result; + } + + knot_time_t normal_expire = 0; + result = zone_tree_sign(update->new_cont->nodes, zone_keys, dnssec_ctx, &ch, &normal_expire); + if (result != KNOT_EOK) { + changeset_clear(&ch); + return result; + } + + knot_time_t nsec3_expire = 0; + result = zone_tree_sign(update->new_cont->nsec3_nodes, zone_keys, dnssec_ctx, + &ch, &nsec3_expire); + if (result != KNOT_EOK) { + changeset_clear(&ch); + return result; + } + + *expire_at = knot_time_min(normal_expire, nsec3_expire); + + result = zone_update_apply_changeset(update, &ch); // _fix not needed + changeset_clear(&ch); + + return result; +} + +int knot_zone_sign_update_dnskeys(zone_update_t *update, + zone_keyset_t *zone_keys, + const kdnssec_ctx_t *dnssec_ctx) +{ + if (update == NULL || zone_keys == NULL || dnssec_ctx == NULL) { + return KNOT_EINVAL; + } + + const zone_node_t *apex = update->new_cont->apex; + knot_rrset_t dnskeys = node_rrset(apex, KNOT_RRTYPE_DNSKEY); + knot_rrset_t cdnskeys = node_rrset(apex, KNOT_RRTYPE_CDNSKEY); + knot_rrset_t cdss = node_rrset(apex, KNOT_RRTYPE_CDS); + knot_rrset_t *add_dnskeys = NULL; + knot_rrset_t *add_cdnskeys = NULL; + knot_rrset_t *add_cdss = NULL; + uint32_t dnskey_ttl = dnssec_ctx->policy->dnskey_ttl; + knot_rrset_t soa = node_rrset(apex, KNOT_RRTYPE_SOA); + if (knot_rrset_empty(&soa)) { + return KNOT_EINVAL; + } + + changeset_t ch; + int ret = changeset_init(&ch, apex->owner); + if (ret != KNOT_EOK) { + return ret; + } + +#define CHECK_RET if (ret != KNOT_EOK) goto cleanup + + // remove all. This will cancel out with additions later + ret = changeset_add_removal(&ch, &dnskeys, 0); + CHECK_RET; + ret = changeset_add_removal(&ch, &cdnskeys, 0); + CHECK_RET; + ret = changeset_add_removal(&ch, &cdss, 0); + CHECK_RET; + + // add DNSKEYs, CDNSKEYs and CDSs + add_dnskeys = knot_rrset_new(apex->owner, KNOT_RRTYPE_DNSKEY, soa.rclass, + dnskey_ttl, NULL); + add_cdnskeys = knot_rrset_new(apex->owner, KNOT_RRTYPE_CDNSKEY, soa.rclass, + 0, NULL); + add_cdss = knot_rrset_new(apex->owner, KNOT_RRTYPE_CDS, soa.rclass, + 0, NULL); + if (add_dnskeys == NULL || add_cdnskeys == NULL || add_cdss == NULL) { + ret = KNOT_ENOMEM; + CHECK_RET; + } + zone_key_t *ksk_for_cds = NULL; + unsigned crp = dnssec_ctx->policy->child_records_publish; + int kfc_prio = (crp == CHILD_RECORDS_ALWAYS ? 0 : (crp == CHILD_RECORDS_ROLLOVER ? 1 : 2)); + for (int i = 0; i < zone_keys->count; i++) { + zone_key_t *key = &zone_keys->keys[i]; + if (key->is_public) { + ret = rrset_add_zone_key(add_dnskeys, key); + CHECK_RET; + } + + // determine which key (if any) will be the one for CDS/CDNSKEY + if (key->is_ksk && key->cds_priority > kfc_prio) { + ksk_for_cds = key; + kfc_prio = key->cds_priority; + } + } + + if (ksk_for_cds != NULL) { + ret = rrset_add_zone_key(add_cdnskeys, ksk_for_cds); + CHECK_RET; + ret = rrset_add_zone_ds(add_cdss, ksk_for_cds); + CHECK_RET; + } + + if (crp == CHILD_RECORDS_EMPTY) { + const uint8_t cdnskey_empty[5] = { 0, 0, 3, 0, 0 }; + const uint8_t cds_empty[5] = { 0, 0, 0, 0, 0 }; + ret = knot_rrset_add_rdata(add_cdnskeys, cdnskey_empty, + sizeof(cdnskey_empty), NULL); + CHECK_RET; + ret = knot_rrset_add_rdata(add_cdss, cds_empty, + sizeof(cds_empty), NULL); + CHECK_RET; + } + + if (!knot_rrset_empty(add_cdnskeys)) { + ret = changeset_add_addition(&ch, add_cdnskeys, CHANGESET_CHECK | + CHANGESET_CHECK_CANCELOUT); + CHECK_RET; + } + + if (!knot_rrset_empty(add_cdss)) { + ret = changeset_add_addition(&ch, add_cdss, CHANGESET_CHECK | + CHANGESET_CHECK_CANCELOUT); + CHECK_RET; + } + + if (!knot_rrset_empty(add_dnskeys)) { + ret = changeset_add_addition(&ch, add_dnskeys, CHANGESET_CHECK | + CHANGESET_CHECK_CANCELOUT); + CHECK_RET; + } + + ret = zone_update_apply_changeset(update, &ch); + +#undef CHECK_RET + +cleanup: + knot_rrset_free(add_dnskeys, NULL); + knot_rrset_free(add_cdnskeys, NULL); + knot_rrset_free(add_cdss, NULL); + changeset_clear(&ch); + return ret; +} + +bool knot_zone_sign_use_key(const zone_key_t *key, const knot_rrset_t *covered) +{ + if (key == NULL || covered == NULL) { + return false; + } + + if (!key->is_active) { + return false; + } + + // this may be a problem with offline KSK + bool cds_sign_by_ksk = true; + + assert(key->is_zsk || key->is_ksk); + bool is_apex = knot_dname_is_equal(covered->owner, + dnssec_key_get_dname(key->key)); + if (!is_apex) { + return key->is_zsk; + } + + switch (covered->type) { + case KNOT_RRTYPE_DNSKEY: + return key->is_ksk; + case KNOT_RRTYPE_CDS: + case KNOT_RRTYPE_CDNSKEY: + return (cds_sign_by_ksk ? key->is_ksk : key->is_zsk); + default: + return key->is_zsk; + } +} + +bool knot_zone_sign_soa_expired(const zone_contents_t *zone, + const zone_keyset_t *zone_keys, + const kdnssec_ctx_t *dnssec_ctx) +{ + if (zone == NULL || zone_keys == NULL || dnssec_ctx == NULL) { + return false; + } + + knot_rrset_t soa = node_rrset(zone->apex, KNOT_RRTYPE_SOA); + assert(!knot_rrset_empty(&soa)); + knot_rrset_t rrsigs = node_rrset(zone->apex, KNOT_RRTYPE_RRSIG); + zone_sign_ctx_t *sign_ctx = zone_sign_ctx(zone_keys, dnssec_ctx); + if (sign_ctx == NULL) { + return false; + } + bool exist = all_signatures_exist(&soa, &rrsigs, sign_ctx); + zone_sign_ctx_free(sign_ctx); + return !exist; +} + +static int sign_changeset(const zone_contents_t *zone, + const changeset_t *in_ch, + changeset_t *out_ch, + const zone_keyset_t *zone_keys, + const kdnssec_ctx_t *dnssec_ctx, + knot_time_t *expire_at) +{ + if (zone == NULL || in_ch == NULL || out_ch == NULL) { + return KNOT_EINVAL; + } + + int ret = KNOT_EOK; + + // Create args for wrapper function - trie for duplicate sigs + changeset_signing_data_t args = { + .zone = zone, + .sign_ctx = zone_sign_ctx(zone_keys, dnssec_ctx), + .changeset = out_ch, + .signed_tree = trie_create(NULL) + }; + + if (args.sign_ctx == NULL || args.signed_tree == NULL) { + ret = KNOT_ENOMEM; + goto cleanup; + } + + changeset_iter_t itt; + changeset_iter_all(&itt, in_ch); + + knot_rrset_t rr = changeset_iter_next(&itt); + while (!knot_rrset_empty(&rr)) { + ret = sign_changeset_wrap(&rr, &args, expire_at); + if (ret != KNOT_EOK) { + changeset_iter_clear(&itt); + goto cleanup; + } + rr = changeset_iter_next(&itt); + } + changeset_iter_clear(&itt); + + if (!knot_rrset_empty(in_ch->soa_from)) { + ret = sign_changeset_wrap(in_ch->soa_from, &args, expire_at); + if (ret != KNOT_EOK) { + goto cleanup; + } + } + if (!knot_rrset_empty(in_ch->soa_to)) { + ret = sign_changeset_wrap(in_ch->soa_to, &args, expire_at); + if (ret != KNOT_EOK) { + goto cleanup; + } + } + +cleanup: + knot_zone_clear_sorted_changes(args.signed_tree); + trie_free(args.signed_tree); + zone_sign_ctx_free(args.sign_ctx); + + return ret; +} + +int knot_zone_sign_nsecs_in_changeset(const zone_keyset_t *zone_keys, + const kdnssec_ctx_t *dnssec_ctx, + changeset_t *changeset) +{ + if (zone_keys == NULL || dnssec_ctx == NULL || changeset == NULL) { + return KNOT_EINVAL; + } + + zone_sign_ctx_t *sign_ctx = zone_sign_ctx(zone_keys, dnssec_ctx); + if (sign_ctx == NULL) { + return KNOT_ENOMEM; + } + + changeset_iter_t itt; + changeset_iter_add(&itt, changeset); + + knot_rrset_t rr = changeset_iter_next(&itt); + while (!knot_rrset_empty(&rr)) { + if (rr.type == KNOT_RRTYPE_NSEC || + rr.type == KNOT_RRTYPE_NSEC3 || + rr.type == KNOT_RRTYPE_NSEC3PARAM) { + int ret = add_missing_rrsigs(&rr, NULL, sign_ctx, + changeset, NULL); + if (ret != KNOT_EOK) { + changeset_iter_clear(&itt); + return ret; + } + } + rr = changeset_iter_next(&itt); + } + + changeset_iter_clear(&itt); + zone_sign_ctx_free(sign_ctx); + + return KNOT_EOK; +} + +bool knot_zone_sign_rr_should_be_signed(const zone_node_t *node, + const knot_rrset_t *rrset) +{ + if (node == NULL || knot_rrset_empty(rrset)) { + return false; + } + + // We do not want to sign RRSIGs + if (rrset->type == KNOT_RRTYPE_RRSIG) { + return false; + } + + // At delegation points we only want to sign NSECs and DSs + if (node->flags & NODE_FLAGS_DELEG) { + if (!(rrset->type == KNOT_RRTYPE_NSEC || + rrset->type == KNOT_RRTYPE_DS)) { + return false; + } + } + + return true; +} + +int knot_zone_sign_update(zone_update_t *update, + zone_keyset_t *zone_keys, + const kdnssec_ctx_t *dnssec_ctx, + knot_time_t *expire_at) +{ + if (update == NULL || zone_keys == NULL || dnssec_ctx == NULL || expire_at == NULL) { + return KNOT_EINVAL; + } + + int ret = KNOT_EOK; + + + ret = apply_prepare_to_sign(update->a_ctx); + if (ret != KNOT_EOK) { + return ret; + } + + /* Check if the UPDATE changed DNSKEYs or NSEC3PARAM. + * If so, we have to sign the whole zone. */ + const bool full_sign = apex_dnssec_changed(update); + if (full_sign) { + ret = knot_zone_sign(update, zone_keys, dnssec_ctx, expire_at); + } else { + changeset_t sec_ch; + ret = changeset_init(&sec_ch, update->zone->name); + if (ret != KNOT_EOK) { + return ret; + } + ret = sign_changeset(update->new_cont, &update->change, &sec_ch, + zone_keys, dnssec_ctx, expire_at); + if (ret == KNOT_EOK) { + ret = zone_update_apply_changeset_fix(update, &sec_ch); + } + changeset_clear(&sec_ch); + } + + return ret; +} + +int knot_zone_sign_soa(zone_update_t *update, + const zone_keyset_t *zone_keys, + const kdnssec_ctx_t *dnssec_ctx) +{ + knot_rrset_t soa_to = node_rrset(update->new_cont->apex, KNOT_RRTYPE_SOA); + knot_rrset_t soa_rrsig = node_rrset(update->new_cont->apex, KNOT_RRTYPE_RRSIG); + changeset_t ch; + int ret = changeset_init(&ch, update->zone->name); + if (ret == KNOT_EOK) { + zone_sign_ctx_t *sign_ctx = zone_sign_ctx(zone_keys, dnssec_ctx); + if (sign_ctx == NULL) { + changeset_clear(&ch); + return KNOT_ENOMEM; + } + ret = force_resign_rrset(&soa_to, &soa_rrsig, sign_ctx, &ch); + if (ret == KNOT_EOK) { + ret = zone_update_apply_changeset_fix(update, &ch); + } + zone_sign_ctx_free(sign_ctx); + } + changeset_clear(&ch); + return ret; +} diff --git a/src/knot/dnssec/zone-sign.h b/src/knot/dnssec/zone-sign.h new file mode 100644 index 0000000..33c0a60 --- /dev/null +++ b/src/knot/dnssec/zone-sign.h @@ -0,0 +1,135 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "knot/updates/changesets.h" +#include "knot/updates/zone-update.h" +#include "knot/zone/contents.h" +#include "knot/dnssec/context.h" +#include "knot/dnssec/zone-keys.h" + +/*! + * \brief Adds/removes DNSKEY (and CDNSKEY, CDS) records to zone according to zone keyset. + * + * \param update Structure holding zone contents and to be updated with changes. + * \param zone_keys Keyset with private keys. + * \param dnssec_ctx KASP context. + * + * \return KNOT_E* + */ +int knot_zone_sign_update_dnskeys(zone_update_t *update, + zone_keyset_t *zone_keys, + const kdnssec_ctx_t *dnssec_ctx); + +/*! + * \brief Check if key can be used to sign given RR. + * + * \param key Zone key. + * \param covered RR to be checked. + * + * \return The RR should be signed. + */ +bool knot_zone_sign_use_key(const zone_key_t *key, const knot_rrset_t *covered); + +/*! + * \brief Update zone signatures and store performed changes in update. + * + * Updates RRSIGs, NSEC(3)s, and DNSKEYs. + * + * \param update Zone Update containing the zone and to be updated with new DNSKEYs and RRSIGs. + * \param zone_keys Zone keys. + * \param dnssec_ctx DNSSEC context. + * \param expire_at Time, when the oldest signature in the zone expires. + * + * \return Error code, KNOT_EOK if successful. + */ +int knot_zone_sign(zone_update_t *update, + zone_keyset_t *zone_keys, + const kdnssec_ctx_t *dnssec_ctx, + knot_time_t *expire_at); + +/*! + * \brief Check if zone SOA signatures are expired. + * + * \param zone Zone to be signed. + * \param zone_keys Zone keys. + * \param dnssec_ctx DNSSEC context. + * + * \return True if zone SOA signatures need update, false othewise. + */ +bool knot_zone_sign_soa_expired(const zone_contents_t *zone, + const zone_keyset_t *zone_keys, + const kdnssec_ctx_t *dnssec_ctx); + +/*! + * \brief Sign NSEC/NSEC3 nodes in changeset and update the changeset. + * + * \param zone_keys Zone keys. + * \param dnssec_ctx DNSSEC context. + * \param changeset Changeset to be updated. + * + * \return Error code, KNOT_EOK if successful. + */ +int knot_zone_sign_nsecs_in_changeset(const zone_keyset_t *zone_keys, + const kdnssec_ctx_t *dnssec_ctx, + changeset_t *changeset); + +/*! + * \brief Checks whether RRSet in a node has to be signed. Will not return + * true for all types that should be signed, do not use this as an + * universal function, it is implementation specific. + * + * \param node Node containing the RRSet. + * \param rrset RRSet we are checking for. + * + * \retval true if should be signed. + */ +bool knot_zone_sign_rr_should_be_signed(const zone_node_t *node, + const knot_rrset_t *rrset); + +/*! + * \brief Sign updates of the zone, storing new RRSIGs in this update again. + * + * \param update Zone Update structure. + * \param zone_keys Zone keys. + * \param dnssec_ctx DNSSEC context. + * \param expire_at Time, when the oldest signature in the update expires. + * + * \return Error code, KNOT_EOK if successful. + */ +int knot_zone_sign_update(zone_update_t *update, + zone_keyset_t *zone_keys, + const kdnssec_ctx_t *dnssec_ctx, + knot_time_t *expire_at); + +/*! + * \brief Sign the new SOA record in the Zone Update. + * + * The reason for having this separate is: not updating + * SOA if everything else is unchanged. So, the procedure is + * [refresh_DNSKEY_records]->[recreate_nsec]->[sign_zone]-> + * ->[check_unchanged]->[update_soa]->[sign_soa] + * + * \param update Zone Update with new SOA and to be updated with SOA RRSIG. + * \param zone_keys Zone keys. + * \param dnssec_ctx DNSSEC context. + * + * \return Error code, KNOT_EOK if successful. + */ +int knot_zone_sign_soa(zone_update_t *update, + const zone_keyset_t *zone_keys, + const kdnssec_ctx_t *dnssec_ctx); diff --git a/src/knot/events/events.c b/src/knot/events/events.c new file mode 100644 index 0000000..954c516 --- /dev/null +++ b/src/knot/events/events.c @@ -0,0 +1,475 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <assert.h> +#include <stdarg.h> +#include <time.h> +#include <urcu.h> + +#include "libknot/libknot.h" +#include "knot/common/log.h" +#include "knot/events/events.h" +#include "knot/events/handlers.h" +#include "knot/events/replan.h" +#include "knot/zone/zone.h" + +#define ZONE_EVENT_IMMEDIATE 1 /* Fast-track to worker queue. */ + +typedef int (*zone_event_cb)(conf_t *conf, zone_t *zone); + +typedef struct event_info { + zone_event_type_t type; + const zone_event_cb callback; + const char *name; +} event_info_t; + +static const event_info_t EVENT_INFO[] = { + { ZONE_EVENT_LOAD, event_load, "load" }, + { ZONE_EVENT_REFRESH, event_refresh, "refresh" }, + { ZONE_EVENT_UPDATE, event_update, "update" }, + { ZONE_EVENT_EXPIRE, event_expire, "expiration" }, + { ZONE_EVENT_FLUSH, event_flush, "journal flush" }, + { ZONE_EVENT_NOTIFY, event_notify, "notify" }, + { ZONE_EVENT_DNSSEC, event_dnssec, "DNSSEC re-sign" }, + { ZONE_EVENT_UFREEZE, event_ufreeze, "update freeze" }, + { ZONE_EVENT_UTHAW, event_uthaw, "update thaw" }, + { ZONE_EVENT_NSEC3RESALT, event_nsec3resalt, "NSEC3 resalt" }, + { ZONE_EVENT_PARENT_DS_Q, event_parent_ds_q, "parent DS query" }, + { 0 } +}; + +static const event_info_t *get_event_info(zone_event_type_t type) +{ + const event_info_t *info; + for (info = EVENT_INFO; info->callback != NULL; info++) { + if (info->type == type) { + return info; + } + } + + assert(0); + return NULL; +} + +static bool valid_event(zone_event_type_t type) +{ + return (type > ZONE_EVENT_INVALID && type < ZONE_EVENT_COUNT); +} + +bool ufreeze_applies(zone_event_type_t type) +{ + switch (type) { + case ZONE_EVENT_LOAD: + case ZONE_EVENT_REFRESH: + case ZONE_EVENT_UPDATE: + case ZONE_EVENT_FLUSH: + case ZONE_EVENT_DNSSEC: + case ZONE_EVENT_NSEC3RESALT: + case ZONE_EVENT_PARENT_DS_Q: + return true; + default: + return false; + } +} + +/*! \brief Return remaining time to planned event (seconds). */ +static time_t time_until(time_t planned) +{ + time_t now = time(NULL); + return now < planned ? (planned - now) : 0; +} + +/*! + * \brief Set time of a given event type. + */ +static void event_set_time(zone_events_t *events, zone_event_type_t type, time_t time) +{ + assert(events); + assert(valid_event(type)); + + events->time[type] = time; +} + +/*! + * \brief Get time of a given event type. + */ +static time_t event_get_time(zone_events_t *events, zone_event_type_t type) +{ + assert(events); + assert(valid_event(type)); + + return events->time[type]; +} + +/*! + * \brief Find next scheduled zone event. + * + * \note Afer the UTHAW event, get_next_event() is also invoked. In that situation, + * all the events are suddenly allowed, and those which were planned into + * the ufrozen interval, start to be performed one-by-one sorted by their times. + * + * \param events Zone events. + * + * \return Zone event type, or ZONE_EVENT_INVALID if no event is scheduled. + */ +static zone_event_type_t get_next_event(zone_events_t *events) +{ + if (!events) { + return ZONE_EVENT_INVALID; + } + + zone_event_type_t next_type = ZONE_EVENT_INVALID; + time_t next = 0; + + for (int i = 0; i < ZONE_EVENT_COUNT; i++) { + time_t current = events->time[i]; + + if ((next == 0 || current < next) && (current != 0) && + (events->forced[i] || !events->ufrozen || !ufreeze_applies(i))) { + next = current; + next_type = i; + } + } + + return next_type; +} + +/*! + * \brief Fined time of next scheduled event. + */ +static time_t get_next_time(zone_events_t *events) +{ + zone_event_type_t type = get_next_event(events); + return valid_event(type) ? event_get_time(events, type) : 0; +} + +/*! + * \brief Cancel scheduled item, schedule first enqueued item. + */ +static void reschedule(zone_events_t *events) +{ + assert(events); + + pthread_mutex_lock(&events->reschedule_lock); + pthread_mutex_lock(&events->mx); + + if (!events->event || events->running || events->frozen) { + pthread_mutex_unlock(&events->mx); + pthread_mutex_unlock(&events->reschedule_lock); + return; + } + + zone_event_type_t type = get_next_event(events); + if (!valid_event(type)) { + pthread_mutex_unlock(&events->mx); + pthread_mutex_unlock(&events->reschedule_lock); + return; + } + + time_t diff = time_until(event_get_time(events, type)); + + pthread_mutex_unlock(&events->mx); + + evsched_schedule(events->event, diff * 1000); + + pthread_mutex_unlock(&events->reschedule_lock); +} + +/*! + * \brief Zone event wrapper, expected to be called from a worker thread. + * + * 1. Takes the next planned event. + * 2. Resets the event's scheduled time (and forced flag). + * 3. Perform the event's callback. + * 4. Schedule next event planned event. + */ +static void event_wrap(task_t *task) +{ + assert(task); + assert(task->ctx); + + zone_t *zone = task->ctx; + zone_events_t *events = &zone->events; + + pthread_mutex_lock(&events->mx); + zone_event_type_t type = get_next_event(events); + if (!valid_event(type)) { + events->running = false; + pthread_mutex_unlock(&events->mx); + return; + } + event_set_time(events, type, 0); + events->forced[type] = false; + pthread_mutex_unlock(&events->mx); + + const event_info_t *info = get_event_info(type); + + /* Create a configuration copy just for this event. */ + conf_t *conf; + rcu_read_lock(); + int ret = conf_clone(&conf); + rcu_read_unlock(); + if (ret == KNOT_EOK) { + /* Execute the event callback. */ + ret = info->callback(conf, zone); + conf_free(conf); + } + + if (ret != KNOT_EOK) { + log_zone_error(zone->name, "zone event '%s' failed (%s)", + info->name, knot_strerror(ret)); + } + + pthread_mutex_lock(&events->mx); + events->running = false; + pthread_mutex_unlock(&events->mx); + reschedule(events); +} + +/*! + * \brief Called by scheduler thread if the event occurs. + */ +static void event_dispatch(event_t *event) +{ + assert(event); + assert(event->data); + + zone_events_t *events = event->data; + + pthread_mutex_lock(&events->mx); + if (!events->running && !events->frozen) { + events->running = true; + worker_pool_assign(events->pool, &events->task); + } + pthread_mutex_unlock(&events->mx); +} + +int zone_events_init(zone_t *zone) +{ + if (!zone) { + return KNOT_EINVAL; + } + + zone_events_t *events = &zone->events; + + memset(&zone->events, 0, sizeof(zone->events)); + pthread_mutex_init(&events->mx, NULL); + pthread_mutex_init(&events->reschedule_lock, NULL); + events->task.ctx = zone; + events->task.run = event_wrap; + + return KNOT_EOK; +} + +int zone_events_setup(struct zone *zone, worker_pool_t *workers, + evsched_t *scheduler, knot_db_t *timers_db) +{ + if (!zone || !workers || !scheduler) { + return KNOT_EINVAL; + } + + event_t *event; + event = evsched_event_create(scheduler, event_dispatch, &zone->events); + if (!event) { + return KNOT_ENOMEM; + } + + zone->events.event = event; + zone->events.pool = workers; + zone->events.timers_db = timers_db; + + return KNOT_EOK; +} + +void zone_events_deinit(zone_t *zone) +{ + if (!zone) { + return; + } + + evsched_cancel(zone->events.event); + evsched_event_free(zone->events.event); + + pthread_mutex_destroy(&zone->events.mx); + pthread_mutex_destroy(&zone->events.reschedule_lock); + + memset(&zone->events, 0, sizeof(zone->events)); +} + +void _zone_events_schedule_at(zone_t *zone, ...) +{ + zone_events_t *events = &zone->events; + va_list args; + va_start(args, zone); + + pthread_mutex_lock(&events->mx); + + time_t old_next = get_next_time(events); + + // update timers + for (int type = va_arg(args, int); valid_event(type); type = va_arg(args, int)) { + time_t planned = va_arg(args, time_t); + if (planned < 0) { + continue; + } + + time_t current = event_get_time(events, type); + if (planned == 0 || current == 0 || planned < current) { + event_set_time(events, type, planned); + } + } + + // reschedule if changed + time_t next = get_next_time(events); + pthread_mutex_unlock(&events->mx); + if (old_next != next) { + reschedule(events); + } + + va_end(args); +} + +void zone_events_schedule_user(zone_t *zone, zone_event_type_t type) +{ + if (!zone || !valid_event(type)) { + return; + } + + zone_events_t *events = &zone->events; + pthread_mutex_lock(&events->mx); + events->forced[type] = true; + pthread_mutex_unlock(&events->mx); + + zone_events_schedule_now(zone, type); + + // reschedule because get_next_event result changed outside of _zone_events_schedule_at + reschedule(events); +} + +void zone_events_enqueue(zone_t *zone, zone_event_type_t type) +{ + if (!zone || !valid_event(type)) { + return; + } + + zone_events_t *events = &zone->events; + + pthread_mutex_lock(&events->mx); + + /* Bypass scheduler if no event is running. */ + if (!events->running && !events->frozen && + (!events->ufrozen || !ufreeze_applies(type))) { + events->running = true; + event_set_time(events, type, ZONE_EVENT_IMMEDIATE); + worker_pool_assign(events->pool, &events->task); + pthread_mutex_unlock(&events->mx); + return; + } + + pthread_mutex_unlock(&events->mx); + + /* Execute as soon as possible. */ + zone_events_schedule_now(zone, type); +} + +void zone_events_freeze(zone_t *zone) +{ + if (!zone) { + return; + } + + zone_events_t *events = &zone->events; + + /* Prevent new events being enqueued. */ + pthread_mutex_lock(&events->mx); + events->frozen = true; + pthread_mutex_unlock(&events->mx); + + /* Cancel current event. */ + evsched_cancel(events->event); +} + +void zone_events_start(zone_t *zone) +{ + if (!zone) { + return; + } + + zone_events_t *events = &zone->events; + + /* Unlock the events queue. */ + pthread_mutex_lock(&events->mx); + events->frozen = false; + pthread_mutex_unlock(&events->mx); + + reschedule(events); +} + +time_t zone_events_get_time(const struct zone *zone, zone_event_type_t type) +{ + if (zone == NULL) { + return KNOT_EINVAL; + } + + time_t event_time = KNOT_ENOENT; + zone_events_t *events = (zone_events_t *)&zone->events; + + pthread_mutex_lock(&events->mx); + + /* Get next valid event. */ + if (valid_event(type)) { + event_time = event_get_time(events, type); + } + + pthread_mutex_unlock(&events->mx); + + return event_time; +} + +const char *zone_events_get_name(zone_event_type_t type) +{ + /* Get information about the event and time. */ + const event_info_t *info = get_event_info(type); + if (info == NULL) { + return NULL; + } + + return info->name; +} + +time_t zone_events_get_next(const struct zone *zone, zone_event_type_t *type) +{ + if (zone == NULL || type == NULL) { + return KNOT_EINVAL; + } + + time_t next_time = KNOT_ENOENT; + zone_events_t *events = (zone_events_t *)&zone->events; + + pthread_mutex_lock(&events->mx); + + /* Get time of next valid event. */ + *type = get_next_event(events); + if (valid_event(*type)) { + next_time = event_get_time(events, *type); + } else { + *type = ZONE_EVENT_INVALID; + } + + pthread_mutex_unlock(&events->mx); + + return next_time; +} diff --git a/src/knot/events/events.h b/src/knot/events/events.h new file mode 100644 index 0000000..a95db32 --- /dev/null +++ b/src/knot/events/events.h @@ -0,0 +1,190 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include <pthread.h> +#include <stdbool.h> +#include <sys/time.h> + +#include "knot/conf/conf.h" +#include "knot/common/evsched.h" +#include "knot/worker/pool.h" +#include "libknot/db/db.h" + +struct zone; + +typedef enum zone_event_type { + ZONE_EVENT_INVALID = -1, + // supported event types + ZONE_EVENT_LOAD = 0, + ZONE_EVENT_REFRESH, + ZONE_EVENT_UPDATE, + ZONE_EVENT_EXPIRE, + ZONE_EVENT_FLUSH, + ZONE_EVENT_NOTIFY, + ZONE_EVENT_DNSSEC, + ZONE_EVENT_UFREEZE, + ZONE_EVENT_UTHAW, + ZONE_EVENT_NSEC3RESALT, + ZONE_EVENT_PARENT_DS_Q, + // terminator + ZONE_EVENT_COUNT, +} zone_event_type_t; + +typedef struct zone_events { + pthread_mutex_t mx; //!< Mutex protecting the struct. + pthread_mutex_t reschedule_lock;//!< Prevent concurrent reschedule() making mess. + bool running; //!< Some zone event is being run. + bool frozen; //!< Terminated, don't schedule new events. + bool ufrozen; //!< Updates to the zone temporarily frozen by user. + + event_t *event; //!< Scheduler event. + worker_pool_t *pool; //!< Server worker pool. + knot_db_t *timers_db; //!< Persistent zone timers database. + + task_t task; //!< Event execution context. + time_t time[ZONE_EVENT_COUNT]; //!< Event execution times. + bool forced[ZONE_EVENT_COUNT]; //!< Flag that the event was invoked by user ctl. +} zone_events_t; + +/*! + * \brief Initialize zone events. + * + * The function will not set up the scheduling, use \ref zone_events_setup + * to do that. + * + * \param zone Pointer to zone (context of execution). + * + * \return KNOT_E* + */ +int zone_events_init(struct zone *zone); + +/*! + * \brief Set up zone events execution. + * + * \param zone Zone to setup. + * \param workers Worker thread pool. + * \param scheduler Event scheduler. + * \param timers_db Persistent timers database. Can be NULL. + * + * \return KNOT_E* + */ +int zone_events_setup(struct zone *zone, worker_pool_t *workers, + evsched_t *scheduler, knot_db_t *timers_db); + +/*! + * \brief Deinitialize zone events. + * + * \param zone Zone whose events we want to deinitialize. + */ +void zone_events_deinit(struct zone *zone); + +/*! + * \brief Enqueue event type for asynchronous execution. + * + * \note This is similar to the scheduling an event for NOW, but it can + * bypass the event scheduler if no event is running at the moment. + * + * \param zone Zone to schedule new event for. + * \param type Type of event. + */ +void zone_events_enqueue(struct zone *zone, zone_event_type_t type); + +/*! + * \brief Schedule new zone event. + * + * The function allows to set multiple events at once. + * + * The function intreprets time values (t) as follows: + * + * t > 0: schedule timer for a given time + * t = 0: cancel the timer + * t < 0: ignore change in the timer + * + * If the event is already scheduled, the new time will be set only if the + * new time is earlier than the currently scheduled one. To override the + * check, cancel and schedule the event in a single function call. + * + * \param zone Zone to schedule new event for. + * \param ... Sequence of zone_event_type_t and time_t terminated with + * ZONE_EVENT_INVALID. + */ +void _zone_events_schedule_at(struct zone *zone, ...); + +#define zone_events_schedule_at(zone, events...) \ + _zone_events_schedule_at(zone, events, ZONE_EVENT_INVALID) + +#define zone_events_schedule_now(zone, type) \ + zone_events_schedule_at(zone, type, time(NULL)) + +/*! + * \brief Schedule zone event to now, with forced flag. + */ +void zone_events_schedule_user(struct zone *zone, zone_event_type_t type); + +/*! + * \brief Freeze all zone events and prevent new events from running. + * + * \param zone Zone to freeze events for. + */ +void zone_events_freeze(struct zone *zone); + +/*! + * \brief ufreeze_applies + * \param type Type of event to be checked + * \return true / false if user freeze applies + */ +bool ufreeze_applies(zone_event_type_t type); + +/*! + * \brief Start the events processing. + * + * \param zone Zone to start processing for. + */ +void zone_events_start(struct zone *zone); + +/*! + * \brief Return time of the occurrence of the given event. + * + * \param zone Zone to get event time from. + * \param type Event type. + * + * \retval time of the event when event found + * \retval 0 when the event is not planned + * \retval negative value if event is invalid + */ +time_t zone_events_get_time(const struct zone *zone, zone_event_type_t type); + +/*! + * \brief Return text name of the event. + * + * \param type Type of event. + * + * \retval String with event name if it exists. + * \retval NULL if the event does not exist. + */ +const char *zone_events_get_name(zone_event_type_t type); + +/*! + * \brief Return time and type of the next event. + * + * \param zone Zone to get next event from. + * \param type [out] Type of the next event will be stored in the parameter. + * + * \return time of the next event or an error (negative number) + */ +time_t zone_events_get_next(const struct zone *zone, zone_event_type_t *type); diff --git a/src/knot/events/handlers.h b/src/knot/events/handlers.h new file mode 100644 index 0000000..a103e42 --- /dev/null +++ b/src/knot/events/handlers.h @@ -0,0 +1,47 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "knot/conf/conf.h" +#include "knot/zone/zone.h" +#include "knot/dnssec/zone-events.h" // zone_sign_reschedule_t + +/*! \brief Loads or reloads potentially changed zone. */ +int event_load(conf_t *conf, zone_t *zone); +/*! \brief Refresh a zone from a master. */ +int event_refresh(conf_t *conf, zone_t *zone); +/*! \brief Processes DDNS updates in the zone's DDNS queue. */ +int event_update(conf_t *conf, zone_t *zone); +/*! \brief Empties in-memory zone contents. */ +int event_expire(conf_t *conf, zone_t *zone); +/*! \brief Flushes zone contents into text file. */ +int event_flush(conf_t *conf, zone_t *zone); +/*! \brief Sends notify to slaves. */ +int event_notify(conf_t *conf, zone_t *zone); +/*! \brief Signs the zone using its DNSSEC keys, perform key rollovers. */ +int event_dnssec(conf_t *conf, zone_t *zone); +/*! \brief NOT A HANDLER, just a helper function to reschedule based on reschedule_t */ +void event_dnssec_reschedule(conf_t *conf, zone_t *zone, + const zone_sign_reschedule_t *refresh, bool zone_changed); +/*! \brief Freeze those events causing zone contents change. */ +int event_ufreeze(conf_t *conf, zone_t *zone); +/*! \brief Unfreeze zone updates. */ +int event_uthaw(conf_t *conf, zone_t *zone); +/*! \brief Recreates salt for NSEC3 hashing. */ +int event_nsec3resalt(conf_t *conf, zone_t *zone); +/*! \brief When CDS/CDNSKEY published, look for matching DS */ +int event_parent_ds_q(conf_t *conf, zone_t *zone); diff --git a/src/knot/events/handlers/dnssec.c b/src/knot/events/handlers/dnssec.c new file mode 100644 index 0000000..00d3aef --- /dev/null +++ b/src/knot/events/handlers/dnssec.c @@ -0,0 +1,116 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> + +#include "knot/common/log.h" +#include "knot/conf/conf.h" +#include "knot/dnssec/zone-events.h" +#include "knot/updates/apply.h" +#include "knot/zone/zone.h" +#include "libknot/errcode.h" + +static void log_dnssec_next(const knot_dname_t *zone, knot_time_t refresh_at) +{ + char time_str[64] = { 0 }; + struct tm time_gm = { 0 }; + time_t refresh = refresh_at; + localtime_r(&refresh, &time_gm); + strftime(time_str, sizeof(time_str), KNOT_LOG_TIME_FORMAT, &time_gm); + if (refresh_at == 0) { + log_zone_warning(zone, "DNSSEC, next signing not scheduled"); + } else { + log_zone_info(zone, "DNSSEC, next signing at %s", time_str); + } +} + +void event_dnssec_reschedule(conf_t *conf, zone_t *zone, + const zone_sign_reschedule_t *refresh, bool zone_changed) +{ + time_t now = time(NULL); + time_t ignore = -1; + knot_time_t refresh_at = refresh->next_sign; + + if (knot_time_cmp(refresh->next_rollover, refresh_at) < 0) { + refresh_at = refresh->next_rollover; + } + + log_dnssec_next(zone->name, (time_t)refresh_at); + + if (refresh->plan_ds_query) { + zone->timers.next_parent_ds_q = now; + } + + if (refresh->allow_nsec3resalt) { + zone->timers.last_resalt = time(NULL); + } + + zone_events_schedule_at(zone, + ZONE_EVENT_DNSSEC, refresh_at ? (time_t)refresh_at : ignore, + ZONE_EVENT_PARENT_DS_Q, refresh->plan_ds_query ? now : ignore, + ZONE_EVENT_NSEC3RESALT, refresh->next_nsec3resalt ? refresh->next_nsec3resalt : ignore, + ZONE_EVENT_NOTIFY, zone_changed ? now : ignore + ); +} + +int event_dnssec(conf_t *conf, zone_t *zone) +{ + assert(zone); + + zone_sign_reschedule_t resch = { 0 }; + resch.allow_rollover = true; + int sign_flags = 0; + + if (zone->flags & ZONE_FORCE_RESIGN) { + log_zone_info(zone->name, "DNSSEC, dropping previous " + "signatures, re-signing zone"); + zone->flags &= ~ZONE_FORCE_RESIGN; + sign_flags = ZONE_SIGN_DROP_SIGNATURES; + } else { + log_zone_info(zone->name, "DNSSEC, signing zone"); + sign_flags = 0; + } + + if (zone_events_get_time(zone, ZONE_EVENT_NSEC3RESALT) <= time(NULL)) { + resch.allow_nsec3resalt = true; + } + + zone_update_t up; + int ret = zone_update_init(&up, zone, UPDATE_INCREMENTAL); + if (ret != KNOT_EOK) { + return ret; + } + + ret = knot_dnssec_zone_sign(&up, sign_flags, &resch); + if (ret != KNOT_EOK) { + goto done; + } + + bool zone_changed = !zone_update_no_change(&up); + if (zone_changed) { + ret = zone_update_commit(conf, &up); + if (ret != KNOT_EOK) { + goto done; + } + } + + // Schedule dependent events + event_dnssec_reschedule(conf, zone, &resch, zone_changed); + +done: + zone_update_clear(&up); + return ret; +} diff --git a/src/knot/events/handlers/expire.c b/src/knot/events/handlers/expire.c new file mode 100644 index 0000000..16e904d --- /dev/null +++ b/src/knot/events/handlers/expire.c @@ -0,0 +1,46 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> +#include <urcu.h> + +#include "contrib/trim.h" +#include "knot/common/log.h" +#include "knot/conf/conf.h" +#include "knot/events/handlers.h" +#include "knot/events/replan.h" +#include "knot/zone/contents.h" +#include "knot/zone/zone.h" + +int event_expire(conf_t *conf, zone_t *zone) +{ + assert(zone); + + zone_contents_t *expired = zone_switch_contents(zone, NULL); + log_zone_info(zone->name, "zone expired"); + + synchronize_rcu(); + zone_contents_deep_free(expired); + + zone->zonefile.exists = false; + mem_trim(); + + // NOTE: must preserve zone->timers.soa_expire + zone->timers.next_refresh = time(NULL); + replan_from_timers(conf, zone); + + return KNOT_EOK; +} diff --git a/src/knot/events/handlers/flush.c b/src/knot/events/handlers/flush.c new file mode 100644 index 0000000..227205d --- /dev/null +++ b/src/knot/events/handlers/flush.c @@ -0,0 +1,38 @@ +/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> +#include <time.h> + +#include "knot/conf/conf.h" +#include "knot/zone/zone.h" + +int event_flush(conf_t *conf, zone_t *zone) +{ + assert(conf); + assert(zone); + + if (zone_contents_is_empty(zone->contents)) { + return KNOT_EOK; + } + + int ret = zone_flush_journal(conf, zone); + if (ret != KNOT_EOK) { + return ret; + } + + return KNOT_EOK; +} diff --git a/src/knot/events/handlers/freeze_thaw.c b/src/knot/events/handlers/freeze_thaw.c new file mode 100644 index 0000000..1df3adf --- /dev/null +++ b/src/knot/events/handlers/freeze_thaw.c @@ -0,0 +1,49 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "contrib/macros.h" +#include "knot/events/events.h" +#include "knot/conf/conf.h" +#include "knot/zone/zone.h" +#include "knot/common/log.h" + +int event_ufreeze(conf_t *conf, zone_t *zone) +{ + UNUSED(conf); + assert(zone); + + pthread_mutex_lock(&zone->events.mx); + zone->events.ufrozen = true; + pthread_mutex_unlock(&zone->events.mx); + + log_zone_info(zone->name, "zone updates frozen"); + + return KNOT_EOK; +} + +int event_uthaw(conf_t *conf, zone_t *zone) +{ + UNUSED(conf); + assert(zone); + + pthread_mutex_lock(&zone->events.mx); + zone->events.ufrozen = false; + pthread_mutex_unlock(&zone->events.mx); + + log_zone_info(zone->name, "zone updates unfrozen"); + + return KNOT_EOK; +} diff --git a/src/knot/events/handlers/load.c b/src/knot/events/handlers/load.c new file mode 100644 index 0000000..7410d30 --- /dev/null +++ b/src/knot/events/handlers/load.c @@ -0,0 +1,307 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> + +#include "knot/common/log.h" +#include "knot/conf/conf.h" +#include "knot/dnssec/key-events.h" +#include "knot/dnssec/zone-events.h" +#include "knot/events/handlers.h" +#include "knot/events/replan.h" +#include "knot/zone/zone-diff.h" +#include "knot/zone/zone-load.h" +#include "knot/zone/zone.h" +#include "knot/zone/zonefile.h" +#include "knot/updates/acl.h" + +static bool dontcare_load_error(conf_t *conf, const zone_t *zone) +{ + return (zone->contents == NULL && zone_load_can_bootstrap(conf, zone->name)); +} + +static bool allowed_xfr(conf_t *conf, const zone_t *zone) +{ + conf_val_t acl = conf_zone_get(conf, C_ACL, zone->name); + while (acl.code == KNOT_EOK) { + conf_val_t action = conf_id_get(conf, C_ACL, C_ACTION, &acl); + while (action.code == KNOT_EOK) { + if (conf_opt(&action) == ACL_ACTION_TRANSFER) { + return true; + } + conf_val_next(&action); + } + conf_val_next(&acl); + } + + return false; +} + +int event_load(conf_t *conf, zone_t *zone) +{ + zone_contents_t *journal_conts = NULL, *zf_conts = NULL; + bool old_contents_exist = (zone->contents != NULL); + + conf_val_t val = conf_zone_get(conf, C_JOURNAL_CONTENT, zone->name); + unsigned load_from = conf_opt(&val); + + val = conf_zone_get(conf, C_ZONEFILE_LOAD, zone->name); + unsigned zf_from = conf_opt(&val); + + int ret = KNOT_EOK; + + // If configured, load journal contents. + if (load_from == JOURNAL_CONTENT_ALL && !old_contents_exist && zf_from != ZONEFILE_LOAD_WHOLE) { + ret = zone_load_from_journal(conf, zone, &journal_conts); + if (ret != KNOT_EOK) { + journal_conts = NULL; + } + } + + // If configured, attempt to load zonefile. + if (zf_from != ZONEFILE_LOAD_NONE) { + time_t mtime; + char *filename = conf_zonefile(conf, zone->name); + ret = zonefile_exists(filename, &mtime); + bool zonefile_unchanged = (zone->zonefile.exists && zone->zonefile.mtime == mtime); + free(filename); + if (ret == KNOT_EOK) { + ret = zone_load_contents(conf, zone->name, &zf_conts); + } + if (ret != KNOT_EOK) { + zf_conts = NULL; + if (dontcare_load_error(conf, zone)) { + log_zone_info(zone->name, "failed to parse zone file (%s)", + knot_strerror(ret)); + } else { + log_zone_error(zone->name, "failed to parse zone file (%s)", + knot_strerror(ret)); + } + goto cleanup; + } + + // Save zonefile information. + zone->zonefile.serial = zone_contents_serial(zf_conts); + zone->zonefile.exists = (zf_conts != NULL); + zone->zonefile.mtime = mtime; + + // If configured and possible, fix the SOA serial of zonefile. + zone_contents_t *relevant = (zone->contents != NULL ? zone->contents : journal_conts); + if (zf_conts != NULL && zf_from == ZONEFILE_LOAD_DIFSE && relevant != NULL) { + uint32_t serial = zone_contents_serial(relevant); + conf_val_t policy = conf_zone_get(conf, C_SERIAL_POLICY, zone->name); + uint32_t set = serial_next(serial, conf_opt(&policy)); + zone_contents_set_soa_serial(zf_conts, set); + log_zone_info(zone->name, "zone file parsed, serial corrected %u -> %u", + zone->zonefile.serial, set); + zone->zonefile.serial = set; + } else { + log_zone_info(zone->name, "zone file parsed, serial %u", + zone->zonefile.serial); + } + + // If configured and appliable to zonefile, load journal changes. + bool journal_load_configured1 = (load_from == JOURNAL_CONTENT_CHANGES); + bool journal_load_configured2 = (load_from == JOURNAL_CONTENT_ALL); + + if ((journal_load_configured1 || journal_load_configured2) && + (!old_contents_exist || zonefile_unchanged)) { + ret = zone_load_journal(conf, zone, zf_conts); + if (ret != KNOT_EOK) { + zone_contents_deep_free(zf_conts); + zf_conts = NULL; + log_zone_warning(zone->name, "failed to load journal (%s)", + knot_strerror(ret)); + } + } + } + + // If configured contents=all, but not present, store zonefile. + if (load_from == JOURNAL_CONTENT_ALL && + journal_conts == NULL && zf_conts != NULL && !old_contents_exist) { + ret = zone_in_journal_store(conf, zone, zf_conts); + if (ret != KNOT_EOK) { + log_zone_warning(zone->name, "failed to write zone-in-journal (%s)", + knot_strerror(ret)); + } + } + + val = conf_zone_get(conf, C_DNSSEC_SIGNING, zone->name); + bool dnssec_enable = conf_bool(&val), zu_from_zf_conts = false; + zone_update_t up = { 0 }; + bool ignore_dnssec = ((zf_from == ZONEFILE_LOAD_DIFF || zf_from == ZONEFILE_LOAD_DIFSE) + && dnssec_enable); + + // Create zone_update structure according to current state. + if (old_contents_exist) { + if (zf_conts == NULL) { + // nothing to be re-loaded + ret = KNOT_EOK; + goto cleanup; + } else if (zf_from == ZONEFILE_LOAD_WHOLE) { + // throw old zone contents and load new from ZF + ret = zone_update_from_contents(&up, zone, zf_conts, + (load_from == JOURNAL_CONTENT_NONE ? + UPDATE_FULL : UPDATE_INCREMENTAL)); + zu_from_zf_conts = true; + } else { + // compute ZF diff and if success, apply it + ret = zone_update_from_differences(&up, zone, zone->contents, zf_conts, UPDATE_INCREMENTAL, ignore_dnssec); + zone_contents_deep_free(zf_conts); + zf_conts = NULL; + } + } else { + if (journal_conts != NULL && zf_from != ZONEFILE_LOAD_WHOLE) { + if (zf_conts == NULL) { + // load zone-in-journal + ret = zone_update_from_contents(&up, zone, journal_conts, UPDATE_INCREMENTAL); + } else { + // load zone-in-journal, compute ZF diff and if success, apply it + ret = zone_update_from_differences(&up, zone, journal_conts, zf_conts, + UPDATE_INCREMENTAL | UPDATE_JOURNAL, ignore_dnssec); + zone_contents_deep_free(zf_conts); + zf_conts = NULL; + if (ret == KNOT_ESEMCHECK || ret == KNOT_ERANGE) { + log_zone_warning(zone->name, + "zone file changed with SOA serial %s, " + "ignoring zone file and loading from journal", + (ret == KNOT_ESEMCHECK ? "unupdated" : "decreased")); + ret = zone_update_from_contents(&up, zone, journal_conts, UPDATE_INCREMENTAL); + } + } + } else { + if (zf_conts == NULL) { + // nothing to be loaded + ret = KNOT_ENOENT; + } else { + // load from ZF + ret = zone_update_from_contents(&up, zone, zf_conts, + (load_from == JOURNAL_CONTENT_NONE ? + UPDATE_FULL : UPDATE_INCREMENTAL)); + if (zf_from == ZONEFILE_LOAD_WHOLE) { + zu_from_zf_conts = true; + } + } + } + } + if (ret != KNOT_EOK) { + switch (ret) { + case KNOT_ENOENT: + if (zone_load_can_bootstrap(conf, zone->name)) { + log_zone_info(zone->name, "zone will be bootstrapped"); + } else { + log_zone_info(zone->name, "zone not found"); + } + break; + case KNOT_ESEMCHECK: + log_zone_warning(zone->name, "zone file changed without SOA serial update"); + break; + case KNOT_ERANGE: + log_zone_warning(zone->name, "zone file changed, but SOA serial decreased"); + break; + } + goto cleanup; + } + + // The contents are already part of zone_update. + zf_conts = NULL; + journal_conts = NULL; + + // Sign zone using DNSSEC if configured. + zone_sign_reschedule_t dnssec_refresh = { .allow_rollover = true, .allow_nsec3resalt = true, }; + if (dnssec_enable) { + ret = knot_dnssec_zone_sign(&up, 0, &dnssec_refresh); + if (ret != KNOT_EOK) { + zone_update_clear(&up); + goto cleanup; + } + if (zu_from_zf_conts && (up.flags & UPDATE_INCREMENTAL) && allowed_xfr(conf, zone)) { + log_zone_warning(zone->name, + "with automatic DNSSEC signing and outgoing transfers enabled, " + "'zonefile-load: difference' should be set to avoid malformed " + "IXFR after manual zone file update"); + } + } + + // If the change is only automatically incremented SOA serial, make it no change. + if (zf_from == ZONEFILE_LOAD_DIFSE && (up.flags & UPDATE_INCREMENTAL) && + changeset_differs_just_serial(&up.change)) { + changeset_t *cpy = changeset_clone(&up.change); + if (cpy == NULL) { + ret = KNOT_ENOMEM; + goto cleanup; + } + ret = zone_update_apply_changeset_reverse(&up, cpy); + changeset_free(cpy); + if (ret == KNOT_EOK) { + ret = changeset_remove_addition(&up.change, up.change.soa_to); + } + if (ret != KNOT_EOK) { + goto cleanup; + } + } + + bool incremental = ((up.flags & UPDATE_INCREMENTAL) && !zone_update_no_change(&up)); + uint32_t old_serial = (!incremental ? 0 : + (up.zone->contents ? zone_contents_serial(up.zone->contents) : zone->zonefile.serial) + ); + uint32_t new_serial = zone_contents_serial(up.new_cont); + + // Commit zone_update back to zone (including journal update, rcu,...). + ret = zone_update_commit(conf, &up); + zone_update_clear(&up); + if (ret != KNOT_EOK) { + goto cleanup; + } + + if (incremental) { + log_zone_info(zone->name, "loaded, serial %u -> %u, %zu bytes", + old_serial, new_serial, zone->contents->size); + } else { + log_zone_info(zone->name, "loaded, serial %u, %zu bytes", + new_serial, zone->contents->size); + } + + if (zone->control_update != NULL) { + log_zone_warning(zone->name, "control transaction aborted"); + zone_control_clear(zone); + } + + // Schedule depedent events. + const knot_rdataset_t *soa = zone_soa(zone); + zone->timers.soa_expire = knot_soa_expire(soa->rdata); + + if (dnssec_enable) { + event_dnssec_reschedule(conf, zone, &dnssec_refresh, false); // false since we handle NOTIFY below + } + + replan_from_timers(conf, zone); + + if (old_serial != new_serial) { + zone_events_schedule_now(zone, ZONE_EVENT_NOTIFY); + } + + return KNOT_EOK; + +cleanup: + // Try to bootstrap the zone if local error. + replan_from_timers(conf, zone); + + zone_contents_deep_free(zf_conts); + zone_contents_deep_free(journal_conts); + + return (dontcare_load_error(conf, zone) ? KNOT_EOK : ret); +} diff --git a/src/knot/events/handlers/notify.c b/src/knot/events/handlers/notify.c new file mode 100644 index 0000000..bcd81bf --- /dev/null +++ b/src/knot/events/handlers/notify.c @@ -0,0 +1,158 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> + +#include "knot/common/log.h" +#include "knot/conf/conf.h" +#include "knot/query/query.h" +#include "knot/query/requestor.h" +#include "knot/zone/zone.h" +#include "libknot/errcode.h" + +/*! + * \brief NOTIFY message processing data. + */ +struct notify_data { + const knot_dname_t *zone; + const knot_rrset_t *soa; + const struct sockaddr *remote; + struct query_edns_data edns; +}; + +static int notify_begin(knot_layer_t *layer, void *params) +{ + layer->data = params; + + return KNOT_STATE_PRODUCE; +} + +static int notify_produce(knot_layer_t *layer, knot_pkt_t *pkt) +{ + struct notify_data *data = layer->data; + + // mandatory: NOTIFY opcode, AA flag, SOA qtype + query_init_pkt(pkt); + knot_wire_set_opcode(pkt->wire, KNOT_OPCODE_NOTIFY); + knot_wire_set_aa(pkt->wire); + knot_pkt_put_question(pkt, data->zone, KNOT_CLASS_IN, KNOT_RRTYPE_SOA); + + // unsecure hint: new SOA + if (data->soa) { + knot_pkt_begin(pkt, KNOT_ANSWER); + knot_pkt_put(pkt, KNOT_COMPR_HINT_QNAME, data->soa, 0); + } + + query_put_edns(pkt, &data->edns); + + return KNOT_STATE_CONSUME; +} + +static int notify_consume(knot_layer_t *layer, knot_pkt_t *pkt) +{ + return KNOT_STATE_DONE; +} + +static const knot_layer_api_t NOTIFY_API = { + .begin = notify_begin, + .produce = notify_produce, + .consume = notify_consume, +}; + +#define NOTIFY_LOG(priority, zone, remote, fmt, ...) \ + ns_log(priority, zone, LOG_OPERATION_NOTIFY, LOG_DIRECTION_OUT, remote, \ + fmt, ## __VA_ARGS__) + +static int send_notify(conf_t *conf, zone_t *zone, const knot_rrset_t *soa, + const conf_remote_t *slave, int timeout) +{ + struct notify_data data = { + .zone = zone->name, + .soa = soa, + .remote = (struct sockaddr *)&slave->addr, + }; + + query_edns_data_init(&data.edns, conf, zone->name, slave->addr.ss_family); + + struct knot_requestor requestor = { 0 }; + knot_requestor_init(&requestor, &NOTIFY_API, &data, NULL); + + knot_pkt_t *pkt = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, NULL); + if (!pkt) { + knot_requestor_clear(&requestor); + return KNOT_ENOMEM; + } + + const struct sockaddr *dst = (struct sockaddr *)&slave->addr; + const struct sockaddr *src = (struct sockaddr *)&slave->via; + struct knot_request *req = knot_request_make(NULL, dst, src, pkt, &slave->key, 0); + if (!req) { + knot_request_free(req, NULL); + knot_requestor_clear(&requestor); + return KNOT_ENOMEM; + } + + int ret = knot_requestor_exec(&requestor, req, timeout); + + if (ret == KNOT_EOK) { + NOTIFY_LOG(LOG_INFO, zone->name, dst, + "serial %u", knot_soa_serial(soa->rrs.rdata)); + } else if (knot_pkt_ext_rcode(req->resp) == 0) { + NOTIFY_LOG(LOG_WARNING, zone->name, dst, + "failed (%s)", knot_strerror(ret)); + } else { + NOTIFY_LOG(LOG_WARNING, zone->name, dst, + "server responded with error '%s'", + knot_pkt_ext_rcode_name(req->resp)); + } + + knot_request_free(req, NULL); + knot_requestor_clear(&requestor); + + return ret; +} + +int event_notify(conf_t *conf, zone_t *zone) +{ + assert(zone); + + if (zone_contents_is_empty(zone->contents)) { + return KNOT_EOK; + } + + // NOTIFY content + int timeout = conf->cache.srv_tcp_reply_timeout * 1000; + knot_rrset_t soa = node_rrset(zone->contents->apex, KNOT_RRTYPE_SOA); + + // send NOTIFY to each remote, use working address + conf_val_t notify = conf_zone_get(conf, C_NOTIFY, zone->name); + while (notify.code == KNOT_EOK) { + conf_val_t addr = conf_id_get(conf, C_RMT, C_ADDR, ¬ify); + size_t addr_count = conf_val_count(&addr); + + for (int i = 0; i < addr_count; i++) { + conf_remote_t slave = conf_remote(conf, ¬ify, i); + int ret = send_notify(conf, zone, &soa, &slave, timeout); + if (ret == KNOT_EOK) { + break; + } + } + + conf_val_next(¬ify); + } + + return KNOT_EOK; +} diff --git a/src/knot/events/handlers/nsec3resalt.c b/src/knot/events/handlers/nsec3resalt.c new file mode 100644 index 0000000..479e0b1 --- /dev/null +++ b/src/knot/events/handlers/nsec3resalt.c @@ -0,0 +1,46 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "knot/dnssec/context.h" +#include "knot/dnssec/zone-events.h" + +int event_nsec3resalt(conf_t *conf, zone_t *zone) +{ + bool salt_changed = false; + knot_time_t next_resalt = 0; + + kdnssec_ctx_t kctx = { 0 }; + + int ret = kdnssec_ctx_init(conf, &kctx, zone->name, NULL); + if (ret != KNOT_EOK) { + return ret; + } + + ret = knot_dnssec_nsec3resalt(&kctx, &salt_changed, &next_resalt); + + if (ret == KNOT_EOK && salt_changed) { + zone_events_schedule_now(zone, ZONE_EVENT_DNSSEC); + zone->timers.last_resalt = kctx.now; + } + + kdnssec_ctx_deinit(&kctx); + + if (next_resalt) { + zone_events_schedule_at(zone, ZONE_EVENT_NSEC3RESALT, next_resalt); + } + + return ret; +} diff --git a/src/knot/events/handlers/parent_ds_query.c b/src/knot/events/handlers/parent_ds_query.c new file mode 100644 index 0000000..b16440c --- /dev/null +++ b/src/knot/events/handlers/parent_ds_query.c @@ -0,0 +1,53 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "knot/dnssec/ds_query.h" +#include "knot/zone/zone.h" + +int event_parent_ds_q(conf_t *conf, zone_t *zone) +{ + kdnssec_ctx_t ctx = { 0 }; + + int ret = kdnssec_ctx_init(conf, &ctx, zone->name, NULL); + if (ret != KNOT_EOK) { + return ret; + } + + zone_keyset_t keyset = { 0 }; + ret = load_zone_keys(&ctx, &keyset, false); + if (ret != KNOT_EOK) { + kdnssec_ctx_deinit(&ctx); + return ret; + } + + ret = knot_parent_ds_query(&ctx, &keyset, conf->cache.srv_tcp_reply_timeout * 1000); + + zone->timers.next_parent_ds_q = 0; + if (ret != KNOT_EOK) { + if (ctx.policy->ksk_sbm_check_interval > 0) { + time_t next_check = time(NULL) + ctx.policy->ksk_sbm_check_interval; + zone->timers.next_parent_ds_q = next_check; + zone_events_schedule_at(zone, ZONE_EVENT_PARENT_DS_Q, next_check); + } + } else { + zone_events_schedule_now(zone, ZONE_EVENT_DNSSEC); + } + + free_zone_keys(&keyset); + kdnssec_ctx_deinit(&ctx); + + return KNOT_EOK; // allways ok, if failure it has been rescheduled +} diff --git a/src/knot/events/handlers/refresh.c b/src/knot/events/handlers/refresh.c new file mode 100644 index 0000000..27aa2c4 --- /dev/null +++ b/src/knot/events/handlers/refresh.c @@ -0,0 +1,1169 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> +#include <stdint.h> + +#include "contrib/mempattern.h" +#include "libdnssec/random.h" +#include "knot/common/log.h" +#include "knot/conf/conf.h" +#include "knot/dnssec/zone-events.h" +#include "knot/events/handlers.h" +#include "knot/events/replan.h" +#include "knot/nameserver/ixfr.h" +#include "knot/query/layer.h" +#include "knot/query/query.h" +#include "knot/query/requestor.h" +#include "knot/updates/changesets.h" +#include "knot/zone/serial.h" +#include "knot/zone/zone.h" +#include "knot/zone/zonefile.h" +#include "libknot/errcode.h" + +/*! + * \brief Refresh event processing. + * + * The following diagram represents refresh event processing. + * + * \verbatim + * O + * | + * +-----v-----+ + * | BEGIN | + * +---+---+---+ + * has SOA | | no SOA + * +-------------------+ +------------------------------+ + * | | + * +------v------+ outdated +--------------+ error +-------v------+ + * | SOA query +------------> IXFR query +-----------> AXFR query | + * +-----+---+---+ +------+-------+ +----+----+----+ + * error | | current | success success | | error + * | +-----+ +---------------+ | | + * | | | +--------------------------------------+ | + * | | | | +----------+ +--------------+ + * | | | | | | | + * | +--v-v-v--+ | +--v--v--+ + * | | DONE | | | FAIL | + * | +---------+ | +--------+ + * +----------------------------+ + * + * \endverbatim + */ + +#define REFRESH_LOG(priority, zone, remote, msg...) \ + ns_log(priority, zone, LOG_OPERATION_REFRESH, LOG_DIRECTION_OUT, remote, msg) + +#define _XFRIN_LOG(priority, operation, zone, remote, msg...) \ + ns_log(priority, zone, operation, LOG_DIRECTION_IN, remote, msg) + +#define AXFRIN_LOG(priority, zone, remote, msg...) \ + _XFRIN_LOG(priority, LOG_OPERATION_AXFR, zone, remote, msg) + +#define IXFRIN_LOG(priority, zone, remote, msg...) \ + _XFRIN_LOG(priority, LOG_OPERATION_IXFR, zone, remote, msg) + +#define BOOTSTRAP_MAXTIME (24*60*60) +#define BOOTSTRAP_JITTER (30) + +enum state { + REFRESH_STATE_INVALID = 0, + STATE_SOA_QUERY, + STATE_TRANSFER, +}; + +enum xfr_type { + XFR_TYPE_ERROR = -1, + XFR_TYPE_UNDETERMINED = 0, + XFR_TYPE_UPTODATE, + XFR_TYPE_AXFR, + XFR_TYPE_IXFR, +}; + +struct refresh_data { + // transfer configuration, initialize appropriately: + + zone_t *zone; //!< Zone to eventually updated. + conf_t *conf; //!< Server configuration. + const struct sockaddr *remote; //!< Remote endpoint. + const knot_rrset_t *soa; //!< Local SOA (NULL for AXFR). + const size_t max_zone_size; //!< Maximal zone size. + struct query_edns_data edns; //!< EDNS data to be used in queries. + + // internal state, initialize with zeroes: + + enum state state; //!< Event processing state. + enum xfr_type xfr_type; //!< Transer type (mostly IXFR versus AXFR). + knot_rrset_t *initial_soa_copy; //!< Copy of the received initial SOA. + struct xfr_stats stats; //!< Transfer statistics. + size_t change_size; //!< Size of added and removed RRs. + + struct { + zone_contents_t *zone; //!< AXFR result, new zone. + } axfr; + + struct { + struct ixfr_proc *proc; //!< IXFR processing context. + knot_rrset_t *final_soa; //!< SOA denoting end of transfer. + list_t changesets; //!< IXFR result, zone updates. + } ixfr; + + bool updated; // TODO: Can we fid a better way to check if zone was updated? + knot_mm_t *mm; // TODO: This used to be used in IXFR. Remove or reuse. +}; + +static bool serial_is_current(uint32_t local_serial, uint32_t remote_serial) +{ + return (serial_compare(local_serial, remote_serial) & SERIAL_MASK_GEQ); +} + +static time_t bootstrap_next(const zone_timers_t *timers) +{ + time_t expired_at = timers->last_refresh + timers->soa_expire; + + // previous interval + time_t interval = timers->next_refresh - expired_at; + if (interval < 0) { + interval = 0; + } + + // exponential backoff + interval *= 2; + if (interval > BOOTSTRAP_MAXTIME) { + interval = BOOTSTRAP_MAXTIME; + } + + // prevent burst refresh + interval += dnssec_random_uint16_t() % BOOTSTRAP_JITTER; + + return interval; +} + +static int xfr_validate(zone_contents_t *zone, struct refresh_data *data) +{ + sem_handler_t handler = { + .cb = err_handler_logger + }; + + int ret = sem_checks_process(zone, false, &handler, time(NULL)); + if (ret != KNOT_EOK) { + // error is logged by the error handler + return ret; + } + + if (zone->size > data->max_zone_size) { + ns_log(LOG_WARNING, data->zone->name, + data->xfr_type == XFR_TYPE_IXFR ? LOG_OPERATION_IXFR : LOG_OPERATION_AXFR, + LOG_DIRECTION_IN, data->remote, "zone size exceeded"); + return KNOT_EZONESIZE; + } + + return KNOT_EOK; +} + +static void xfr_log_publish(const knot_dname_t *zone_name, + const struct sockaddr *remote, + const uint32_t old_serial, + const uint32_t new_serial, + bool axfr_bootstrap) +{ + if (!axfr_bootstrap) { + REFRESH_LOG(LOG_INFO, zone_name, remote, + "zone updated, serial %u -> %u", + old_serial, new_serial); + } else { + REFRESH_LOG(LOG_INFO, zone_name, remote, + "zone updated, serial none -> %u", + new_serial); + } +} + +static int axfr_init(struct refresh_data *data) +{ + zone_contents_t *new_zone = zone_contents_new(data->zone->name); + if (new_zone == NULL) { + return KNOT_ENOMEM; + } + + data->axfr.zone = new_zone; + return KNOT_EOK; +} + +static void axfr_cleanup(struct refresh_data *data) +{ + zone_contents_deep_free(data->axfr.zone); + data->axfr.zone = NULL; +} + +static int axfr_finalize(struct refresh_data *data) +{ + zone_contents_t *new_zone = data->axfr.zone; + + int ret = zone_contents_adjust_full(new_zone); + if (ret != KNOT_EOK) { + return ret; + } + + ret = xfr_validate(new_zone, data); + if (ret != KNOT_EOK) { + return ret; + } + + uint32_t master_serial = zone_contents_serial(new_zone); + uint32_t local_serial = zone_contents_serial(data->zone->contents); + bool have_lastsigned = zone_get_lastsigned_serial(data->zone, &local_serial); + uint32_t old_serial = local_serial; + bool bootstrap = (data->soa == NULL); + if ((!bootstrap || have_lastsigned) && + (serial_compare(master_serial, local_serial) != SERIAL_GREATER)) { + conf_val_t val = conf_zone_get(data->conf, C_SERIAL_POLICY, data->zone->name); + local_serial = serial_next(local_serial, conf_opt(&val)); + zone_contents_set_soa_serial(new_zone, local_serial); + } + + zone_update_t up = { 0 }; + ret = zone_update_from_contents(&up, data->zone, new_zone, UPDATE_FULL); + if (ret != KNOT_EOK) { + return ret; + } + + // Seized by zone_update. Don't free the contents again in axfr_cleanup. + data->axfr.zone = NULL; + + conf_val_t val = conf_zone_get(data->conf, C_DNSSEC_SIGNING, data->zone->name); + bool dnssec_enable = conf_bool(&val); + if (dnssec_enable) { + zone_sign_reschedule_t resch = { .allow_rollover = true }; + ret = knot_dnssec_zone_sign(&up, ZONE_SIGN_KEEP_SERIAL, &resch); + if (ret != KNOT_EOK) { + zone_update_clear(&up); + return ret; + } + event_dnssec_reschedule(data->conf, data->zone, &resch, true); + } + + ret = zone_update_commit(data->conf, &up); + zone_update_clear(&up); + if (ret != KNOT_EOK) { + return ret; + } + + if (dnssec_enable) { + ret = zone_set_master_serial(data->zone, master_serial); + if (ret == KNOT_EOK) { + ret = zone_set_lastsigned_serial(data->zone, zone_contents_serial(new_zone)); + } + if (ret != KNOT_EOK) { + log_zone_warning(data->zone->name, + "unable to save master serial, future transfers might be broken"); + } + } + + xfr_log_publish(data->zone->name, data->remote, old_serial, + zone_contents_serial(new_zone), bootstrap); + + return KNOT_EOK; +} + +static int axfr_consume_rr(const knot_rrset_t *rr, struct refresh_data *data) +{ + assert(rr); + assert(data); + assert(data->axfr.zone); + + // zc is stateless structure which can be initialized for each rr + // the changes are stored only in data->axfr.zone (aka zc.z) + zcreator_t zc = { + .z = data->axfr.zone, + .master = false, + .ret = KNOT_EOK + }; + + if (rr->type == KNOT_RRTYPE_SOA && + node_rrtype_exists(zc.z->apex, KNOT_RRTYPE_SOA)) { + return KNOT_STATE_DONE; + } + + int ret = zcreator_step(&zc, rr); + if (ret != KNOT_EOK) { + return KNOT_STATE_FAIL; + } + + data->change_size += knot_rrset_size(rr); + if (data->change_size > data->max_zone_size) { + AXFRIN_LOG(LOG_WARNING, data->zone->name, data->remote, + "zone size exceeded"); + return KNOT_STATE_FAIL; + } + + return KNOT_STATE_CONSUME; +} + +static int axfr_consume_packet(knot_pkt_t *pkt, struct refresh_data *data) +{ + assert(pkt); + assert(data); + + const knot_pktsection_t *answer = knot_pkt_section(pkt, KNOT_ANSWER); + int ret = KNOT_STATE_CONSUME; + for (uint16_t i = 0; i < answer->count && ret == KNOT_STATE_CONSUME; ++i) { + ret = axfr_consume_rr(knot_pkt_rr(answer, i), data); + } + return ret; +} + +static int axfr_consume(knot_pkt_t *pkt, struct refresh_data *data) +{ + assert(pkt); + assert(data); + + // Check RCODE + if (knot_pkt_ext_rcode(pkt) != KNOT_RCODE_NOERROR) { + AXFRIN_LOG(LOG_WARNING, data->zone->name, data->remote, + "server responded with error '%s'", + knot_pkt_ext_rcode_name(pkt)); + return KNOT_STATE_FAIL; + } + + // Initialize with first packet + if (data->axfr.zone == NULL) { + int ret = axfr_init(data); + if (ret != KNOT_EOK) { + AXFRIN_LOG(LOG_WARNING, data->zone->name, data->remote, + "failed to initialize (%s)", + knot_strerror(ret)); + return KNOT_STATE_FAIL; + } + + AXFRIN_LOG(LOG_INFO, data->zone->name, data->remote, "starting"); + xfr_stats_begin(&data->stats); + data->change_size = 0; + } + + int next; + // Process saved SOA if fallback from IXFR + if (data->initial_soa_copy != NULL) { + next = axfr_consume_rr(data->initial_soa_copy, data); + knot_rrset_free(data->initial_soa_copy, data->mm); + data->initial_soa_copy = NULL; + if (next != KNOT_STATE_CONSUME) { + return next; + } + } + + // Process answer packet + xfr_stats_add(&data->stats, pkt->size); + next = axfr_consume_packet(pkt, data); + + // Finalize + if (next == KNOT_STATE_DONE) { + xfr_stats_end(&data->stats); + } + + return next; +} + +/*! \brief Initialize IXFR-in processing context. */ +static int ixfr_init(struct refresh_data *data) +{ + struct ixfr_proc *proc = mm_alloc(data->mm, sizeof(*proc)); + if (proc == NULL) { + return KNOT_ENOMEM; + } + + memset(proc, 0, sizeof(struct ixfr_proc)); + proc->state = IXFR_START; + proc->mm = data->mm; + + data->ixfr.proc = proc; + data->ixfr.final_soa = NULL; + + init_list(&data->ixfr.changesets); + + return KNOT_EOK; +} + +/*! \brief Clean up data allocated by IXFR-in processing. */ +static void ixfr_cleanup(struct refresh_data *data) +{ + if (data->ixfr.proc == NULL) { + return; + } + + knot_rrset_free(data->ixfr.final_soa, data->mm); + data->ixfr.final_soa = NULL; + mm_free(data->mm, data->ixfr.proc); + data->ixfr.proc = NULL; + + changesets_free(&data->ixfr.changesets); +} + +static int ixfr_finalize(struct refresh_data *data) +{ + uint32_t master_serial; + int ret = zone_get_master_serial(data->zone, &master_serial); + if (ret != KNOT_EOK) { + log_zone_error(data->zone->name, "Failed reading master's serial" + "from KASP DB (%s)", knot_strerror(ret)); + return ret; + } + uint32_t local_serial = zone_contents_serial(data->zone->contents); + uint32_t lastsigned_serial = local_serial; + bool have_lastsigned = zone_get_lastsigned_serial(data->zone, &lastsigned_serial); + uint32_t old_serial = local_serial; + changeset_t *chs = NULL; + WALK_LIST(chs, data->ixfr.changesets) { + master_serial = knot_soa_serial(chs->soa_to->rrs.rdata); + knot_soa_serial_set(chs->soa_from->rrs.rdata, local_serial); + if (have_lastsigned && (serial_compare(lastsigned_serial, local_serial) & SERIAL_MASK_GEQ)) { + local_serial = lastsigned_serial; + } + if (serial_compare(master_serial, local_serial) != SERIAL_GREATER) { + conf_val_t val = conf_zone_get(data->conf, C_SERIAL_POLICY, data->zone->name); + local_serial = serial_next(local_serial, conf_opt(&val)); + knot_soa_serial_set(chs->soa_to->rrs.rdata, local_serial); + } else { + local_serial = master_serial; + } + } + + zone_update_t up = { 0 }; + ret = zone_update_init(&up, data->zone, UPDATE_INCREMENTAL | UPDATE_STRICT); + if (ret != KNOT_EOK) { + return ret; + } + + changeset_t *set = NULL; + WALK_LIST(set, data->ixfr.changesets) { + ret = zone_update_apply_changeset(&up, set); + if (ret != KNOT_EOK) { + zone_update_clear(&up); + IXFRIN_LOG(LOG_WARNING, data->zone->name, data->remote, + "failed to apply changes to zone (%s)", + knot_strerror(ret)); + return ret; + } + } + + ret = xfr_validate(up.new_cont, data); + if (ret != KNOT_EOK) { + zone_update_clear(&up); + return ret; + } + + conf_val_t val = conf_zone_get(data->conf, C_DNSSEC_SIGNING, data->zone->name); + bool dnssec_enable = conf_bool(&val); + if (dnssec_enable) { + zone_sign_reschedule_t resch = { .allow_rollover = true }; + ret = knot_dnssec_sign_update(&up, &resch); + if (ret != KNOT_EOK) { + zone_update_clear(&up); + return ret; + } + event_dnssec_reschedule(data->conf, data->zone, &resch, true); + } + + ret = zone_update_commit(data->conf, &up); + + if (ret == KNOT_EOK) { + if (dnssec_enable && !EMPTY_LIST(data->ixfr.changesets)) { + ret = zone_set_master_serial(data->zone, master_serial); + if (ret == KNOT_EOK) { + ret = zone_set_lastsigned_serial(data->zone, + zone_contents_serial(up.zone->contents)); + } + if (ret != KNOT_EOK) { + log_zone_warning(data->zone->name, + "unable to save master serial, future transfers might be broken"); + } + } + xfr_log_publish(data->zone->name, data->remote, old_serial, + zone_contents_serial(up.zone->contents), false); + } else { + IXFRIN_LOG(LOG_WARNING, data->zone->name, data->remote, + "failed to store changes (%s)", knot_strerror(ret)); + } + + zone_update_clear(&up); + return ret; +} + +/*! \brief Stores starting SOA into changesets structure. */ +static int ixfr_solve_start(const knot_rrset_t *rr, struct refresh_data *data) +{ + assert(data->ixfr.final_soa == NULL); + if (rr->type != KNOT_RRTYPE_SOA) { + return KNOT_EMALF; + } + + // Store terminal SOA + data->ixfr.final_soa = knot_rrset_copy(rr, data->mm); + if (data->ixfr.final_soa == NULL) { + return KNOT_ENOMEM; + } + + // Initialize list for changes + init_list(&data->ixfr.changesets); + + return KNOT_EOK; +} + +/*! \brief Decides what to do with a starting SOA (deletions). */ +static int ixfr_solve_soa_del(const knot_rrset_t *rr, struct refresh_data *data) +{ + if (rr->type != KNOT_RRTYPE_SOA) { + return KNOT_EMALF; + } + + // Create new changeset. + changeset_t *change = changeset_new(data->zone->name); + if (change == NULL) { + return KNOT_ENOMEM; + } + + // Store SOA into changeset. + change->soa_from = knot_rrset_copy(rr, NULL); + if (change->soa_from == NULL) { + changeset_free(change); + return KNOT_ENOMEM; + } + + // Add changeset. + add_tail(&data->ixfr.changesets, &change->n); + + return KNOT_EOK; +} + +/*! \brief Stores ending SOA into changeset. */ +static int ixfr_solve_soa_add(const knot_rrset_t *rr, changeset_t *change, knot_mm_t *mm) +{ + if (rr->type != KNOT_RRTYPE_SOA) { + return KNOT_EMALF; + } + + change->soa_to = knot_rrset_copy(rr, NULL); + if (change->soa_to == NULL) { + return KNOT_ENOMEM; + } + + return KNOT_EOK; +} + +/*! \brief Adds single RR into remove section of changeset. */ +static int ixfr_solve_del(const knot_rrset_t *rr, changeset_t *change, knot_mm_t *mm) +{ + return changeset_add_removal(change, rr, 0); +} + +/*! \brief Adds single RR into add section of changeset. */ +static int ixfr_solve_add(const knot_rrset_t *rr, changeset_t *change, knot_mm_t *mm) +{ + return changeset_add_addition(change, rr, 0); +} + +/*! \brief Decides what the next IXFR-in state should be. */ +static int ixfr_next_state(struct refresh_data *data, const knot_rrset_t *rr) +{ + const bool soa = (rr->type == KNOT_RRTYPE_SOA); + enum ixfr_state state = data->ixfr.proc->state; + + if ((state == IXFR_SOA_ADD || state == IXFR_ADD) && + knot_rrset_equal(rr, data->ixfr.final_soa, KNOT_RRSET_COMPARE_WHOLE)) { + return IXFR_DONE; + } + + switch (state) { + case IXFR_START: + // Final SOA already stored or transfer start. + return data->ixfr.final_soa ? IXFR_SOA_DEL : IXFR_START; + case IXFR_SOA_DEL: + // Empty delete section or start of delete section. + return soa ? IXFR_SOA_ADD : IXFR_DEL; + case IXFR_SOA_ADD: + // Empty add section or start of add section. + return soa ? IXFR_SOA_DEL : IXFR_ADD; + case IXFR_DEL: + // End of delete section or continue. + return soa ? IXFR_SOA_ADD : IXFR_DEL; + case IXFR_ADD: + // End of add section or continue. + return soa ? IXFR_SOA_DEL : IXFR_ADD; + default: + assert(0); + return IXFR_INVALID; + } +} + +/*! + * \brief Processes single RR according to current IXFR-in state. The states + * correspond with IXFR-in message structure, in the order they are + * mentioned in the code. + * + * \param rr RR to process. + * \param proc Processing context. + * + * \return KNOT_E* + */ +static int ixfr_step(const knot_rrset_t *rr, struct refresh_data *data) +{ + data->ixfr.proc->state = ixfr_next_state(data, rr); + changeset_t *change = TAIL(data->ixfr.changesets); + + switch (data->ixfr.proc->state) { + case IXFR_START: + return ixfr_solve_start(rr, data); + case IXFR_SOA_DEL: + return ixfr_solve_soa_del(rr, data); + case IXFR_DEL: + return ixfr_solve_del(rr, change, data->mm); + case IXFR_SOA_ADD: + return ixfr_solve_soa_add(rr, change, data->mm); + case IXFR_ADD: + return ixfr_solve_add(rr, change, data->mm); + case IXFR_DONE: + return KNOT_EOK; + default: + return KNOT_ERROR; + } +} + +static int ixfr_consume_rr(const knot_rrset_t *rr, struct refresh_data *data) +{ + if (knot_dname_in_bailiwick(rr->owner, data->zone->name) < 0) { + return KNOT_STATE_CONSUME; + } + + int ret = ixfr_step(rr, data); + if (ret != KNOT_EOK) { + IXFRIN_LOG(LOG_WARNING, data->zone->name, data->remote, + "failed (%s)", knot_strerror(ret)); + return KNOT_STATE_FAIL; + } + + data->change_size += knot_rrset_size(rr); + if (data->change_size / 2 > data->max_zone_size) { + IXFRIN_LOG(LOG_WARNING, data->zone->name, data->remote, + "transfer size exceeded"); + return KNOT_STATE_FAIL; + } + + if (data->ixfr.proc->state == IXFR_DONE) { + return KNOT_STATE_DONE; + } + + return KNOT_STATE_CONSUME; +} + +/*! + * \brief Processes IXFR reply packet and fills in the changesets structure. + * + * \param pkt Packet containing the IXFR reply in wire format. + * \param adata Answer data, including processing context. + * + * \return KNOT_STATE_CONSUME, KNOT_STATE_DONE, KNOT_STATE_FAIL + */ +static int ixfr_consume_packet(knot_pkt_t *pkt, struct refresh_data *data) +{ + // Process RRs in the message. + const knot_pktsection_t *answer = knot_pkt_section(pkt, KNOT_ANSWER); + int ret = KNOT_STATE_CONSUME; + for (uint16_t i = 0; i < answer->count && ret == KNOT_STATE_CONSUME; ++i) { + ret = ixfr_consume_rr(knot_pkt_rr(answer, i), data); + } + return ret; +} + +static enum xfr_type determine_xfr_type(const knot_pktsection_t *answer, + uint32_t zone_serial, const knot_rrset_t *initial_soa) +{ + if (answer->count < 1) { + return XFR_TYPE_ERROR; + } + + const knot_rrset_t *rr_one = knot_pkt_rr(answer, 0); + if (initial_soa != NULL) { + if (rr_one->type == KNOT_RRTYPE_SOA) { + return knot_rrset_equal(initial_soa, rr_one, KNOT_RRSET_COMPARE_WHOLE) ? + XFR_TYPE_AXFR : XFR_TYPE_IXFR; + } + return XFR_TYPE_AXFR; + } + + if (answer->count == 1) { + if (rr_one->type == KNOT_RRTYPE_SOA) { + return serial_is_current(zone_serial, knot_soa_serial(rr_one->rrs.rdata)) ? + XFR_TYPE_UPTODATE : XFR_TYPE_UNDETERMINED; + } + return XFR_TYPE_ERROR; + } + + const knot_rrset_t *rr_two = knot_pkt_rr(answer, 1); + if (answer->count == 2 && rr_one->type == KNOT_RRTYPE_SOA && + knot_rrset_equal(rr_one, rr_two, KNOT_RRSET_COMPARE_WHOLE)) { + return XFR_TYPE_AXFR; + } + + return (rr_one->type == KNOT_RRTYPE_SOA && rr_two->type != KNOT_RRTYPE_SOA) ? + XFR_TYPE_AXFR : XFR_TYPE_IXFR; +} + +static int ixfr_consume(knot_pkt_t *pkt, struct refresh_data *data) +{ + assert(pkt); + assert(data); + + // Check RCODE + if (knot_pkt_ext_rcode(pkt) != KNOT_RCODE_NOERROR) { + IXFRIN_LOG(LOG_WARNING, data->zone->name, data->remote, + "server responded with error '%s'", + knot_pkt_ext_rcode_name(pkt)); + return KNOT_STATE_FAIL; + } + + // Initialize with first packet + if (data->ixfr.proc == NULL) { + const knot_pktsection_t *answer = knot_pkt_section(pkt, KNOT_ANSWER); + + uint32_t master_serial; + int ret = zone_get_master_serial(data->zone, &master_serial); + if (ret != KNOT_EOK) { + log_zone_error(data->zone->name, "Failed reading master's serial" + "from KASP DB (%s)", knot_strerror(ret)); + return KNOT_STATE_FAIL; + } + data->xfr_type = determine_xfr_type(answer, master_serial, + data->initial_soa_copy); + switch (data->xfr_type) { + case XFR_TYPE_ERROR: + IXFRIN_LOG(LOG_WARNING, data->zone->name, data->remote, + "malformed response SOA"); + return KNOT_STATE_FAIL; + case XFR_TYPE_UNDETERMINED: + // Store the SOA and check with next packet + data->initial_soa_copy = knot_rrset_copy(knot_pkt_rr(answer, 0), data->mm); + if (data->initial_soa_copy == NULL) { + return KNOT_STATE_FAIL; + } + xfr_stats_add(&data->stats, pkt->size); + return KNOT_STATE_CONSUME; + case XFR_TYPE_AXFR: + IXFRIN_LOG(LOG_INFO, data->zone->name, data->remote, + "receiving AXFR-style IXFR"); + return axfr_consume(pkt, data); + case XFR_TYPE_UPTODATE: + IXFRIN_LOG(LOG_INFO, data->zone->name, data->remote, + "zone is up-to-date"); + xfr_stats_begin(&data->stats); + xfr_stats_add(&data->stats, pkt->size); + xfr_stats_end(&data->stats); + return KNOT_STATE_DONE; + case XFR_TYPE_IXFR: + break; + default: + assert(0); + return KNOT_STATE_FAIL; + } + + ret = ixfr_init(data); + if (ret != KNOT_EOK) { + IXFRIN_LOG(LOG_WARNING, data->zone->name, data->remote, + "failed to initialize (%s)", knot_strerror(ret)); + return KNOT_STATE_FAIL; + } + + IXFRIN_LOG(LOG_INFO, data->zone->name, data->remote, "starting"); + xfr_stats_begin(&data->stats); + data->change_size = 0; + } + + int next; + // Process saved SOA if existing + if (data->initial_soa_copy != NULL) { + next = ixfr_consume_rr(data->initial_soa_copy, data); + knot_rrset_free(data->initial_soa_copy, data->mm); + data->initial_soa_copy = NULL; + if (next != KNOT_STATE_CONSUME) { + return next; + } + } + + // Process answer packet + xfr_stats_add(&data->stats, pkt->size); + next = ixfr_consume_packet(pkt, data); + + // Finalize + if (next == KNOT_STATE_DONE) { + xfr_stats_end(&data->stats); + } + + return next; +} + +static int soa_query_produce(knot_layer_t *layer, knot_pkt_t *pkt) +{ + struct refresh_data *data = layer->data; + + query_init_pkt(pkt); + + int r = knot_pkt_put_question(pkt, data->zone->name, KNOT_CLASS_IN, KNOT_RRTYPE_SOA); + if (r != KNOT_EOK) { + return KNOT_STATE_FAIL; + } + + r = query_put_edns(pkt, &data->edns); + if (r != KNOT_EOK) { + return KNOT_STATE_FAIL; + } + + return KNOT_STATE_CONSUME; +} + +static int soa_query_consume(knot_layer_t *layer, knot_pkt_t *pkt) +{ + struct refresh_data *data = layer->data; + + if (knot_pkt_ext_rcode(pkt) != KNOT_RCODE_NOERROR) { + REFRESH_LOG(LOG_WARNING, data->zone->name, data->remote, + "server responded with error '%s'", + knot_pkt_ext_rcode_name(pkt)); + return KNOT_STATE_FAIL; + } + + const knot_pktsection_t *answer = knot_pkt_section(pkt, KNOT_ANSWER); + const knot_rrset_t *rr = answer->count == 1 ? knot_pkt_rr(answer, 0) : NULL; + if (!rr || rr->type != KNOT_RRTYPE_SOA || rr->rrs.count != 1) { + REFRESH_LOG(LOG_WARNING, data->zone->name, data->remote, + "malformed message"); + return KNOT_STATE_FAIL; + } + + uint32_t local_serial; + int ret = zone_get_master_serial(data->zone, &local_serial); + if (ret != KNOT_EOK) { + log_zone_error(data->zone->name, "Failed reading master's serial" + "from KASP DB (%s)", knot_strerror(ret)); + return KNOT_STATE_FAIL; + } + uint32_t remote_serial = knot_soa_serial(rr->rrs.rdata); + bool current = serial_is_current(local_serial, remote_serial); + bool master_uptodate = serial_is_current(remote_serial, local_serial); + + REFRESH_LOG(LOG_INFO, data->zone->name, data->remote, + "remote serial %u, %s", remote_serial, + current ? (master_uptodate ? "zone is up-to-date" : + "master is outdated") : "zone is outdated"); + + if (current) { + return master_uptodate ? KNOT_STATE_DONE : KNOT_STATE_FAIL; + } else { + data->state = STATE_TRANSFER; + return KNOT_STATE_RESET; + } +} + +static int transfer_produce(knot_layer_t *layer, knot_pkt_t *pkt) +{ + struct refresh_data *data = layer->data; + + bool ixfr = (data->xfr_type == XFR_TYPE_IXFR); + + query_init_pkt(pkt); + knot_pkt_put_question(pkt, data->zone->name, KNOT_CLASS_IN, + ixfr ? KNOT_RRTYPE_IXFR : KNOT_RRTYPE_AXFR); + + if (ixfr) { + assert(data->soa); + knot_rrset_t *sending_soa = knot_rrset_copy(data->soa, data->mm); + uint32_t master_serial; + int ret = zone_get_master_serial(data->zone, &master_serial); + if (ret != KNOT_EOK) { + log_zone_error(data->zone->name, "Failed reading master's serial" + "from KASP DB (%s)", knot_strerror(ret)); + } + if (sending_soa == NULL || ret != KNOT_EOK) { + knot_rrset_free(sending_soa, data->mm); + return KNOT_STATE_FAIL; + } + knot_soa_serial_set(sending_soa->rrs.rdata, master_serial); + knot_pkt_begin(pkt, KNOT_AUTHORITY); + knot_pkt_put(pkt, KNOT_COMPR_HINT_QNAME, sending_soa, 0); + knot_rrset_free(sending_soa, data->mm); + } + + query_put_edns(pkt, &data->edns); + + return KNOT_STATE_CONSUME; +} + +static int transfer_consume(knot_layer_t *layer, knot_pkt_t *pkt) +{ + struct refresh_data *data = layer->data; + + int next = (data->xfr_type == XFR_TYPE_AXFR) ? axfr_consume(pkt, data) : + ixfr_consume(pkt, data); + + // Transfer completed + if (next == KNOT_STATE_DONE) { + // Log transfer even if we still can fail + xfr_log_finished(data->zone->name, + data->xfr_type == XFR_TYPE_IXFR || + data->xfr_type == XFR_TYPE_UPTODATE ? + LOG_OPERATION_IXFR : LOG_OPERATION_AXFR, + LOG_DIRECTION_IN, data->remote, &data->stats); + + /* + * TODO: Move finialization into finish + * callback. And update requestor to allow reset from fallback + * as we need IXFR to AXFR failover. + */ + if (tsig_unsigned_count(layer->tsig) != 0) { + return KNOT_STATE_FAIL; + } + + // Finalize and publish the zone + int ret; + switch (data->xfr_type) { + case XFR_TYPE_IXFR: + ret = ixfr_finalize(data); + break; + case XFR_TYPE_AXFR: + ret = axfr_finalize(data); + break; + default: + return next; + } + if (ret == KNOT_EOK) { + data->updated = true; + } else { + next = KNOT_STATE_FAIL; + } + } + + return next; +} + +static int refresh_begin(knot_layer_t *layer, void *_data) +{ + layer->data = _data; + struct refresh_data *data = _data; + + if (data->soa) { + data->state = STATE_SOA_QUERY; + data->xfr_type = XFR_TYPE_IXFR; + data->initial_soa_copy = NULL; + } else { + data->state = STATE_TRANSFER; + data->xfr_type = XFR_TYPE_AXFR; + data->initial_soa_copy = NULL; + } + + return KNOT_STATE_PRODUCE; +} + +static int refresh_produce(knot_layer_t *layer, knot_pkt_t *pkt) +{ + struct refresh_data *data = layer->data; + + switch (data->state) { + case STATE_SOA_QUERY: return soa_query_produce(layer, pkt); + case STATE_TRANSFER: return transfer_produce(layer, pkt); + default: + return KNOT_STATE_FAIL; + } +} + +static int refresh_consume(knot_layer_t *layer, knot_pkt_t *pkt) +{ + struct refresh_data *data = layer->data; + + switch (data->state) { + case STATE_SOA_QUERY: return soa_query_consume(layer, pkt); + case STATE_TRANSFER: return transfer_consume(layer, pkt); + default: + return KNOT_STATE_FAIL; + } +} + +static int refresh_reset(knot_layer_t *layer) +{ + return KNOT_STATE_PRODUCE; +} + +static int refresh_finish(knot_layer_t *layer) +{ + struct refresh_data *data = layer->data; + + // clean processing context + axfr_cleanup(data); + ixfr_cleanup(data); + + return KNOT_STATE_NOOP; +} + +static const knot_layer_api_t REFRESH_API = { + .begin = refresh_begin, + .produce = refresh_produce, + .consume = refresh_consume, + .reset = refresh_reset, + .finish = refresh_finish, +}; + +static size_t max_zone_size(conf_t *conf, const knot_dname_t *zone) +{ + conf_val_t val = conf_zone_get(conf, C_MAX_ZONE_SIZE, zone); + return conf_int(&val); +} + +typedef struct { + bool force_axfr; + bool send_notify; +} try_refresh_ctx_t; + +static int try_refresh(conf_t *conf, zone_t *zone, const conf_remote_t *master, void *ctx) +{ + // TODO: Abstract interface to issue DNS queries. This is almost copy-pasted. + + assert(zone); + assert(master); + assert(ctx); + + try_refresh_ctx_t *trctx = ctx; + + knot_rrset_t soa = { 0 }; + if (zone->contents) { + soa = node_rrset(zone->contents->apex, KNOT_RRTYPE_SOA); + } + + struct refresh_data data = { + .zone = zone, + .conf = conf, + .remote = (struct sockaddr *)&master->addr, + .soa = zone->contents && !trctx->force_axfr ? &soa : NULL, + .max_zone_size = max_zone_size(conf, zone->name), + }; + + query_edns_data_init(&data.edns, conf, zone->name, master->addr.ss_family); + + struct knot_requestor requestor; + knot_requestor_init(&requestor, &REFRESH_API, &data, NULL); + + knot_pkt_t *pkt = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, NULL); + if (!pkt) { + knot_requestor_clear(&requestor); + return KNOT_ENOMEM; + } + + const struct sockaddr *dst = (struct sockaddr *)&master->addr; + const struct sockaddr *src = (struct sockaddr *)&master->via; + struct knot_request *req = knot_request_make(NULL, dst, src, pkt, &master->key, 0); + if (!req) { + knot_request_free(req, NULL); + knot_requestor_clear(&requestor); + return KNOT_ENOMEM; + } + + int timeout = conf->cache.srv_tcp_reply_timeout * 1000; + + int ret; + + // while loop runs 0x or 1x; IXFR to AXFR failover + while ((ret = knot_requestor_exec(&requestor, req, timeout)) != KNOT_EOK && + data.xfr_type == XFR_TYPE_IXFR) { + REFRESH_LOG(LOG_WARNING, data.zone->name, data.remote, + "fallback to AXFR"); + ixfr_cleanup(&data); + data.xfr_type = XFR_TYPE_AXFR; + requestor.layer.state = KNOT_STATE_RESET; + requestor.layer.flags |= KNOT_RQ_LAYER_CLOSE; + } + knot_request_free(req, NULL); + knot_requestor_clear(&requestor); + + if (ret == KNOT_EOK) { + trctx->send_notify = data.updated; + } + + return ret; +} + +static int64_t min_refresh_interval(conf_t *conf, const knot_dname_t *zone) +{ + conf_val_t val = conf_zone_get(conf, C_MIN_REFRESH_INTERVAL, zone); + return conf_int(&val); +} + +static int64_t max_refresh_interval(conf_t *conf, const knot_dname_t *zone) +{ + conf_val_t val = conf_zone_get(conf, C_MAX_REFRESH_INTERVAL, zone); + return conf_int(&val); +} + +int event_refresh(conf_t *conf, zone_t *zone) +{ + assert(zone); + + if (!zone_is_slave(conf, zone)) { + return KNOT_EOK; + } + + try_refresh_ctx_t trctx = { 0 }; + + // TODO: Flag on zone is ugly. Event specific parameters would be nice. + if (zone->flags & ZONE_FORCE_AXFR) { + zone->flags &= ~ZONE_FORCE_AXFR; + trctx.force_axfr = true; + } + + int ret = zone_master_try(conf, zone, try_refresh, &trctx, "refresh"); + if (ret != KNOT_EOK) { + log_zone_error(zone->name, "refresh, failed (%s)", knot_strerror(ret)); + } + + time_t now = time(NULL); + const knot_rdataset_t *soa = zone_soa(zone); + + if (ret == KNOT_EOK) { + zone->timers.soa_expire = knot_soa_expire(soa->rdata); + zone->timers.last_refresh = now; + zone->timers.next_refresh = now + knot_soa_refresh(soa->rdata); + } else { + time_t next = 0; + if (soa) { + next = knot_soa_retry(soa->rdata); + } else { + next = bootstrap_next(&zone->timers); + } + zone->timers.next_refresh = now + next; + } + + /* Check for allowed refresh interval limits. */ + int64_t min_refresh = min_refresh_interval(conf, zone->name); + if(zone->timers.next_refresh < now + min_refresh) { + zone->timers.next_refresh = now + min_refresh; + } + int64_t max_refresh = max_refresh_interval(conf, zone->name); + if(zone->timers.next_refresh > now + max_refresh) { + zone->timers.next_refresh = now + max_refresh; + } + + /* Rechedule events. */ + replan_from_timers(conf, zone); + if (trctx.send_notify) { + zone_events_schedule_at(zone, ZONE_EVENT_NOTIFY, time(NULL) + 1); + } + + return KNOT_EOK; +} diff --git a/src/knot/events/handlers/update.c b/src/knot/events/handlers/update.c new file mode 100644 index 0000000..6478364 --- /dev/null +++ b/src/knot/events/handlers/update.c @@ -0,0 +1,33 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> +#include <stdint.h> + +#include "knot/conf/conf.h" +#include "knot/nameserver/update.h" +#include "knot/zone/zone.h" +#include "libknot/errcode.h" + +int event_update(conf_t *conf, zone_t *zone) +{ + assert(zone); + + /* Process update list - forward if zone has master, or execute. */ + updates_execute(conf, zone); + + return KNOT_EOK; +} diff --git a/src/knot/events/replan.c b/src/knot/events/replan.c new file mode 100644 index 0000000..a3f6fcd --- /dev/null +++ b/src/knot/events/replan.c @@ -0,0 +1,175 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <assert.h> + +#include "knot/events/replan.h" + +#define TIME_CANCEL 0 +#define TIME_IGNORE (-1) + +/*! + * \brief Move DDNS queue from old zone to new zone and replan if necessary. + * + * New zone will contain references from the old zone. New zone will free + * the data. + */ +static void replan_ddns(zone_t *zone, zone_t *old_zone) +{ + assert(zone); + assert(old_zone); + + if (old_zone->ddns_queue_size == 0) { + return; + } + + ptrnode_t *node = NULL; + WALK_LIST(node, old_zone->ddns_queue) { + ptrlist_add(&zone->ddns_queue, node->d, NULL); + } + zone->ddns_queue_size = old_zone->ddns_queue_size; + + ptrlist_free(&old_zone->ddns_queue, NULL); + + zone_events_schedule_now(zone, ZONE_EVENT_UPDATE); +} + +/*! + * \brief Replan NOTIFY event if it was queued for the old zone. + */ +static void replan_notify(zone_t *zone, const zone_t *old_zone) +{ + assert(zone); + assert(old_zone); + + time_t notify = zone_events_get_time(old_zone, ZONE_EVENT_NOTIFY); + if (notify > 0) { + zone_events_schedule_at(zone, notify); + } +} + +/*! + * \brief Replan DNSSEC if automatic signing enabled. + * + * This is required as the configuration could have changed. + */ +static void replan_dnssec(conf_t *conf, zone_t *zone) +{ + assert(conf); + assert(zone); + + conf_val_t val = conf_zone_get(conf, C_DNSSEC_SIGNING, zone->name); + if (conf_bool(&val)) { + zone_events_schedule_now(zone, ZONE_EVENT_DNSSEC); + } +} + +static bool can_expire(const zone_t *zone) +{ + return zone->timers.soa_expire > 0 && !zone_expired(zone); +} + +/*! + * \brief Replan events that depend on zone timers (REFRESH, EXPIRE, FLUSH, RESALT, PARENT DS QUERY). + */ +void replan_from_timers(conf_t *conf, zone_t *zone) +{ + assert(conf); + assert(zone); + + time_t now = time(NULL); + + time_t refresh = TIME_CANCEL; + if (zone_is_slave(conf, zone)) { + refresh = zone->timers.next_refresh; + assert(refresh > 0); + } + + time_t expire_pre = TIME_IGNORE; + time_t expire = TIME_IGNORE; + if (zone_is_slave(conf, zone) && can_expire(zone)) { + expire_pre = TIME_CANCEL; + expire = zone->timers.last_refresh + zone->timers.soa_expire; + } + + time_t flush = TIME_IGNORE; + if (!zone_is_slave(conf, zone) || can_expire(zone)) { + conf_val_t val = conf_zone_get(conf, C_ZONEFILE_SYNC, zone->name); + int64_t sync_timeout = conf_int(&val); + if (sync_timeout > 0) { + flush = zone->timers.last_flush + sync_timeout; + } + } + + time_t resalt = TIME_CANCEL; + conf_val_t val = conf_zone_get(conf, C_DNSSEC_SIGNING, zone->name); + if (conf_bool(&val)) { + conf_val_t policy = conf_zone_get(conf, C_DNSSEC_POLICY, zone->name); + conf_id_fix_default(&policy); + val = conf_id_get(conf, C_POLICY, C_NSEC3, &policy); + if (conf_bool(&val)) { + if (zone->timers.last_resalt == 0) { + resalt = now; + } else { + val = conf_id_get(conf, C_POLICY, C_NSEC3_SALT_LIFETIME, &policy); + resalt = zone->timers.last_resalt + conf_int(&val); + } + } + } + + time_t ds = zone->timers.next_parent_ds_q; + if (ds == 0) { + ds = TIME_IGNORE; + } + + zone_events_schedule_at(zone, + ZONE_EVENT_REFRESH, refresh, + ZONE_EVENT_EXPIRE, expire_pre, + ZONE_EVENT_EXPIRE, expire, + ZONE_EVENT_FLUSH, flush, + ZONE_EVENT_NSEC3RESALT, resalt, + ZONE_EVENT_PARENT_DS_Q, ds); +} + +void replan_load_new(zone_t *zone) +{ + // enqueue directly, make first load waitable + // other events will cascade from load + zone_events_enqueue(zone, ZONE_EVENT_LOAD); +} + +void replan_load_bootstrap(conf_t *conf, zone_t *zone) +{ + replan_from_timers(conf, zone); +} + +void replan_load_current(conf_t *conf, zone_t *zone, zone_t *old_zone) +{ + replan_ddns(zone, old_zone); + replan_notify(zone, old_zone); + + replan_from_timers(conf, zone); + replan_dnssec(conf, zone); +} + +void replan_load_updated(zone_t *zone, zone_t *old_zone) +{ + replan_ddns(zone, old_zone); + replan_notify(zone, old_zone); + + // other events will cascade from load + zone_events_schedule_now(zone, ZONE_EVENT_LOAD); +} diff --git a/src/knot/events/replan.h b/src/knot/events/replan.h new file mode 100644 index 0000000..05e3112 --- /dev/null +++ b/src/knot/events/replan.h @@ -0,0 +1,35 @@ +/* Copyright (C) 2014 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include "knot/conf/conf.h" +#include "knot/zone/zone.h" + +/*! + * \brief Replan timer dependent refresh, expire, and flush. + */ +void replan_from_timers(conf_t *conf, zone_t *zone); + +/*! + * \defgroup replan_load Replan timers after zone load or reload. + * @{ + */ +void replan_load_new(zone_t *zone); +void replan_load_bootstrap(conf_t *conf, zone_t *zone); +void replan_load_current(conf_t *conf, zone_t *zone, zone_t *old_zone); +void replan_load_updated(zone_t *zone, zone_t *old_zone); +/*! @} */ diff --git a/src/knot/include/module.h b/src/knot/include/module.h new file mode 100644 index 0000000..b3ca878 --- /dev/null +++ b/src/knot/include/module.h @@ -0,0 +1,549 @@ +/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \file + * + * \brief Knot DNS module interface. + * + * \addtogroup module + * @{ + */ + +#pragma once + +#include <stdarg.h> +#include <stdint.h> +#include <syslog.h> +#include <sys/socket.h> + +#include <libknot/libknot.h> +#include <libknot/yparser/ypschema.h> + +/*** Query module API. ***/ + +/*! Current module ABI version. */ +#define KNOTD_MOD_ABI_VERSION 200 +/*! Module configuration name prefix. */ +#define KNOTD_MOD_NAME_PREFIX "mod-" + +/*! Configuration check function context. */ +typedef struct { + const yp_item_t *item; /*!< Current item descriptor. */ + const uint8_t *id; /*!< Current section identifier. */ + size_t id_len; /*!< Current section identifier length. */ + const uint8_t *data; /*!< Current item data. */ + size_t data_len; /*!< Current item data length. */ + const char *err_str; /*!< Output error message. */ + struct knotd_conf_check_extra *extra; /*!< Private items (conf/tools.h). */ +} knotd_conf_check_args_t; + +/*! Module context. */ +typedef struct knotd_mod knotd_mod_t; + +/*! + * Module load callback. + * + * Responsibilities: + * - Query processing hooks registration + * - Optional module specific context initialization + * - Module configuration processing + * - Query statistics counters registration + * + * \param[in] mod Module context. + * + * \return Error code, KNOT_EOK if success. + */ +typedef int (*knotd_mod_load_f)(knotd_mod_t *mod); + +/*! + * Module unload callback. + * + * Responsibilities: + * - Optional module specific context deinitialization + * + * \param[in] mod Module context. + */ +typedef void (*knotd_mod_unload_f)(knotd_mod_t *mod); + +/*! + * Module configuration section check callback. + * + * Responsibilities: + * - Optional module configuration section items checks. + * + * \note Set args.err_str to proper error message if error. + * + * \param[in] args Configuration check arguments. + * + * \return Error code, KNOT_EOK if success. + */ +typedef int (*knotd_conf_check_f)(knotd_conf_check_args_t *args); + +/*! Module flags. */ +typedef enum { + KNOTD_MOD_FLAG_NONE = 0, /*!< Unspecified. */ + KNOTD_MOD_FLAG_OPT_CONF = 1 << 0, /*!< Optional module configuration. */ + KNOTD_MOD_FLAG_SCOPE_GLOBAL = 1 << 1, /*!< Can be specified as global module. */ + KNOTD_MOD_FLAG_SCOPE_ZONE = 1 << 2, /*!< Can be specified as zone module. */ + KNOTD_MOD_FLAG_SCOPE_ANY = KNOTD_MOD_FLAG_SCOPE_GLOBAL | + KNOTD_MOD_FLAG_SCOPE_ZONE, +} knotd_mod_flag_t; + +/*! Module API. */ +typedef struct { + uint32_t version; /*!< Embedded version of the module ABI. */ + const char *name; /*!< Module name. */ + knotd_mod_flag_t flags; /*!< Module flags. */ + knotd_mod_load_f load; /*!< Module load callback. */ + knotd_mod_unload_f unload; /*!< Module unload callback. */ + const yp_item_t *config; /*!< Module configuration schema. */ + knotd_conf_check_f config_check; /*!< Module configuration check callback. */ +} knotd_mod_api_t; + +/*! Static module API symbol must have a unique name. */ +#ifdef KNOTD_MOD_STATIC + #define KNOTD_MOD_API_NAME(mod_name) knotd_mod_api_##mod_name +#else + #define KNOTD_MOD_API_NAME(mod_name) knotd_mod_api +#endif + +/*! Module API instance initialization helper macro. */ +#define KNOTD_MOD_API(mod_name, mod_flags, mod_load, mod_unload, mod_conf, mod_conf_check) \ + __attribute__((visibility("default"))) \ + const knotd_mod_api_t KNOTD_MOD_API_NAME(mod_name) = { \ + .version = KNOTD_MOD_ABI_VERSION, \ + .name = KNOTD_MOD_NAME_PREFIX #mod_name, \ + .flags = mod_flags, \ + .load = mod_load, \ + .unload = mod_unload, \ + .config = mod_conf, \ + .config_check = mod_conf_check, \ + } + +/*** Configuration, statistics, logging,... API. ***/ + +/*! + * Checks reference item (YP_TREF) value if the destination exists. + * + * \note This function is intended to be used in module schema. + * + * \param[in] args Configuration check arguments. + * + * \return Error code, KNOT_EOK if success. + */ +int knotd_conf_check_ref(knotd_conf_check_args_t *args); + +/*! + * Gets optional module context. + * + * \param[in] mod Module context. + * + * \return Pointer to optional module context. + */ +void *knotd_mod_ctx(knotd_mod_t *mod); + +/*! + * Sets optional module context. + * + * \param[in] mod Module context. + * \param[in] ctx Optional module context. + */ +void knotd_mod_ctx_set(knotd_mod_t *mod, void *ctx); + +/*! + * Gets the zone name the module is configured for. + * + * \param[in] mod Module context. + * + * \return Zone name. + */ +const knot_dname_t *knotd_mod_zone(knotd_mod_t *mod); + +/*! + * Emits a module specific log message. + * + * \param[in] mod Module context. + * \param[in] priority Message priority (LOG_DEBUG...LOG_CRIT). + * \param[in] fmt Content of the message. + */ +void knotd_mod_log(knotd_mod_t *mod, int priority, const char *fmt, ...); + +/*! + * Emits a module specific log message (va_list variant). + * + * \param[in] mod Module context. + * \param[in] priority Message priority (LOG_DEBUG...LOG_CRIT). + * \param[in] fmt Content of the message. + * \param[in] args Variable argument list. + */ +void knotd_mod_vlog(knotd_mod_t *mod, int priority, const char *fmt, va_list args); + +/*! + * Statistics multi-counter index to name transformation callback. + * + * \param[in] idx Multi-counter index. + * \param[in] idx_count Number of subcounters. + * + * \return Index name string. + */ +typedef char* (*knotd_mod_idx_to_str_f)(uint32_t idx, uint32_t idx_count); + +/*! + * Registers a statistics counter. + * + * \param[in] mod Module context. + * \param[in] ctr_name Counter name + * \param[in] idx_count Number of subcounters (set 1 for single-counter). + * \param[in] idx_to_str Subcounter index to name transformation callback + * (set NULL for single-counter). + * + * \return Error code, KNOT_EOK if success. + */ +int knotd_mod_stats_add(knotd_mod_t *mod, const char *ctr_name, uint32_t idx_count, + knotd_mod_idx_to_str_f idx_to_str); + +/*! + * Increments a statistics counter. + * + * \param[in] mod Module context. + * \param[in] ctr_id Counter id (counted in the order the counters were registered). + * \param[in] idx Subcounter index (set 0 for single-counter). + * \param[in] val Value increment. + */ +void knotd_mod_stats_incr(knotd_mod_t *mod, uint32_t ctr_id, uint32_t idx, uint64_t val); + +/*! + * Decrements a statistics counter. + * + * \param[in] mod Module context. + * \param[in] ctr_id Counter id (counted in the order the counters were registered). + * \param[in] idx Subcounter index (set 0 for single-counter). + * \param[in] val Value decrement. + */ +void knotd_mod_stats_decr(knotd_mod_t *mod, uint32_t ctr_id, uint32_t idx, uint64_t val); + +/*! + * Sets a statistics counter value. + * + * \param[in] mod Module context. + * \param[in] ctr_id Counter id (counted in the order the counters were registered). + * \param[in] idx Subcounter index (set 0 for single-counter). + * \param[in] val Value. + */ +void knotd_mod_stats_store(knotd_mod_t *mod, uint32_t ctr_id, uint32_t idx, uint64_t val); + +/*! Configuration single-value abstraction. */ +typedef union { + int64_t integer; + unsigned option; + bool boolean; + const char *string; + const knot_dname_t *dname; + struct { + struct sockaddr_storage addr; + struct sockaddr_storage addr_max; + int addr_mask; + }; + struct { + const uint8_t *data; + size_t data_len; + }; +} knotd_conf_val_t; + +/*! Configuration value. */ +typedef struct { + knotd_conf_val_t single; /*!< Single-valued item data. */ + knotd_conf_val_t *multi; /*!< Multi-valued item data. */ + size_t count; /*!< Number of items (0 if default single value). */ +} knotd_conf_t; + +/*! Environment items. */ +typedef enum { + KNOTD_CONF_ENV_VERSION = 0, /*!< Software version. */ + KNOTD_CONF_ENV_HOSTNAME = 1, /*!< Current hostname. */ + KNOTD_CONF_ENV_WORKERS_UDP = 2, /*!< Current number of UDP workers. */ + KNOTD_CONF_ENV_WORKERS_TCP = 3, /*!< Current number of TCP workers. */ +} knotd_conf_env_t; + +/*! + * Gets general configuration value. + * + * \param[in] mod Module context. + * \param[in] section_name Section name. + * \param[in] item_name Section item name. + * \param[in] id Section identifier (NULL for simple section). + * + * \return Configuration value. + */ +knotd_conf_t knotd_conf(knotd_mod_t *mod, const yp_name_t *section_name, + const yp_name_t *item_name, const knotd_conf_t *id); + +/*! + * Gets environment value. + * + * \param[in] mod Module context. + * \param[in] env Environment item. + * + * \return Configuration value. + */ +knotd_conf_t knotd_conf_env(knotd_mod_t *mod, knotd_conf_env_t env); + +/*! + * Gets module configuration value. + * + * \param[in] mod Module context. + * \param[in] item_name Module section item name. + * + * \return Configuration value. + */ +knotd_conf_t knotd_conf_mod(knotd_mod_t *mod, const yp_name_t *item_name); + +/*! + * Gets zone configuration value. + * + * \param[in] mod Module context. + * \param[in] item_name Zone section item name. + * \param[in] zone Zone name. + * + * \return Configuration value. + */ +knotd_conf_t knotd_conf_zone(knotd_mod_t *mod, const yp_name_t *item_name, + const knot_dname_t *zone); + +/*! + * Gets module configuration value during the checking phase. + * + * \note This function is intended to be used in 'knotd_conf_check_f' callbacks. + * + * \param[in] args + * \param[in] item_name + * + * \return Configuration value. + */ +knotd_conf_t knotd_conf_check_item(knotd_conf_check_args_t *args, + const yp_name_t *item_name); + +/*! + * \brief Checks if address is in at least one of given ranges. + * + * \param[in] range + * \param[in] addr + * + * \return true if addr is in at least one range, false otherwise. + */ +bool knotd_conf_addr_range_match(const knotd_conf_t *range, + const struct sockaddr_storage *addr); + +/*! + * Deallocates multi-valued configuration values. + * + * \param[in] conf Configuration value. + */ +void knotd_conf_free(knotd_conf_t *conf); + +/*** Query processing API. ***/ + +/*! + * DNS query type. + * + * This type encompasses the different query types distinguished by both the + * OPCODE and the QTYPE. + */ +typedef enum { + KNOTD_QUERY_TYPE_INVALID, /*!< Invalid query. */ + KNOTD_QUERY_TYPE_NORMAL, /*!< Normal query. */ + KNOTD_QUERY_TYPE_AXFR, /*!< Request for AXFR transfer. */ + KNOTD_QUERY_TYPE_IXFR, /*!< Request for IXFR transfer. */ + KNOTD_QUERY_TYPE_NOTIFY, /*!< NOTIFY query. */ + KNOTD_QUERY_TYPE_UPDATE, /*!< Dynamic update. */ +} knotd_query_type_t; + +/*! Query processing specific flags. */ +typedef enum { + KNOTD_QUERY_FLAG_NO_AXFR = 1 << 0, /*!< Don't process AXFR. */ + KNOTD_QUERY_FLAG_NO_IXFR = 1 << 1, /*!< Don't process IXFR. */ + KNOTD_QUERY_FLAG_LIMIT_ANY = 1 << 2, /*!< Limit ANY QTYPE (respond with TC=1). */ + KNOTD_QUERY_FLAG_LIMIT_SIZE = 1 << 3, /*!< Apply UDP size limit. */ + KNOTD_QUERY_FLAG_COOKIE = 1 << 4, /*!< Valid DNS Cookie indication. */ +} knotd_query_flag_t; + +/*! Query processing data context parameters. */ +typedef struct { + knotd_query_flag_t flags; /*!< Current query flgas. */ + const struct sockaddr_storage *remote; /*!< Current remote address. */ + int socket; /*!< Current network socket. */ + unsigned thread_id; /*!< Current thread id. */ + void *server; /*!< Server object private item. */ +} knotd_qdata_params_t; + +/*! Query processing data context. */ +typedef struct { + knot_pkt_t *query; /*!< Query to be solved. */ + knotd_query_type_t type; /*!< Query packet type. */ + const knot_dname_t *name; /*!< Currently processed name. */ + uint16_t rcode; /*!< Resulting RCODE (Whole extended RCODE). */ + uint16_t rcode_tsig; /*!< Resulting TSIG RCODE. */ + knot_rrset_t opt_rr; /*!< OPT record. */ + knot_sign_context_t sign; /*!< Signing context. */ + knot_edns_client_subnet_t *ecs; /*!< EDNS Client Subnet option. */ + bool err_truncated; /*!< Set TC bit if error reply. */ + + /*! Persistent items on processing reset. */ + knot_mm_t *mm; /*!< Memory context. */ + knotd_qdata_params_t *params; /*!< Low-level processing parameters. */ + + struct knotd_qdata_extra *extra; /*!< Private items (process_query.h). */ +} knotd_qdata_t; + +/*! + * Gets the current zone name. + * + * \param[in] qdata Query data. + * + * \return Zone name. + */ +const knot_dname_t *knotd_qdata_zone_name(knotd_qdata_t *qdata); + +/*! + * Gets the current zone apex rrset of the given type. + * + * \param[in] qdata Query data. + * \param[in] type Rrset type. + * + * \return A copy of the zone apex rrset. + */ +knot_rrset_t knotd_qdata_zone_apex_rrset(knotd_qdata_t *qdata, uint16_t type); + +/*! General query processing states. */ +typedef enum { + KNOTD_STATE_NOOP = 0, /*!< No response. */ + KNOTD_STATE_DONE = 4, /*!< Finished. */ + KNOTD_STATE_FAIL = 5, /*!< Error. */ + KNOTD_STATE_FINAL = 6, /*!< Finished and finalized (QNAME, EDNS, TSIG). */ +} knotd_state_t; + +/*! brief Internet query processing states. */ +typedef enum { + KNOTD_IN_STATE_BEGIN, /*!< Begin name resolution. */ + KNOTD_IN_STATE_NODATA, /*!< Positive result with NO data. */ + KNOTD_IN_STATE_HIT, /*!< Positive result. */ + KNOTD_IN_STATE_MISS, /*!< Negative result. */ + KNOTD_IN_STATE_DELEG, /*!< Result is delegation. */ + KNOTD_IN_STATE_FOLLOW, /*!< Resolution not complete (CNAME/DNAME chain). */ + KNOTD_IN_STATE_TRUNC, /*!< Finished, packet size limit encountered. */ + KNOTD_IN_STATE_ERROR, /*!< Resolution failed. */ +} knotd_in_state_t; + +/*! Query module processing stages. */ +typedef enum { + KNOTD_STAGE_BEGIN = 0, /*!< Before query processing. */ + KNOTD_STAGE_PREANSWER, /*!< Before section processing. */ + KNOTD_STAGE_ANSWER, /*!< Answer section processing. */ + KNOTD_STAGE_AUTHORITY, /*!< Authority section processing. */ + KNOTD_STAGE_ADDITIONAL, /*!< Additional section processing. */ + KNOTD_STAGE_END, /*!< After query processing. */ +} knotd_stage_t; + +/*! + * General processing hook. + * + * \param[in] state Current processing state. + * \param[in,out] pkt Response packet. + * \param[in] qdata Query data. + * \param[in] mod Module context. + * + * \return Next processing state. + */ +typedef knotd_state_t (*knotd_mod_hook_f) + (knotd_state_t state, knot_pkt_t *pkt, knotd_qdata_t *qdata, knotd_mod_t *mod); + +/*! + * Internet class processing hook. + * + * \param[in] state Current processing state. + * \param[in,out] pkt Response packet. + * \param[in] qdata Query data. + * \param[in] mod Module context. + * + * \return Next processing state. + */ +typedef knotd_in_state_t (*knotd_mod_in_hook_f) + (knotd_in_state_t state, knot_pkt_t *pkt, knotd_qdata_t *qdata, knotd_mod_t *mod); + +/*! + * Registers general processing module hook. + * + * \param[in] mod Module context. + * \param[in] stage Processing stage (KNOTD_STAGE_BEGIN or KNOTD_STAGE_END). + * \param[in] hook Module hook. + * + * \return Error code, KNOT_EOK if success. + */ +int knotd_mod_hook(knotd_mod_t *mod, knotd_stage_t stage, knotd_mod_hook_f hook); + +/*! + * Registers Internet class module hook. + * + * \param[in] mod Module context. + * \param[in] stage Processing stage (KNOTD_STAGE_ANSWER..KNOTD_STAGE_ADDITIONAL). + * \param[in] hook Module hook. + * + * \return Error code, KNOT_EOK if success. + */ +int knotd_mod_in_hook(knotd_mod_t *mod, knotd_stage_t stage, knotd_mod_in_hook_f hook); + +/*** DNSSEC API. ***/ + +/*! + * Initializes DNSSEC signing context. + * + * \param[in] mod Module context. + * + * \return Error code, KNOT_EOK if success. + */ +int knotd_mod_dnssec_init(knotd_mod_t *mod); + +/*! + * Loads available DNSSEC signing keys. + * + * \param[in] mod Module context. + * \param[in] verbose Print key summary into log indication. + * + * \return Error code, KNOT_EOK if success. + */ +int knotd_mod_dnssec_load_keyset(knotd_mod_t *mod, bool verbose); + +/*! + * Frees up resources before re-loading DNSSEC signing keys. + * + * \param[in] mod Module context. + */ +void knotd_mod_dnssec_unload_keyset(knotd_mod_t *mod); + +/*! + * Generates RRSIGs for given RRSet. + * + * \param[in] mod Module context. + * \param[out] rrsigs Output RRSIG RRSet. + * \param[in] rrset Input RRSet to generate RRSIGs for. + * \param[in] mm Memory context. + * + * \return Error code, KNOT_EOK if success. + */ +int knotd_mod_dnssec_sign_rrset(knotd_mod_t *mod, knot_rrset_t *rrsigs, + const knot_rrset_t *rrset, knot_mm_t *mm); + +/*! @} */ diff --git a/src/knot/journal/chgset_ctx.c b/src/knot/journal/chgset_ctx.c new file mode 100644 index 0000000..092bacf --- /dev/null +++ b/src/knot/journal/chgset_ctx.c @@ -0,0 +1,88 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "chgset_ctx.h" + +#include "knot/journal/journal.h" +#include "knot/journal/serialization.h" + +chgset_ctx_t *chgset_ctx_create(size_t chunk_count) +{ + chgset_ctx_t *ch = calloc(1, sizeof(*ch)); + if (ch != NULL) { + ch->chunk_count = chunk_count; + ch->src_chunks = calloc(chunk_count, sizeof(*ch->src_chunks)); + ch->chunk_sizes = calloc(chunk_count, sizeof(*ch->chunk_sizes)); + if (ch->src_chunks == NULL || ch->chunk_sizes == NULL) { + chgset_ctx_free(ch); + ch = NULL; + } + } + return ch; +} + +void chgset_ctx_free(chgset_ctx_t *ch) +{ + free(ch->src_chunks); + free(ch->chunk_sizes); + free(ch); +} + +void chgset_ctx_list_close(chgset_ctx_list_t *l) +{ + chgset_ctx_t *ch = NULL, *nxt = NULL; + WALK_LIST_DELSAFE(ch, nxt, l->l) { + chgset_ctx_free(ch); + } + journal_txn_commit(l->txn); + free(l->txn); + memset(l, 0, sizeof(*l)); +} + +void chgset_ctx_iterate(chgset_ctx_t *ch) +{ + assert(ch->chunk_count > 0); + + ch->curr_chunk = 0; + ch->wire = wire_ctx_init(ch->src_chunks[0], ch->chunk_sizes[0]); + ch->phase = CHGSET_CTX_START; +} + +int chgset_ctx_next(chgset_ctx_t *ch, knot_rrset_t *rrset) +{ + int ret = deserialize_rrset_chunks(&ch->wire, rrset, ch->src_chunks, + ch->chunk_sizes, ch->chunk_count, &ch->curr_chunk); + if (ret != KNOT_EOK) { + return ret; + } + + if (ch->phase == CHGSET_CTX_START && rrset->type != KNOT_RRTYPE_SOA) { + return KNOT_EMALF; + } + + if (ch->phase == CHGSET_CTX_SOA_FROM || ch->phase == CHGSET_CTX_SOA_TO || + rrset->type == KNOT_RRTYPE_SOA) { + ch->phase++; + } + + if (ch->curr_chunk == ch->chunk_count - 1 && wire_ctx_available(&ch->wire) == 0) { + ch->phase = CHGSET_CTX_DONE; + } else if (ch->phase == CHGSET_CTX_DONE) { + return KNOT_EMALF; + } + + return ret; +} diff --git a/src/knot/journal/chgset_ctx.h b/src/knot/journal/chgset_ctx.h new file mode 100644 index 0000000..22e1903 --- /dev/null +++ b/src/knot/journal/chgset_ctx.h @@ -0,0 +1,63 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "contrib/ucw/lists.h" +#include "contrib/wire_ctx.h" +#include "libknot/rrset.h" + +typedef enum { + CHGSET_CTX_NOITER = 0, + CHGSET_CTX_START, + CHGSET_CTX_SOA_FROM, + CHGSET_CTX_REM, + CHGSET_CTX_SOA_TO, + CHGSET_CTX_ADD, + CHGSET_CTX_DONE, +} chgset_ctx_phase_t; + +struct journal_txn; // journal.c + +typedef struct { + node_t n; + + uint8_t **src_chunks; + size_t *chunk_sizes; + size_t chunk_count; + size_t curr_chunk; + wire_ctx_t wire; + chgset_ctx_phase_t phase; + + uint32_t serial_from; + uint32_t serial_to; +} chgset_ctx_t; + +typedef struct { + list_t l; + struct journal_txn *txn; +} chgset_ctx_list_t; + +chgset_ctx_t *chgset_ctx_create(size_t chunk_count); + +void chgset_ctx_free(chgset_ctx_t *ch); + +void chgset_ctx_list_close(chgset_ctx_list_t *l); + +void chgset_ctx_iterate(chgset_ctx_t *ch); + +int chgset_ctx_next(chgset_ctx_t *ch, knot_rrset_t *rrset); + diff --git a/src/knot/journal/journal.c b/src/knot/journal/journal.c new file mode 100644 index 0000000..c3ab541 --- /dev/null +++ b/src/knot/journal/journal.c @@ -0,0 +1,2197 @@ +/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <inttypes.h> +#include <limits.h> +#include <sys/stat.h> +#include <stdarg.h> + +#include "knot/journal/journal.h" +#include "knot/common/log.h" +#include "contrib/files.h" +#include "contrib/ctype.h" +#include "libknot/endian.h" +#include "contrib/dynarray.h" + +/*! \brief Journal version. */ +#define JOURNAL_VERSION "1.0" +/*! \brief Changeset chunk size. */ +#define CHUNK_MAX (70 * 1024) +/*! \brief Max number of concurrent DB readers. */ +#define JOURNAL_MAX_READERS 630 + +/*! \brief Various metadata DB key strings. Also hardcoded in macro txn_commit()! */ +#define MDKEY_GLOBAL_VERSION "version" +#define MDKEY_GLOBAL_JOURNAL_COUNT "journal_count" +#define MDKEY_GLOBAL_LAST_TOTAL_OCCUPIED "last_total_occupied" +#define MDKEY_GLOBAL_LAST_INSERTER_ZONE "last_inserter_zone" +#define MDKEY_PERZONE_OCCUPIED "occupied" +#define MDKEY_PERZONE_FLAGS "flags" +#define KEY_BOOTSTRAP_CHANGESET "bootstrap" + +/*! \brief The number of unused bytes in DB key. */ +#define DB_KEY_UNUSED_ZERO (4) + +/*! \brief Metadata inserted on the beginning of each chunk: + * uint32_t serial_to + uint32_t chunk_count + 24B unused */ +#define JOURNAL_HEADER_SIZE (32) + +// eventually move to contrib and reuse as needed +#define local_array_max_static_size (100) +#define local_array(type, name, size) \ + type name ## _static__[local_array_max_static_size] = { 0 }; \ + type *name ## _dynamic__ = ((size) > local_array_max_static_size ? calloc((size), sizeof(type)) : NULL); \ + type *name = ((size) > local_array_max_static_size ? name ## _dynamic__ : name ## _static__); +#define local_array_free(name) { free(name ## _dynamic__); } + +enum { + LAST_FLUSHED_VALID = 1 << 0, /* "last flush is valid" flag. */ + SERIAL_TO_VALID = 1 << 1, /* "last serial_to is valid" flag. */ + MERGED_SERIAL_VALID = 1 << 2, /* "serial_from" of merged changeset. */ + DIRTY_SERIAL_VALID = 1 << 3, /* "dirty_serial" is present in the DB. */ + FIRST_SERIAL_INVALID = 1 << 4, /* "first_serial" is not valid. */ +}; + +static bool journal_flush_allowed(journal_t *j) { + conf_val_t val = conf_zone_get(conf(), C_ZONEFILE_SYNC, j->zone); + return conf_int(&val) >= 0; +} + +static bool journal_merge_allowed(journal_t *j) { + return !journal_flush_allowed(j); // TODO think of other behaviour, e.g. setting +} + +static size_t journal_max_usage(journal_t *j) +{ + conf_val_t val = conf_zone_get(conf(), C_MAX_JOURNAL_USAGE, j->zone); + return conf_int(&val); +} + +static size_t journal_max_changesets(journal_t *j) +{ + conf_val_t val = conf_zone_get(conf(), C_MAX_JOURNAL_DEPTH, j->zone); + return conf_int(&val); +} + +static float journal_tofree_factor(journal_t *j) +{ + return 2.0f; +} + +static float journal_minfree_factor(journal_t *j) +{ + return 0.33f; +} + +/* + * ***************************** PART I ******************************* + * + * Transaction manipulation functions + * + * ******************************************************************** + */ + +typedef struct { + uint32_t first_serial; // Serial_from of the first changeset. + uint32_t last_serial; // Serial_from of the last changeset. + uint32_t last_serial_to; // Serial_to of the last changeset. + uint32_t last_flushed; // Serial_from of the last flushed (or merged) chengeset. + uint32_t merged_serial; // "serial_from" of merged changeset. + uint32_t dirty_serial; // Serial_from of an incompletely inserted changeset which shall be deleted (see DB_MAX_INSERT_TXN). + uint32_t changeset_count; // Number of changesets in this journal. + uint32_t flags; // LAST_FLUSHED_VALID, SERIAL_TO_VALID, MERGED_SERIAL_VALID. +} metadata_t; + +typedef struct journal_txn { + journal_t *j; + knot_db_txn_t *txn; + int ret; + bool opened; + + bool is_rw; + + knot_db_iter_t *iter; + + knot_db_val_t key; + knot_db_val_t val; + uint8_t key_raw[512]; + + metadata_t shadow_md; +} txn_t; + +static void md_get(txn_t *txn, const knot_dname_t *zone, const char *mdkey, uint64_t *res); +static void md_get32(txn_t *txn, const knot_dname_t *zone, const char *mdkey, uint32_t *res); +static void md_set(txn_t *txn, const knot_dname_t *zone, const char *mdkey, uint64_t val); +static void md_set32(txn_t *txn, const knot_dname_t *zone, const char *mdkey, uint32_t val); + +static void txn_init(txn_t *txn, knot_db_txn_t *db_txn, journal_t *j) +{ + memset(txn, 0, sizeof(*txn)); + txn->j = j; + txn->txn = db_txn; + txn->key.data = &txn->key_raw; +} + +#define local_txn_t(txn_name, journal) \ + knot_db_txn_t __db_txn_ ## txn_name; \ + txn_t __local_txn_ ## txn_name; \ + txn_t *txn_name = &__local_txn_ ## txn_name; \ + txn_init(txn_name, &__db_txn_ ## txn_name, (journal)) + +/* + * Structure of the DB key: + * Metadata: + * | [ zone_name | \0 ] | unused zero 4B | metadata_key | \0 | + * + * Changeset: + * | zone_name | \0 | unused zero 4B | (be32)serial_from | (be32)chunk_index | + * or + * | zone_name | \0 | unused zero 4B | metadata_key | \0 | (be32)serial_from | + * + * Structure of the changeset: + * | (be32)serial_to | (be32)#of_chunks | unused zero 24B | serialized_changeset... + * + */ + +static bool key_is_ok(const knot_db_val_t *key, bool zone_related) +{ + const uint8_t *it = key->data; + ssize_t it_len = key->len; + if (zone_related) { + size_t dname_len = knot_dname_size(it); + it += dname_len; + it_len -= dname_len; + } + it += 4; + it_len -= 4; + + return ((zone_related && it_len == 8) || // normal changeset + (is_lower(*it) && !is_lower(*(it-1)))); // metadata +} + +static void txn_key_str(txn_t *txn, const knot_dname_t *zone, const char *key) +{ + size_t zone_size = knot_dname_size(zone); + txn->key.len = zone_size + DB_KEY_UNUSED_ZERO + strlen(key) + 1; + if (txn->key.len > 512) { + txn->ret = KNOT_ERROR; + return; + } + if (zone != NULL) memcpy(txn->key.data, zone, zone_size); + memset(txn->key.data + zone_size, 0, DB_KEY_UNUSED_ZERO); + strcpy(txn->key.data + zone_size + DB_KEY_UNUSED_ZERO, key); + assert(key_is_ok(&txn->key, zone != NULL)); +} + +static void txn_key_2u32(txn_t *txn, const knot_dname_t *zone, uint32_t key1, uint32_t key2) +{ + size_t zone_size = knot_dname_size(zone); + txn->key.len = zone_size + DB_KEY_UNUSED_ZERO + 2*sizeof(uint32_t); + if (txn->key.len > 512) { + txn->ret = KNOT_ERROR; + return; + } + if (zone != NULL) memcpy(txn->key.data, zone, zone_size); + memset(txn->key.data + zone_size, 0, DB_KEY_UNUSED_ZERO); + uint32_t key_be1 = htobe32(key1); + uint32_t key_be2 = htobe32(key2); + memcpy(txn->key.data + zone_size + DB_KEY_UNUSED_ZERO, &key_be1, sizeof(uint32_t)); + memcpy(txn->key.data + zone_size + DB_KEY_UNUSED_ZERO + sizeof(uint32_t), + &key_be2, sizeof(uint32_t)); + assert(key_is_ok(&txn->key, zone != NULL)); +} + +static void txn_key_str_u32(txn_t *txn, const knot_dname_t *zone, const char *key1, uint32_t key2) +{ + size_t zone_size = knot_dname_size(zone); + txn->key.len = zone_size + DB_KEY_UNUSED_ZERO + strlen(key1) + 1 + sizeof(uint32_t); + if (txn->key.len > 512) { + txn->ret = KNOT_ERROR; + return; + } + if (zone != NULL) memcpy(txn->key.data, zone, zone_size); + memset(txn->key.data + zone_size, 0, DB_KEY_UNUSED_ZERO); + strcpy(txn->key.data + zone_size + DB_KEY_UNUSED_ZERO, key1); + uint32_t key_be2 = htobe32(key2); + memcpy(txn->key.data + zone_size + DB_KEY_UNUSED_ZERO + strlen(key1) + 1, + &key_be2, sizeof(uint32_t)); + assert(key_is_ok(&txn->key, zone != NULL)); +} + +static int txn_cmpkey(txn_t *txn, knot_db_val_t *key2) +{ + if (txn->key.len != key2->len) { + return (txn->key.len < key2->len ? -1 : 1); + } + if (key2->len == 0) { + return 0; + } + return memcmp(txn->key.data, key2->data, key2->len); +} + +static void txn_val_u64(txn_t *txn, uint64_t *res) +{ + if (txn->ret != KNOT_EOK) { + return; + } + uint32_t beval32; + uint64_t beval; + switch (txn->val.len) { + case sizeof(uint32_t): + memcpy(&beval32, (uint32_t *)txn->val.data, sizeof(beval32)); + *res = (uint64_t)be32toh(beval32); + break; + case sizeof(uint64_t): + memcpy(&beval, (uint64_t *)txn->val.data, sizeof(beval)); + *res = be64toh(beval); + break; + default: + txn->ret = KNOT_EMALF; + } +} + +#define txn_begin_md(md) md_get32(txn, txn->j->zone, #md, &txn->shadow_md.md) +#define txn_commit_md(md) md_set32(txn, txn->j->zone, #md, txn->shadow_md.md) + +#define txn_check_open(txn) if (((txn)->ret = ((txn)->opened ? (txn)->ret : KNOT_EINVAL)) != KNOT_EOK) return +#define txn_check_ret(txn) if (((txn)->ret = ((txn)->opened ? (txn)->ret : KNOT_EINVAL)) != KNOT_EOK) return ((txn)->ret) + +static void txn_begin(txn_t *txn, bool write_allowed) +{ + if (txn->ret == KNOT_EOK && txn->opened) { + txn->ret = KNOT_EINVAL; + } + if (txn->ret != KNOT_EOK) { + return; + } + + txn->ret = txn->j->db->db_api->txn_begin(txn->j->db->db, txn->txn, + (write_allowed ? 0 : KNOT_DB_RDONLY)); + + txn->is_rw = write_allowed; + txn->opened = true; + + txn_begin_md(first_serial); + txn_begin_md(last_serial); + txn_begin_md(last_serial_to); + txn_begin_md(last_flushed); + txn_begin_md(merged_serial); + txn_begin_md(dirty_serial); + txn_begin_md(changeset_count); + txn_begin_md(flags); +} + +static void txn_find_force(txn_t *txn) +{ + txn_check_open(txn); + txn->ret = txn->j->db->db_api->find(txn->txn, &txn->key, &txn->val, 0); +} + +static bool txn_find(txn_t *txn) +{ + if (txn->ret != KNOT_EOK || !txn->opened) { + return false; + } + txn_find_force(txn); + if (txn->ret == KNOT_ENOENT) { + txn->ret = KNOT_EOK; + return false; + } + return (txn->ret == KNOT_EOK); +} + +static void txn_insert(txn_t *txn) +{ + txn_check_open(txn); + txn->ret = txn->j->db->db_api->insert(txn->txn, &txn->key, &txn->val, 0); +} + +static void txn_del(txn_t *txn) +{ + txn_check_open(txn); + txn->ret = txn->j->db->db_api->del(txn->txn, &txn->key); +} + +static void txn_iter_begin(txn_t *txn) +{ + txn_check_open(txn); + txn->iter = txn->j->db->db_api->iter_begin(txn->txn, KNOT_DB_FIRST); + if (txn->iter == NULL) { + txn->ret = KNOT_ENOMEM; + } +} + +#define txn_check_iter if (txn->iter == NULL && txn->ret == KNOT_EOK) txn->ret = KNOT_EINVAL; \ + if (txn->ret != KNOT_EOK) return; + +static void txn_iter_seek(txn_t *txn) +{ + txn_check_iter + txn->iter = txn->j->db->db_api->iter_seek(txn->iter, &txn->key, 0); + if (txn->iter == NULL) { + txn->ret = KNOT_ENOENT; + } +} + +static void txn_iter_key(txn_t *txn, knot_db_val_t *at_key) +{ + txn_check_iter + txn->ret = txn->j->db->db_api->iter_key(txn->iter, at_key); +} + +static void txn_iter_val(txn_t *txn) +{ + txn_check_iter + txn->ret = txn->j->db->db_api->iter_val(txn->iter, &txn->val); +} + +static void txn_iter_next(txn_t *txn) +{ + txn_check_iter + txn->iter = txn->j->db->db_api->iter_next(txn->iter); + if (txn->iter == NULL) { + txn->ret = KNOT_ENOENT; + } +} + +static void txn_iter_finish(txn_t *txn) +{ + if (txn->iter != NULL) { + txn->j->db->db_api->iter_finish(txn->iter); + } + txn->iter = NULL; +} + +static void txn_abort(txn_t *txn) +{ + if (txn->opened) { + txn_iter_finish(txn); + txn->j->db->db_api->txn_abort(txn->txn); + txn->opened = false; + } +} + +static void txn_commit(txn_t *txn) +{ + if (txn->is_rw) { + txn_commit_md(first_serial); + txn_commit_md(last_serial); + txn_commit_md(last_serial_to); + txn_commit_md(last_flushed); + txn_commit_md(merged_serial); + txn_commit_md(dirty_serial); + txn_commit_md(changeset_count); + txn_commit_md(flags); + } + + if (txn->ret != KNOT_EOK) { + txn_abort(txn); + return; + } + + txn_iter_finish(txn); + txn->ret = txn->j->db->db_api->txn_commit(txn->txn); + + if (txn->ret == KNOT_EOK) { + txn->opened = false; + } + txn_abort(txn); // no effect if all ok +} + +void journal_txn_commit(struct journal_txn *txn) +{ + if (txn != NULL) { + txn_commit(txn); + } +} + +static void txn_restart(txn_t *txn) +{ + txn_commit(txn); + assert(!txn->opened); + if (txn->ret == KNOT_EOK) { + txn_begin(txn, txn->is_rw); + } +} + +static void txn_reuse(txn_t **txn, txn_t *to_reuse, bool write_allowed) +{ + if (to_reuse == NULL) { + txn_begin(*txn, write_allowed); + } else { + *txn = to_reuse; + } +} + +static void txn_unreuse(txn_t **txn, txn_t *reused) +{ + if (reused == NULL) { + txn_commit(*txn); + } +} + +#define reuse_txn(name, journal, to_reuse, wa) local_txn_t(name, journal); txn_reuse(&name, to_reuse, wa) +#define unreuse_txn(name, reused) txn_unreuse(&name, reused) + +/* + * ***************************** PART II ****************************** + * + * DB metadata manip. and Chunk metadata headers + * + * ******************************************************************** + */ + +static void md_get(txn_t *txn, const knot_dname_t *zone, const char *mdkey, uint64_t *res) +{ + txn_check_open(txn); + txn_key_str(txn, zone, mdkey); + uint64_t res1 = 0; + if (txn_find(txn)) { + txn_val_u64(txn, &res1); + } + *res = res1; +} + +static void md_get32(txn_t *txn, const knot_dname_t *zone, const char *mdkey, uint32_t *res) +{ + uint64_t res1 = 0; + md_get(txn, zone, mdkey, &res1); + if (res1 > UINT32_MAX) { + txn->ret = KNOT_EMALF; + } else { + *res = (uint32_t)res1; + } +} + +// allocates res +static void md_get_common_last_inserter_zone(txn_t *txn, knot_dname_t **res) +{ + txn_check_open(txn); + txn_key_str(txn, NULL, MDKEY_GLOBAL_LAST_INSERTER_ZONE); + if (txn_find(txn)) { + *res = knot_dname_copy(txn->val.data, NULL); + } else { + *res = NULL; + } +} + +static int md_set_common_last_inserter_zone(txn_t *txn, knot_dname_t *zone) +{ + txn_check_ret(txn); + txn_key_str(txn, NULL, MDKEY_GLOBAL_LAST_INSERTER_ZONE); + txn->val.len = knot_dname_size(zone); + txn->val.data = zone; + txn_insert(txn); + return txn->ret; +} + +static void md_del_last_inserter_zone(txn_t *txn, knot_dname_t *if_equals) +{ + txn_check_open(txn); + txn_key_str(txn, NULL, MDKEY_GLOBAL_LAST_INSERTER_ZONE); + if (txn_find(txn)) { + if (if_equals == NULL || knot_dname_is_equal(txn->val.data, if_equals)) { + txn_del(txn); + } + } +} + +static void md_get_common_last_occupied(txn_t *txn, size_t *res) +{ + uint64_t sres = 0; + md_get(txn, NULL, MDKEY_GLOBAL_LAST_TOTAL_OCCUPIED, &sres); + *res = (size_t) sres; +} + +static void md_set(txn_t *txn, const knot_dname_t *zone, const char *mdkey, uint64_t val) +{ + txn_key_str(txn, zone, mdkey); + uint64_t val1 = htobe64(val); + txn->val.len = sizeof(uint64_t); + txn->val.data = &val1; + txn_insert(txn); +} + +static void md_set32(txn_t *txn, const knot_dname_t *zone, const char *mdkey, uint32_t val) +{ + txn_key_str(txn, zone, mdkey); + uint32_t val1 = htobe32(val); + txn->val.len = sizeof(uint32_t); + txn->val.data = &val1; + txn_insert(txn); +} + +static bool md_flag(txn_t *txn, int flag) +{ + return (txn->shadow_md.flags & flag); +} + +/*! \brief Marks metadata as flushed */ +static void md_flush(txn_t *txn) +{ + if (md_flag(txn, SERIAL_TO_VALID) && !md_flag(txn, FIRST_SERIAL_INVALID)) { + txn->shadow_md.last_flushed = txn->shadow_md.last_serial; + txn->shadow_md.flags |= LAST_FLUSHED_VALID; + } +} + +static int md_flushed(txn_t *txn) +{ + return (!md_flag(txn, SERIAL_TO_VALID) || + (md_flag(txn, LAST_FLUSHED_VALID) && + serial_equal(txn->shadow_md.last_flushed, txn->shadow_md.last_serial))); +} + +static void make_header(knot_db_val_t *to, uint32_t serial_to, int chunk_count) +{ + assert(to->len >= JOURNAL_HEADER_SIZE); + assert(chunk_count > 0); + + uint32_t be_serial_to = htobe32(serial_to); + uint32_t be_chunk_count = htobe32((uint32_t)chunk_count); + + memcpy(to->data, &be_serial_to, sizeof(be_serial_to)); + memcpy(to->data + sizeof(be_serial_to), &be_chunk_count, sizeof(be_chunk_count)); + memset(to->data + sizeof(be_serial_to) + sizeof(be_chunk_count), 0, + JOURNAL_HEADER_SIZE - sizeof(be_serial_to) - sizeof(be_chunk_count)); +} + +/*! \brief read properties from chunk header "from". All the output params are optional */ +static void unmake_header(const knot_db_val_t *from, uint32_t *serial_to, + int *chunk_count, size_t *header_size) +{ + assert(from->len >= JOURNAL_HEADER_SIZE); + + uint32_t be_serial_to, be_chunk_count; + if (serial_to != NULL) { + memcpy(&be_serial_to, from->data, sizeof(be_serial_to)); + *serial_to = be32toh(be_serial_to); + } + if (chunk_count != NULL) { + memcpy(&be_chunk_count, from->data + sizeof(be_serial_to), sizeof(be_chunk_count)); + assert(be32toh(be_chunk_count) <= INT_MAX); + *chunk_count = (int)be32toh(be_chunk_count); + } + if (header_size != NULL) { + *header_size = JOURNAL_HEADER_SIZE; + } +} + +static int first_digit(char * of) +{ + return atoi(of); +} + +static void md_update_journal_count(txn_t * txn, int change_amount) +{ + uint64_t jcnt = 0; + md_get(txn, NULL, MDKEY_GLOBAL_JOURNAL_COUNT, &jcnt); + md_set(txn, NULL, MDKEY_GLOBAL_JOURNAL_COUNT, jcnt + change_amount); +} + +static int initial_md_check(journal_t *j, bool *dirty_present) +{ + *dirty_present = 0; + + bool something_updated = false; + + local_txn_t(txn, j); + txn_begin(txn, true); + txn_key_str(txn, NULL, MDKEY_GLOBAL_VERSION); + if (!txn_find(txn)) { + txn->val.len = strlen(JOURNAL_VERSION) + 1; + txn->val.data = JOURNAL_VERSION; + txn_insert(txn); + something_updated = true; + } else { + char * jver = txn->val.data; + if (first_digit(jver) != first_digit(JOURNAL_VERSION)) { + txn_abort(txn); + return KNOT_ENOTSUP; + } + } + txn_key_str(txn, j->zone, MDKEY_PERZONE_FLAGS); + if (!txn_find(txn)) { + md_update_journal_count(txn, +1); + something_updated = true; + } + *dirty_present = md_flag(txn, DIRTY_SERIAL_VALID); + + if (something_updated) { + txn_commit(txn); + } else { // abort to gain up speed when opening a lot of zones + txn_abort(txn); + } + + return txn->ret; +} + +/* + * **************************** PART III ****************************** + * + * DB iteration + * + * ******************************************************************** + */ + +enum { + JOURNAL_ITERATION_CHUNKS, // call the iteration callback for each chunk read, with just the chunk in ctx->val + JOURNAL_ITERATION_CHANGESETS // call the iteration callback after the last chunk of a changeset read, with all its chunks in ctx->val +}; + +typedef struct { + txn_t *txn; // DB txn not to be touched by callback, just contains journal pointer + uint32_t serial; // serial-from of current changeset + uint32_t serial_to; // serial-to of current changeset + const int method; // JOURNAL_ITERATION_CHUNKS or JOURNAL_ITERATION_CHANGESETS, to be set by the caller of iterate() + int chunk_index; // index of current chunk + int chunk_count; // # of chunks of current changeset + knot_db_val_t *val; // one val if JOURNAL_ITERATION_CHUNKS; chunk_count vals if JOURNAL_ITERATION_CHANGESETS + knot_db_iter_t *iter; // DB iteration context, not to be touched by callback + void *iter_context; // anything to send to the callback by the caller of iterate(), untouched by iterate() +} iteration_ctx_t; + +typedef int (*iteration_cb_t)(iteration_ctx_t *ctx); + +/*! + * \brief Move iter to next changeset chunk. + * + * Try optimisticly fast move to next DB item. But the changeset can be out of order, + * so if we don't succeed (different serial or end of DB), we lookup next serial slowly. + */ + +static void get_iter_next(iteration_ctx_t *ctx, iteration_cb_t key_cb) +{ + knot_db_val_t other_key = { 0 }; + + txn_check_open(ctx->txn); + txn_iter_next(ctx->txn); + txn_iter_key(ctx->txn, &other_key); + key_cb(ctx); + if (ctx->txn->ret == KNOT_ENOENT || + (ctx->txn->ret == KNOT_EOK && txn_cmpkey(ctx->txn, &other_key) != 0)) { + ctx->txn->ret = KNOT_EOK; + if (ctx->txn->iter != NULL) { + txn_iter_finish(ctx->txn); + } + txn_iter_begin(ctx->txn); + txn_iter_seek(ctx->txn); + } +} + +static int iterate(journal_t *j, txn_t *_txn, iteration_cb_t cb, int method, + void *iter_context, uint32_t first, uint32_t last, iteration_cb_t key_cb) +{ + reuse_txn(txn, j, _txn, true); + + iteration_ctx_t ctx = { + .method = method, + .iter_context = iter_context, + .txn = txn, + .serial = first, + .chunk_index = 0 + }; + + knot_db_val_t *vals = NULL; + + txn_iter_begin(txn); + + key_cb(&ctx); + txn_iter_seek(txn); + + ctx.val = &txn->val; + + while (true) { + txn_iter_val(txn); + if (txn->ret != KNOT_EOK) { + break; + } + + unmake_header(&txn->val, &ctx.serial_to, &ctx.chunk_count, NULL); + + if (method == JOURNAL_ITERATION_CHANGESETS) { + if (ctx.chunk_index == 0) { + if (vals != NULL) free(vals); + vals = malloc(ctx.chunk_count * sizeof(knot_db_val_t)); + if (vals == NULL) { + txn->ret = KNOT_ENOMEM; + break; + } + ctx.val = vals; + } + memcpy(vals + ctx.chunk_index, &txn->val, sizeof(knot_db_val_t)); + } + + if (method == JOURNAL_ITERATION_CHUNKS) { + txn->ret = cb(&ctx); + } + + if (ctx.chunk_index == ctx.chunk_count - 1) { // hit last chunk of current changeset + if (method == JOURNAL_ITERATION_CHANGESETS) { + txn->ret = cb(&ctx); + } + + if (ctx.serial == last) { + break; // standard loop exit here + } + + ctx.serial = ctx.serial_to; + ctx.chunk_index = 0; + } else { + ctx.chunk_index++; + } + + get_iter_next(&ctx, key_cb); + } + + if (vals != NULL) { + free(vals); + } + txn_iter_finish(txn); + + unreuse_txn(txn, _txn); + + return txn->ret; +} + +static int normal_iterkeycb(iteration_ctx_t *ctx) +{ + txn_key_2u32(ctx->txn, ctx->txn->j->zone, ctx->serial, ctx->chunk_index); + return KNOT_EOK; +} + +/* + * ***************************** PART IV ****************************** + * + * Reading changesets + * + * ******************************************************************** + */ + +/*! \brief Deserialize changeset from chunks (in vals) */ +static int vals_to_changeset(knot_db_val_t *vals, int nvals, + const knot_dname_t *zone_name, changeset_t **ch) +{ + local_array(uint8_t *, valps, nvals) + local_array(size_t, vallens, nvals) + if (valps == NULL || vallens == NULL) { + local_array_free(valps) + local_array_free(vallens) + return KNOT_ENOMEM; + } + + for (size_t i = 0; i < nvals; i++) { + valps[i] = vals[i].data + JOURNAL_HEADER_SIZE; + vallens[i] = vals[i].len - JOURNAL_HEADER_SIZE; + } + + changeset_t *t_ch = changeset_new(zone_name); + if (t_ch == NULL) { + local_array_free(valps) + local_array_free(vallens) + return KNOT_ENOMEM; + } + + int ret = changeset_deserialize(t_ch, valps, vallens, nvals); + + local_array_free(valps) + local_array_free(vallens) + if (ret != KNOT_EOK) { + changeset_free(t_ch); + return ret; + } + *ch = t_ch; + return KNOT_EOK; +} + +static int vals_to_chgset_ctx(knot_db_val_t *vals, int nvals, uint32_t serial_from, + uint32_t serial_to, chgset_ctx_t **ch) +{ + if (nvals < 1) { + return KNOT_EINVAL; + } + + chgset_ctx_t *t_ch = chgset_ctx_create(nvals); + if (t_ch == NULL) { + return KNOT_ENOMEM; + } + + for (size_t i = 0; i < nvals; i++) { + t_ch->src_chunks[i] = vals[i].data + JOURNAL_HEADER_SIZE; + t_ch->chunk_sizes[i] = vals[i].len - JOURNAL_HEADER_SIZE; + } + + t_ch->serial_from = serial_from; + t_ch->serial_to = serial_to; + + *ch = t_ch; + return KNOT_EOK; +} + +static int load_one_itercb(iteration_ctx_t *ctx) +{ + changeset_t *ch = NULL, **targ = ctx->iter_context; + if (*targ != NULL) { + return KNOT_EINVAL; + } + + int ret = vals_to_changeset(ctx->val, ctx->chunk_count, ctx->txn->j->zone, &ch); + if (ret == KNOT_EOK) *targ = ch; + return ret; +} + +static int load_list_itercb(iteration_ctx_t *ctx) +{ + changeset_t *ch = NULL; + list_t *chlist = *(list_t **) ctx->iter_context; + + int ret = vals_to_changeset(ctx->val, ctx->chunk_count, ctx->txn->j->zone, &ch); + + if (ret == KNOT_EOK) { + add_tail(chlist, &ch->n); + } + return ret; +} + +static int load_list_ctx_itercb(iteration_ctx_t *ctx) +{ + chgset_ctx_t *ch = NULL; + list_t *chlist = *(list_t **) ctx->iter_context; + + int ret = vals_to_chgset_ctx(ctx->val, ctx->chunk_count, ctx->serial, ctx->serial_to, &ch); + + if (ret == KNOT_EOK) { + add_tail(chlist, &ch->n); + } + return ret; +} + +/*! \brief Load one changeset (with serial) from DB */ +static int load_one(journal_t *j, txn_t *_txn, uint32_t serial, changeset_t **ch) +{ + reuse_txn(txn, j, _txn, false); + changeset_t *rch = NULL; + iterate(j, txn, load_one_itercb, JOURNAL_ITERATION_CHANGESETS, &rch, serial, serial, normal_iterkeycb); + unreuse_txn(txn, _txn); + if (txn->ret == KNOT_EOK) { + if (rch == NULL) txn->ret = KNOT_ENOENT; + else *ch = rch; + } + return txn->ret; +} + +static int load_merged_changeset(journal_t *j, txn_t *_txn, changeset_t **mch, + const uint32_t *only_if_serial) +{ + assert(*mch == NULL); + + reuse_txn(txn, j, _txn, false); + txn_check_ret(txn); + uint32_t ms = txn->shadow_md.merged_serial, fl = txn->shadow_md.flags; + + if ((fl & MERGED_SERIAL_VALID) && + (only_if_serial == NULL || serial_equal(ms, *only_if_serial))) { + load_one(j, txn, ms, mch); + } + unreuse_txn(txn, _txn); + + return txn->ret; +} + +int journal_load_changesets(journal_t *j, list_t *dst, uint32_t from) +{ + if (j == NULL || j->db == NULL || dst == NULL) return KNOT_EINVAL; + + local_txn_t(txn, j); + txn_begin(txn, false); + + uint32_t ls = txn->shadow_md.last_serial; + iterate(j, txn, load_list_itercb, JOURNAL_ITERATION_CHANGESETS, &dst, from, + ls, normal_iterkeycb); + txn_commit(txn); + + return txn->ret; +} + +int journal_load_chgset_ctx(journal_t *j, chgset_ctx_list_t *dst, uint32_t from) +{ + if (j == NULL || j->db == NULL || dst == NULL) return KNOT_EINVAL; + + txn_t *txn = calloc(1, sizeof(*txn) + sizeof(*txn->txn)); + if (txn == NULL) { + return KNOT_ENOMEM; + } + txn_init(txn, ((void *)txn) + sizeof(*txn), j); + txn_begin(txn, false); + + init_list(&dst->l); + dst->txn = txn; + list_t *dstl = &dst->l; + + uint32_t ls = txn->shadow_md.last_serial; + iterate(j, txn, load_list_ctx_itercb, JOURNAL_ITERATION_CHANGESETS, &dstl, from, + ls, normal_iterkeycb); + + if (txn->ret != KNOT_EOK) { + int ret = txn->ret; + txn_commit(txn); + free(txn); + return ret; + } + + return txn->ret; +} + +int load_bootstrap_iterkeycb(iteration_ctx_t *ctx) +{ + txn_key_str_u32(ctx->txn, ctx->txn->j->zone, KEY_BOOTSTRAP_CHANGESET, ctx->chunk_index); + return KNOT_EOK; +} + +static int load_bootstrap_changeset(journal_t *j, txn_t *_txn, changeset_t **ch) +{ + reuse_txn(txn, j, _txn, false); + changeset_t *rch = NULL; + iterate(j, txn, load_one_itercb, JOURNAL_ITERATION_CHANGESETS, &rch, + 0, 0, load_bootstrap_iterkeycb); + unreuse_txn(txn, _txn); + if (txn->ret == KNOT_EOK) { + if (rch == NULL) txn->ret = KNOT_ENOENT; + else *ch = rch; + } + return txn->ret; +} + +static bool has_bootstrap_changeset(journal_t *j, txn_t *_txn) +{ + reuse_txn(txn, j, _txn, false); + txn_key_str_u32(txn, j->zone, KEY_BOOTSTRAP_CHANGESET, 0); + bool res = txn_find(txn); + unreuse_txn(txn, _txn); + return res; +} + +int journal_load_bootstrap(journal_t *j, list_t *dst) +{ + if (j == NULL || j->db == NULL || dst == NULL) return KNOT_EINVAL; + + local_txn_t(txn, j); + txn_begin(txn, false); + + changeset_t *bch = NULL; + load_bootstrap_changeset(j, txn, &bch); + if (bch == NULL) { + txn->ret = KNOT_ENOENT; + goto jlb_end; + } + add_tail(dst, &bch->n); + uint32_t from = knot_soa_serial(bch->soa_to->rrs.rdata); + + uint32_t ls = txn->shadow_md.last_serial; + iterate(j, txn, load_list_itercb, JOURNAL_ITERATION_CHANGESETS, &dst, + from, ls, normal_iterkeycb); + if (txn->ret == KNOT_ENOENT) { + txn->ret = KNOT_EOK; + } +jlb_end: + txn_commit(txn); + return txn->ret; +} + +/* + * ***************************** PART V ******************************* + * + * Deleting changesets + * + * ******************************************************************** + */ + +typedef struct { + size_t freed_approx; + size_t to_be_freed; +} delete_status_t; + +static int del_upto_itercb(iteration_ctx_t *ctx) +{ + txn_key_2u32(ctx->txn, ctx->txn->j->zone, ctx->serial, ctx->chunk_index); + txn_del(ctx->txn); + txn_check_ret(ctx->txn); + + // one whole changeset has been deleted => update metadata. + // We are sure that the deleted changeset is first at this time. + // If it's not merged changeset, point first_serial to next one + if (ctx->chunk_index == ctx->chunk_count - 1) { + if (!md_flag(ctx->txn, MERGED_SERIAL_VALID) || + !serial_equal(ctx->txn->shadow_md.merged_serial,ctx->serial)) { + ctx->txn->shadow_md.first_serial = ctx->serial_to; + ctx->txn->shadow_md.changeset_count--; + } + if (serial_equal(ctx->txn->shadow_md.last_flushed, ctx->serial)) { + ctx->txn->shadow_md.flags &= ~LAST_FLUSHED_VALID; + } + if (serial_equal(ctx->txn->shadow_md.last_serial, ctx->serial)) { + ctx->txn->shadow_md.flags &= ~SERIAL_TO_VALID; + } + if (serial_equal(ctx->txn->shadow_md.merged_serial,ctx->serial)) { + ctx->txn->shadow_md.flags &= ~MERGED_SERIAL_VALID; + } + } + return KNOT_EOK; +} + +/*! \brief Delete from beginning of DB up to "last" changeset including. + * Please ensure (dbfirst == j->metadata.first_serial) */ +static int delete_upto(journal_t *j, txn_t *txn, uint32_t dbfirst, uint32_t last) +{ + return iterate(j, txn, del_upto_itercb, JOURNAL_ITERATION_CHUNKS, NULL, + dbfirst, last, normal_iterkeycb); +} + +static int delete_merged_changeset(journal_t *j, txn_t *t) +{ + reuse_txn(txn, j, t, true); + txn_check_ret(txn); + if (!md_flag(txn, MERGED_SERIAL_VALID)) { + txn->ret = KNOT_ENOENT; + } else { + delete_upto(j, txn, txn->shadow_md.merged_serial, txn->shadow_md.merged_serial); + } + unreuse_txn(txn, t); + return txn->ret; +} + +static int delete_bootstrap_changeset(journal_t *j, txn_t *_txn); + +static int drop_journal(journal_t *j, txn_t *_txn) +{ + reuse_txn(txn, j, _txn, true); + txn_check_ret(txn); + if (md_flag(txn, MERGED_SERIAL_VALID)) { + delete_merged_changeset(j, txn); + } + if (md_flag(txn, SERIAL_TO_VALID) && !md_flag(txn, FIRST_SERIAL_INVALID)) { + delete_upto(j, txn, txn->shadow_md.first_serial, txn->shadow_md.last_serial); + } + delete_bootstrap_changeset(j, txn); + md_del_last_inserter_zone(txn, j->zone); + md_set(txn, j->zone, MDKEY_PERZONE_OCCUPIED, 0); + unreuse_txn(txn, _txn); + return txn->ret; +} + +static int del_tofree_itercb(iteration_ctx_t *ctx) +{ + delete_status_t *ds = ctx->iter_context; + + if (ds->to_be_freed == 0) { + return KNOT_EOK; // all done, just running through the rest of records w/o change + } + + txn_key_2u32(ctx->txn, ctx->txn->j->zone, ctx->serial, ctx->chunk_index); + txn_del(ctx->txn); + txn_check_ret(ctx->txn); + + ds->freed_approx += /*4096 + */ctx->val->len; + + // when whole changeset deleted, check target and update metadata + if (ctx->chunk_index == ctx->chunk_count - 1) { + ctx->txn->shadow_md.first_serial = ctx->serial_to; + ctx->txn->shadow_md.changeset_count--; + if (serial_equal(ctx->txn->shadow_md.last_flushed, ctx->serial)) { + ctx->txn->shadow_md.flags &= ~LAST_FLUSHED_VALID; + ds->to_be_freed = 0; // prevents deleting unflushed changesets + } + if (serial_equal(ctx->txn->shadow_md.last_serial, ctx->serial)) { + ctx->txn->shadow_md.flags &= ~SERIAL_TO_VALID; + } + if (ds->freed_approx >= ds->to_be_freed) { + ds->to_be_freed = 0; + } + } + + return KNOT_EOK; +} + +/*! + * \brief Deletes from j->db oldest changesets to free up space + * + * It tries deleting olny flushed changesets, preserves all unflushed ones. + * + * \retval KNOT_EOK if no error, even if too little or nothing deleted (check really_freed for result); KNOT_E* if error + */ +static int delete_tofree(journal_t *j, txn_t *_txn, size_t to_be_freed, size_t *really_freed) +{ + reuse_txn(txn, j, _txn, true); + txn_check_ret(txn); + + if (!md_flag(txn, LAST_FLUSHED_VALID)) { + *really_freed = 0; + return KNOT_EOK; + } + delete_status_t ds = { .freed_approx = 0, .to_be_freed = to_be_freed }; + iterate(j, txn, del_tofree_itercb, JOURNAL_ITERATION_CHUNKS, &ds, + txn->shadow_md.first_serial, txn->shadow_md.last_serial, normal_iterkeycb); + unreuse_txn(txn, _txn); + + if (txn->ret == KNOT_EOK) { + *really_freed = ds.freed_approx; + } + return txn->ret; +} + +static int del_count_itercb(iteration_ctx_t *ctx) +{ + delete_status_t *ds = ctx->iter_context; + if (ds->freed_approx >= ds->to_be_freed) { + return KNOT_EOK; + } + txn_key_2u32(ctx->txn, ctx->txn->j->zone, ctx->serial, ctx->chunk_index); + txn_del(ctx->txn); + txn_check_ret(ctx->txn); + + // when whole changeset deleted, check target and update metadata + if (ctx->chunk_index == ctx->chunk_count - 1) { + ctx->txn->shadow_md.first_serial = ctx->serial_to; + ctx->txn->shadow_md.changeset_count--; + if (serial_equal(ctx->txn->shadow_md.last_flushed, ctx->serial)) { + ctx->txn->shadow_md.flags &= ~LAST_FLUSHED_VALID; + ds->to_be_freed = ds->freed_approx; // prevents deleting unflushed changesets + } + if (serial_equal(ctx->txn->shadow_md.last_serial, ctx->serial)) { + ctx->txn->shadow_md.flags &= ~SERIAL_TO_VALID; + } + ds->freed_approx++; + } + return KNOT_EOK; +} + +/*! + * \brief Deletes specified number of changesets + * + * It tries deleting olny flushed changesets, preserves all unflushed ones. + * + * \retval KNOT_EOK if no error, even if too little or nothing deleted (check really_deleted for result) + * \return KNOT_E* if error + */ +static int delete_count(journal_t *j, txn_t *_txn, size_t to_be_deleted, size_t *really_deleted) +{ + reuse_txn(txn, j, _txn, true); + txn_check_ret(txn); + + if (!md_flag(txn, LAST_FLUSHED_VALID)) { + *really_deleted = 0; + return KNOT_EOK; + } + delete_status_t ds = { .freed_approx = 0, .to_be_freed = to_be_deleted }; + iterate(j, txn, del_count_itercb, JOURNAL_ITERATION_CHUNKS, &ds, + txn->shadow_md.first_serial, txn->shadow_md.last_serial, normal_iterkeycb); + unreuse_txn(txn, _txn); + + if (txn->ret == KNOT_EOK) { + *really_deleted = ds.freed_approx; + } + return txn->ret; +} + +static int delete_dirty_serial(journal_t *j, txn_t *_txn) +{ + reuse_txn(txn, j, _txn, true); + txn_check_ret(txn); + + if (!md_flag(txn, DIRTY_SERIAL_VALID)) return KNOT_EOK; + + uint32_t ds = txn->shadow_md.dirty_serial, chunk = 0; + + txn_key_2u32(txn, j->zone, ds, chunk); + while (txn_find(txn)) { + txn_del(txn); + txn_key_2u32(txn, j->zone, ds, ++chunk); + } + unreuse_txn(txn, _txn); + if (txn->ret == KNOT_EOK) { + txn->shadow_md.flags &= ~DIRTY_SERIAL_VALID; + } + return txn->ret; +} + +static int delete_bootstrap_changeset(journal_t *j, txn_t *_txn) +{ + reuse_txn(txn, j, _txn, false); + uint32_t chunk = 0; + txn_key_str_u32(txn, j->zone, KEY_BOOTSTRAP_CHANGESET, chunk); + while (txn_find(txn)) { + txn_del(txn); + txn_key_str_u32(txn, j->zone, KEY_BOOTSTRAP_CHANGESET, ++chunk); + } + unreuse_txn(txn, _txn); + return txn->ret; +} + +/* + * ***************************** PART VI ****************************** + * + * Writing changesets + * + * ******************************************************************** + */ + +static int merge_itercb(iteration_ctx_t *ctx) +{ + changeset_t *ch = NULL, *mch = *(changeset_t **)ctx->iter_context; + + int ret = vals_to_changeset(ctx->val, ctx->chunk_count, ctx->txn->j->zone, &ch); + if (ret == KNOT_EOK) { + ret = changeset_merge(mch, ch, 0); + changeset_free(ch); + } + return ret; +} + +static int merge_unflushed_changesets(journal_t *j, txn_t *_txn, changeset_t **mch, bool *merged_bootstrap) +{ + reuse_txn(txn, j, _txn, false); + txn_check_ret(txn); + *mch = NULL; + if (md_flushed(txn)) { + goto m_u_ch_end; + } + uint32_t from; + txn->ret = load_bootstrap_changeset(j, txn, mch); + *merged_bootstrap = (txn->ret == KNOT_EOK); + if (txn->ret == KNOT_ENOENT) { // no bootstrap changeset (normal operation) + bool was_merged = md_flag(txn, MERGED_SERIAL_VALID); + bool was_flushed = md_flag(txn, LAST_FLUSHED_VALID); + txn->ret = KNOT_EOK; + from = was_merged ? txn->shadow_md.merged_serial : + (was_flushed ? txn->shadow_md.last_flushed : + txn->shadow_md.first_serial); + txn->ret = load_one(j, txn, from, mch); + if (!was_merged && was_flushed && txn->ret == KNOT_EOK) { + // we have to jump to ONE AFTER last_flushed + from = knot_soa_serial((*mch)->soa_to->rrs.rdata); + changeset_free(*mch); + *mch = NULL; + txn->ret = load_one(j, txn, from, mch); + } + } + if (txn->ret != KNOT_EOK) { + goto m_u_ch_end; + } + from = knot_soa_serial((*mch)->soa_to->rrs.rdata); + + if (!serial_equal(from, txn->shadow_md.last_serial_to)) { + txn->ret = iterate(j, txn, merge_itercb, JOURNAL_ITERATION_CHANGESETS, + mch, from, txn->shadow_md.last_serial, normal_iterkeycb); + } + +m_u_ch_end: + unreuse_txn(txn, _txn); + if (txn->ret != KNOT_EOK && *mch != NULL) { + changeset_free(*mch); + *mch = NULL; + } + return txn->ret; +} + +dynarray_declare(chunk, knot_db_val_t, DYNARRAY_VISIBILITY_STATIC, 32) +dynarray_define(chunk, knot_db_val_t, DYNARRAY_VISIBILITY_STATIC) + +// uses local context, e.g.: j, txn, changesets, nchs, serialized_size_total, store_changeset_cleanup, inserting_merged +#define try_flush \ + if (!md_flushed(txn)) { \ + if (journal_merge_allowed(j)) { \ + changeset_t *merged; \ + merge_unflushed_changesets(j, txn, &merged, &merged_into_bootstrap); \ + if (txn->ret != KNOT_EOK) { \ + goto store_changeset_cleanup; \ + } \ + add_tail(changesets, &merged->n); \ + nchs++; \ + serialized_size_merged += changeset_serialized_size(merged); \ + md_flush(txn); \ + inserting_merged = true; \ + } \ + else { \ + txn->ret = KNOT_EBUSY; \ + goto store_changeset_cleanup; \ + } \ + } + +static int store_changesets(journal_t *j, list_t *changesets) +{ + // PART 1 : initializers, compute serialized_sizes, transaction start + changeset_t *ch; + + size_t nchs = 0, inserted_size = 0; + size_t serialized_size_changes = 0, serialized_size_merged = 0; + + size_t chunks = 0; + + bool inserting_merged = false; + bool merged_into_bootstrap = false; + bool inserting_bootstrap = false; + + size_t occupied_last, occupied_now = knot_db_lmdb_get_usage(j->db->db); + + WALK_LIST(ch, *changesets) { + nchs++; + serialized_size_changes += changeset_serialized_size(ch); + if (ch->soa_from == NULL) { + inserting_bootstrap = true; + } + } + + local_txn_t(txn, j); + txn_begin(txn, true); + + bool zone_in_journal = has_bootstrap_changeset(j, txn); + bool merge_allowed = journal_merge_allowed(j); + + // if you're tempted to add dirty_serial deletion somewhere here, you're wrong. Don't do it. + + // PART 2 : recalculating the previous insert's occupy change + md_get_common_last_occupied(txn, &occupied_last); + md_set(txn, NULL, MDKEY_GLOBAL_LAST_TOTAL_OCCUPIED, occupied_now); + if (occupied_now != occupied_last) { + knot_dname_t *last_zone = NULL; + uint64_t lz_occupied; + md_get_common_last_inserter_zone(txn, &last_zone); + if (last_zone != NULL) { + md_get(txn, last_zone, MDKEY_PERZONE_OCCUPIED, &lz_occupied); + lz_occupied = (lz_occupied + occupied_now > occupied_last ? + lz_occupied + occupied_now - occupied_last : 0); + md_set(txn, last_zone, MDKEY_PERZONE_OCCUPIED, lz_occupied); + free(last_zone); + } + } + md_set_common_last_inserter_zone(txn, j->zone); + + // PART 3a : delete all if inserting bootstrap changeset + if (inserting_bootstrap) { + drop_journal(j, txn); + txn_restart(txn); + } + + // PART 3b : check if we exceeded designed occupation and delete some + uint64_t occupied = 0, occupied_max; + md_get(txn, j->zone, MDKEY_PERZONE_OCCUPIED, &occupied); + occupied_max = journal_max_usage(j); + occupied += serialized_size_changes; + if (occupied > occupied_max) { + size_t freed; + size_t tofree = (occupied - occupied_max) * journal_tofree_factor(j); + size_t free_min = tofree * journal_minfree_factor(j); + delete_tofree(j, txn, tofree, &freed); + if (freed < free_min) { + tofree -= freed; + free_min -= freed; + try_flush + tofree += serialized_size_merged; + delete_tofree(j, txn, tofree, &freed); + if (freed < free_min) { + txn->ret = KNOT_ESPACE; + log_zone_warning(j->zone, "journal, unable to make free space for insert, " + "required: %"PRIu64", max: %"PRIu64, + occupied, occupied_max); + goto store_changeset_cleanup; + } + } + } + + // PART 3c : check if we exceeded history depth + long over_limit = (long)txn->shadow_md.changeset_count - journal_max_changesets(j) + + list_size(changesets) - (inserting_merged ? 1 : 0); + if (zone_in_journal && over_limit > 0 && !merge_allowed) { + txn->ret = KNOT_ESPACE; + log_zone_warning(j->zone, "journal, unable to make free slot for insert"); + goto store_changeset_cleanup; + } else if (over_limit > 0) { + size_t deled; + delete_count(j, txn, over_limit, &deled); + over_limit -= deled; + if (over_limit > 0) { + try_flush + delete_count(j, txn, over_limit, &deled); + // ignore further errors here, the limit is not so important + } + } + + // PART 4: continuity and duplicity check + changeset_t * chs_head = (HEAD(*changesets)); + bool is_first_bootstrap = (chs_head->soa_from == NULL); + uint32_t serial = is_first_bootstrap ? 0 : knot_soa_serial(chs_head->soa_from->rrs.rdata); + if (md_flag(txn, SERIAL_TO_VALID) && (is_first_bootstrap || + !serial_equal(txn->shadow_md.last_serial_to, serial)) && + !inserting_bootstrap /* if inserting bootstrap, drop_journal() was called, so no discontinuity */) { + log_zone_warning(j->zone, "journal, discontinuity in changes history (%u -> %u), dropping older changesets", + txn->shadow_md.last_serial_to, serial); + if (zone_in_journal) { + txn->ret = KNOT_ERANGE; // we can't drop history if zone-in-journal, so this is forbidden + goto store_changeset_cleanup; + } else if (merge_allowed) { + // flush would only merge and drop would delete the merge, so skip it + } else { + try_flush + } + drop_journal(j, txn); + txn_restart(txn); + } + WALK_LIST(ch, *changesets) { + uint32_t serial_to = knot_soa_serial(ch->soa_to->rrs.rdata); + bool is_this_bootstrap = (ch->soa_from == NULL); + bool is_this_merged = (inserting_merged && ch == TAIL(*changesets)); + if (is_this_bootstrap || is_this_merged) { + continue; + } + txn_key_2u32(txn, j->zone, serial_to, 0); + if (txn_find(txn)) { + log_zone_warning(j->zone, "journal, duplicate changeset serial (%u), dropping older changesets", + serial_to); + if (zone_in_journal) { + if (merge_allowed) { + try_flush // merge will get rid of the duplicity => OK + } else { + txn->ret = KNOT_EEXIST; // we can't fix it in this case, refuse to do it + goto store_changeset_cleanup; + } + } else { + try_flush + } + delete_upto(j, txn, txn->shadow_md.first_serial, serial_to); + txn_restart(txn); + } + } + + // PART 5: serializing into lmdb + WALK_LIST(ch, *changesets) { + if (txn->ret != KNOT_EOK) { + break; + } + + chunk_dynarray_t dchunks = { 0 }; + chunks = 0; + + serialize_ctx_t *sctx = serialize_init(ch); + if (sctx == NULL) { + txn->ret = KNOT_ENOMEM; + break; + } + + bool is_this_merged = (inserting_merged && ch == TAIL(*changesets)); + bool is_this_bootstrap = (ch->soa_from == NULL); + uint32_t serial = is_this_bootstrap ? 0 : knot_soa_serial(ch->soa_from->rrs.rdata); + uint32_t serial_to = knot_soa_serial(ch->soa_to->rrs.rdata); + + while (serialize_unfinished(sctx)) { + size_t chunk_size; + serialize_prepare(sctx, CHUNK_MAX - JOURNAL_HEADER_SIZE, &chunk_size); + if (chunk_size == 0) { + break; + } + + inserted_size += chunk_size; + + if (is_this_bootstrap) { + txn_key_str_u32(txn, j->zone, KEY_BOOTSTRAP_CHANGESET, chunks); + } else { + txn_key_2u32(txn, j->zone, serial, chunks); + } + + txn->val.data = NULL; + txn->val.len = chunk_size + JOURNAL_HEADER_SIZE; + + txn_insert(txn); + if (txn->ret != KNOT_EOK) break; + + chunk_dynarray_add(&dchunks, &txn->val); + + chunks++; + + serialize_chunk(sctx, txn->val.data + JOURNAL_HEADER_SIZE, chunk_size); + } + + serialize_deinit(sctx); + + dynarray_foreach(chunk, knot_db_val_t, val, dchunks) { + make_header(val, serial_to, chunks); + } + chunk_dynarray_free(&dchunks); + + // PART 7: metadata update + if (txn->ret != KNOT_EOK) { + break; + } + if (is_this_merged && !merged_into_bootstrap) { + txn->shadow_md.flags |= MERGED_SERIAL_VALID; + txn->shadow_md.merged_serial = serial; + } + else if (is_this_bootstrap) { + if (!md_flag(txn, SERIAL_TO_VALID) || !is_this_merged) { + txn->shadow_md.flags |= FIRST_SERIAL_INVALID; + txn->shadow_md.last_serial_to = serial_to; + } + txn->shadow_md.flags |= SERIAL_TO_VALID; + } else { + if (!md_flag(txn, SERIAL_TO_VALID) || md_flag(txn, FIRST_SERIAL_INVALID)) { + txn->shadow_md.first_serial = serial; + } + txn->shadow_md.flags &= ~FIRST_SERIAL_INVALID; + txn->shadow_md.flags |= SERIAL_TO_VALID; + txn->shadow_md.last_serial = serial; + txn->shadow_md.last_serial_to = serial_to; + txn->shadow_md.changeset_count++; + } + } + + // PART X : finalization and cleanup + +store_changeset_cleanup: + + txn_commit(txn); + + if (txn->ret != KNOT_EOK) { + local_txn_t(ddtxn, j); + txn_begin(ddtxn, true); + if (md_flag(ddtxn, DIRTY_SERIAL_VALID)) { + delete_dirty_serial(j, ddtxn); + } + txn_commit(ddtxn); + } + + changeset_t *dbgchst = TAIL(*changesets); + + if (inserting_merged) { + // free the merged changeset + rem_node(&dbgchst->n); + changeset_free(dbgchst); + } + + return txn->ret; +} +#undef try_flush + +int journal_store_changeset(journal_t *journal, changeset_t *ch) +{ + if (journal == NULL || journal->db == NULL || ch == NULL) return KNOT_EINVAL; + + changeset_t *ch_shallowcopy = malloc(sizeof(changeset_t)); + if (ch_shallowcopy == NULL) { + return KNOT_ENOMEM; + } + memcpy(ch_shallowcopy, ch, sizeof(changeset_t)); // we need to copy the changeset_t structure not to break ch->n + + list_t list; + init_list(&list); + add_tail(&list, &ch_shallowcopy->n); + int ret = store_changesets(journal, &list); + + free(ch_shallowcopy); + return ret; +} + +int journal_store_changesets(journal_t *journal, list_t *src) +{ + if (journal == NULL || journal->db == NULL || src == NULL) return KNOT_EINVAL; + return store_changesets(journal, src); +} + +/* + * **************************** PART VII ****************************** + * + * Journal initialization and global manipulation + * + * ******************************************************************** + */ + +journal_t *journal_new() +{ + return calloc(1, sizeof(journal_t)); +} + +void journal_free(journal_t **journal) +{ + if (journal == NULL || *journal == NULL) return; + + if ((*journal)->zone != NULL) { + free((knot_dname_t *)(*journal)->zone); + } + free(*journal); + *journal = NULL; +} + +static int open_journal_db_unsafe(journal_db_t **db) +{ + if ((*db)->db != NULL) return KNOT_EOK; + + struct knot_db_lmdb_opts opts = KNOT_DB_LMDB_OPTS_INITIALIZER; + opts.path = (*db)->path; + opts.mapsize = (*db)->fslimit; + opts.maxdbs = 1; + opts.maxreaders = JOURNAL_MAX_READERS; + opts.flags.env = ((*db)->mode == JOURNAL_MODE_ASYNC ? + KNOT_DB_LMDB_WRITEMAP | KNOT_DB_LMDB_MAPASYNC : 0); + opts.flags.env |= KNOT_DB_LMDB_NOTLS; + + int ret = (*db)->db_api->init(&(*db)->db, NULL, &opts); + if (ret != KNOT_EOK) { + (*db)->db = NULL; + return ret; + } + + size_t real_fslimit = knot_db_lmdb_get_mapsize((*db)->db); + (*db)->fslimit = real_fslimit; + + return KNOT_EOK; +} + +int journal_open_db(journal_db_t **db) +{ + if (*db == NULL) return KNOT_EINVAL; + pthread_mutex_lock(&(*db)->db_mutex); + int ret = open_journal_db_unsafe(db); + pthread_mutex_unlock(&(*db)->db_mutex); + return ret; +} + +int journal_open(journal_t *journal, journal_db_t **db, const knot_dname_t *zone_name) +{ + int ret = KNOT_EOK; + + if (journal == NULL || (*db) == NULL) return KNOT_EINVAL; + if (journal->db != NULL) { + return KNOT_EOK; + } + + // open shared journal DB if not already + if ((*db)->db == NULL) { + ret = journal_open_db(db); + } + if (ret != KNOT_EOK) { + return ret; + } + journal->db = *db; + + journal->zone = knot_dname_copy(zone_name, NULL); + if (journal->zone == NULL) { + return KNOT_ENOMEM; + } + + bool dirty_serial_valid; + ret = initial_md_check(journal, &dirty_serial_valid); + + if (ret == KNOT_EOK && dirty_serial_valid) { + delete_dirty_serial(journal, NULL); + } + + return ret; +} + +void journal_close(journal_t *journal) +{ + journal->db = NULL; + free(journal->zone); + journal->zone = NULL; +} + +int journal_db_init(journal_db_t **db, const char *lmdb_dir_path, size_t lmdb_fslimit, + journal_mode_t mode) +{ + if (*db != NULL) { + return KNOT_EOK; + } + *db = malloc(sizeof(journal_db_t)); + if (*db == NULL) { + return KNOT_ENOMEM; + } + journal_db_t dbinit = { + .db = NULL, + .db_api = knot_db_lmdb_api(), + .path = strdup(lmdb_dir_path), + .fslimit = ((lmdb_fslimit < JOURNAL_MIN_FSLIMIT) ? JOURNAL_MIN_FSLIMIT : lmdb_fslimit), + .mode = mode, + }; + memcpy(*db, &dbinit, sizeof(journal_db_t)); + pthread_mutex_init(&(*db)->db_mutex, NULL); + return KNOT_EOK; +} + +static void destroy_journal_db(journal_db_t **db) +{ + assert((*db)->db == NULL); + + pthread_mutex_destroy(&(*db)->db_mutex); + free((*db)->path); + free((*db)); + *db = NULL; +} + +void journal_db_close(journal_db_t **db) +{ + if (db == NULL || *db == NULL) { + return; + } + + pthread_mutex_lock(&(*db)->db_mutex); + if ((*db)->db != NULL) { + (*db)->db_api->deinit((*db)->db); + (*db)->db = NULL; + } + pthread_mutex_unlock(&(*db)->db_mutex); + + destroy_journal_db(db); +} + +int journal_flush(journal_t *journal) +{ + if (journal == NULL || journal->db == NULL) { + return KNOT_EINVAL; + } + + local_txn_t(txn, journal); + txn_begin(txn, true); + md_flush(txn); + txn_commit(txn); + return txn->ret; +} + +bool journal_exists(journal_db_t **db, knot_dname_t *zone_name) +{ + if (db == NULL || *db == NULL || zone_name == NULL) { + return false; + } + + if ((*db)->db == NULL) { + struct stat st; + if (stat((*db)->path, &st) != 0 || st.st_size == 0) { + return false; + } + int ret = journal_open_db(db); + if (ret != KNOT_EOK) { + return false; + } + } + + journal_t fake_journal = { .db = *db, .zone = zone_name }; + local_txn_t(txn, &fake_journal); + txn_begin(txn, false); + txn_key_str(txn, zone_name, MDKEY_PERZONE_FLAGS); + bool res = txn_find(txn); + txn_abort(txn); + + return res; +} + +static knot_db_val_t *dbval_copy(const knot_db_val_t *from) +{ + knot_db_val_t *to = malloc(sizeof(knot_db_val_t) + from->len); + if (to != NULL) { + memcpy(to, from, sizeof(knot_db_val_t)); + to->data = to + 1; // == ((uit8_t *)to) + sizeof(knot_db_val_t) + memcpy(to->data, from->data, from->len); + } + return to; +} // TODO think of moving this fun into different place/lib + +int journal_scrape(journal_t *j) +{ + if (j->db == NULL) return KNOT_EINVAL; + local_txn_t(txn, j); + txn_begin(txn, true); + txn_check_ret(txn); + + knot_db_val_t key = { .len = 0, .data = "" }; + + list_t to_del; + init_list(&to_del); + + txn_iter_begin(txn); + while (txn->ret == KNOT_EOK && txn->iter != NULL) { + txn_iter_key(txn, &key); + if (knot_dname_is_equal((const knot_dname_t *) key.data, j->zone) + && key_is_ok(&key, true)) { + knot_db_val_t * inskey = dbval_copy(&key); + if (inskey == NULL) { + txn->ret = KNOT_ENOMEM; + goto scrape_end; + } + ptrlist_add(&to_del, inskey, NULL); + } + txn_iter_next(txn); + } + if (txn->ret == KNOT_ENOENT) { + txn->ret = KNOT_EOK; + } + txn_iter_finish(txn); + + ptrnode_t *del_one; + if (txn->ret == KNOT_EOK) { + WALK_LIST(del_one, to_del) { + txn->ret = j->db->db_api->del(txn->txn, (knot_db_val_t *)del_one->d); + } + if (!EMPTY_LIST(to_del)) { + md_update_journal_count(txn, -1); + } + + md_del_last_inserter_zone(txn, j->zone); + + txn->ret = j->db->db_api->txn_commit(txn->txn); + } +scrape_end: + ptrlist_deep_free(&to_del, NULL); + + return txn->ret; +} + +void journal_metadata_info(journal_t *j, bool *has_bootstrap, kserial_t *merged_serial, + kserial_t *first_serial, kserial_t *last_flushed, kserial_t *serial_to, + uint64_t *occupied, uint64_t *occupied_all_zones) +{ + // NOTE: there is NEVER the situation that only merged changeset would be present and no common changeset in db. + + if (j == NULL || j->db == NULL) { + if (has_bootstrap != NULL) { + *has_bootstrap = false; + } + if (merged_serial != NULL) { + merged_serial->valid = false; + } + if (first_serial != NULL) { + first_serial->valid = false; + } + if (last_flushed != NULL) { + last_flushed->valid = false; + } + if (serial_to != NULL) { + serial_to->valid = false; + } + if (occupied != NULL) { + *occupied = 0; + } + return; + } + + uint64_t occupied_total = knot_db_lmdb_get_usage(j->db->db); + + local_txn_t(txn, j); + txn_begin(txn, false); + txn_check_open(txn); + + if (has_bootstrap != NULL) { + *has_bootstrap = has_bootstrap_changeset(j, txn); + } + if (merged_serial != NULL) { + merged_serial->valid = md_flag(txn, MERGED_SERIAL_VALID); + merged_serial->serial = txn->shadow_md.merged_serial; + } + if (first_serial != NULL) { + first_serial->valid = !md_flag(txn, FIRST_SERIAL_INVALID); + first_serial->serial = txn->shadow_md.first_serial; + } + if (last_flushed != NULL) { + last_flushed->valid = md_flag(txn, LAST_FLUSHED_VALID); + last_flushed->serial = txn->shadow_md.last_flushed; + } + if (serial_to != NULL) { + serial_to->valid = md_flag(txn, SERIAL_TO_VALID); + serial_to->serial = txn->shadow_md.last_serial_to; + } + if (occupied != NULL) { + md_get(txn, j->zone, MDKEY_PERZONE_OCCUPIED, occupied); + knot_dname_t *last_inserter = NULL; + md_get_common_last_inserter_zone(txn, &last_inserter); + if (last_inserter != NULL && knot_dname_is_equal(last_inserter, j->zone)) { + size_t lz_occupied; + md_get_common_last_occupied(txn, &lz_occupied); + *occupied += occupied_total - lz_occupied; + } + free(last_inserter); + } + if (occupied_all_zones != NULL) { + *occupied_all_zones = occupied_total; + } + + txn_abort(txn); +} + +int journal_drop_changesets(journal_t *journal) +{ + return drop_journal(journal, NULL); +} + +int journal_db_list_zones(journal_db_t **db, list_t *zones) +{ + uint64_t expected_count; + + if (list_size(zones) > 0) { + return KNOT_EINVAL; + } + + if ((*db)->db == NULL) { + int ret = journal_open_db(db); + if (ret != KNOT_EOK) { + return ret; + } + } + + journal_t fake_journal = { .db = *db, .zone = (knot_dname_t *)"" }; + local_txn_t(txn, &fake_journal); + txn_begin(txn, false); + md_get(txn, NULL, MDKEY_GLOBAL_JOURNAL_COUNT, &expected_count); + txn_check_ret(txn); + + knot_db_val_t key; + txn_iter_begin(txn); + while (txn->ret == KNOT_EOK && txn->iter != NULL) { + txn_iter_key(txn, &key); + + int metaflag_len = strlen(MDKEY_PERZONE_FLAGS); + char *compare_metaflag = key.data; + compare_metaflag += key.len - 1; + if (txn->ret == KNOT_EOK && *compare_metaflag == '\0') { + compare_metaflag -= metaflag_len; + if (strcmp(compare_metaflag, MDKEY_PERZONE_FLAGS) == 0) { + knot_dname_t *found_zone = knot_dname_copy((const knot_dname_t *)key.data, NULL); + if (found_zone == NULL) { + txn->ret = KNOT_ENOMEM; + break; + } + ptrlist_add(zones, found_zone, NULL); + } + } + txn_iter_next(txn); + } + if (txn->ret == KNOT_ENOENT) { + txn->ret = KNOT_EOK; + } + txn_iter_finish(txn); + txn_abort(txn); + if (list_size(zones) < 1) { + txn->ret = KNOT_ENOENT; + } + return txn->ret; +} + +/* + * *************************** PART VIII ****************************** + * + * Journal check + * + * ******************************************************************** + */ + +static void _jch_print(const knot_dname_t *zname, int warn_level, const char *format, ...) +{ + char buf[512] = "journal check: "; + char *zname_ch = NULL; + + va_list args; + va_start(args, format); + vsprintf(buf + strlen(buf), format, args); + va_end(args); + + switch (warn_level) { + case JOURNAL_CHECK_STDERR: + zname_ch = knot_dname_to_str_alloc(zname); + fprintf(stderr, "[%s] %s\n", zname_ch, buf); + free(zname_ch); + break; + case JOURNAL_CHECK_INFO: + log_zone_info(zname, "%s", buf); + break; + case JOURNAL_CHECK_WARN: + log_zone_error(zname, "%s", buf); + break; + default: + break; + } +} + +#define jch_print(wl, fmt_args...) if ((wl) <= warn_level) _jch_print(j->zone, warn_level, fmt_args) +#define jch_info(fmt_args...) jch_print(JOURNAL_CHECK_INFO, fmt_args) +#define jch_warn(fmt_args...) jch_print((allok = 0, JOURNAL_CHECK_WARN), fmt_args) +#define jch_txn(comment, fatal) do { if (txn->ret != KNOT_EOK) { \ + jch_warn("failed transaction: %s (%s)", (comment), knot_strerror(txn->ret)); \ + if (fatal) return txn->ret; } } while (0) + +int journal_check(journal_t *j, journal_check_level_t warn_level) +{ + int ret, allok = 1; + changeset_t *ch = NULL; + uint32_t sfrom, sto; + uint32_t first_unflushed; + uint32_t chcount; + + jch_info("started"); + + if (j->db == NULL) { + jch_warn("is not open"); + return KNOT_ESEMCHECK; + } + + local_txn_t(txn, j); + txn_begin(txn, true); + jch_txn("begin", true); + + jch_info("metadata: flags >> %d << fs %u ls %u lst %u lf %u ms %u ds %u cnt %u", + txn->shadow_md.flags, txn->shadow_md.first_serial, txn->shadow_md.last_serial, + txn->shadow_md.last_serial_to, txn->shadow_md.last_flushed, txn->shadow_md.merged_serial, + txn->shadow_md.dirty_serial, txn->shadow_md.changeset_count); + + chcount = txn->shadow_md.changeset_count; + first_unflushed = txn->shadow_md.first_serial; + + if (md_flag(txn, DIRTY_SERIAL_VALID)) { + jch_warn("there is some post-crash mess in the DB"); + } + + if (!md_flag(txn, SERIAL_TO_VALID)) { + if (md_flag(txn, LAST_FLUSHED_VALID)) { + jch_warn("journal flagged empty but last_flushed valid"); + } + if (md_flag(txn, MERGED_SERIAL_VALID)) { + jch_warn("no other than merged changeset present, this should not happen"); + } + goto check_merged; + } + + if (md_flag(txn, FIRST_SERIAL_INVALID)) { + jch_info("there is just the bootstrap changeset in journal"); + ret = load_bootstrap_changeset(j, txn, &ch); + if (ret != KNOT_EOK) { + jch_warn("can't read bootstrap changeset (%s)", knot_strerror(ret)); + } else { + changeset_free(ch); + } + goto check_merged; + } else { + ret = load_bootstrap_changeset(j, txn, &ch); + switch (ret) { + case KNOT_EOK: + sto = knot_soa_serial(ch->soa_to->rrs.rdata); + jch_info("bootstrap changeset loaded, sto %u", sto); + changeset_free(ch); + break; + case KNOT_ENOENT: + txn->ret = KNOT_EOK; + break; + default: + jch_info("failed to read bootstrap changeset (%s)", knot_strerror(ret)); + break; + } + } + + ret = load_one(j, txn, txn->shadow_md.first_serial, &ch); + if (ret != KNOT_EOK) { + jch_warn("can't read first changeset %u (%s)", + txn->shadow_md.first_serial, knot_strerror(ret)); + goto check_merged; + } + + sfrom = knot_soa_serial(ch->soa_from->rrs.rdata), sto = knot_soa_serial(ch->soa_to->rrs.rdata); + if (!serial_equal(txn->shadow_md.first_serial, sfrom)) { + jch_warn("first changeset's serial 'from' %u is not ok", sfrom); + } + + if (md_flag(txn, LAST_FLUSHED_VALID)) { + changeset_free(ch); + ret = load_one(j, txn, txn->shadow_md.last_flushed, &ch); + if (ret != KNOT_EOK) { + jch_warn("can't read last flushed changeset %u (%s)", + txn->shadow_md.last_flushed, knot_strerror(ret)); + } else { + first_unflushed = knot_soa_serial(ch->soa_to->rrs.rdata); + } + } + if (ret == KNOT_EOK) { + changeset_free(ch); + } + + if (serial_equal(txn->shadow_md.last_serial_to, sto)) { + jch_info("there is just one changeset in the journal"); + goto check_merged; + } + ret = load_one(j, txn, sto, &ch); + if (ret != KNOT_EOK) { + jch_warn("can't read second changeset %u (%s)", sto, knot_strerror(ret)); + } else { + sfrom = knot_soa_serial(ch->soa_from->rrs.rdata); + if (!serial_equal(sfrom, sto)) { + jch_warn("second changeset's serial 'from' %u is not ok", sfrom); + } + changeset_free(ch); + } + + sfrom = txn->shadow_md.first_serial; + sto = txn->shadow_md.last_serial_to; + txn_commit(txn); + jch_txn("commit", true); + + list_t l; + init_list(&l); + ret = journal_load_changesets(j, &l, sfrom); + if (ret != KNOT_EOK) { + jch_warn("can't read all changesets %u -> %u (%s)", sfrom, sto, knot_strerror(ret)); + goto check_merged; + } + jch_info("listed %zu changesets", list_size(&l)); + if (list_size(&l) != chcount) { + jch_warn("expected %u changesets but found %zu", chcount, list_size(&l)); + } + + ch = HEAD(l); + if (!serial_equal(sfrom, knot_soa_serial(ch->soa_from->rrs.rdata))) { + jch_warn("first listed changeset's serial 'from' %u is not ok", + knot_soa_serial(ch->soa_from->rrs.rdata)); + } + ch = TAIL(l); + if (!serial_equal(sto, knot_soa_serial(ch->soa_to->rrs.rdata))) { + jch_warn("last listed changeset's serial 'to' %u is not ok", + knot_soa_serial(ch->soa_to->rrs.rdata)); + } + changesets_free(&l); + +check_merged: + if (txn->opened) txn_abort(txn); + txn_begin(txn, false); + jch_txn("begin2", true); + if (md_flag(txn, MERGED_SERIAL_VALID)) { + ch = NULL; + ret = load_merged_changeset(j, txn, &ch, NULL); + if (ret != KNOT_EOK) { + jch_warn("can't read merged changeset (%s)", knot_strerror(ret)); + } else { + sfrom = knot_soa_serial(ch->soa_from->rrs.rdata); + sto = knot_soa_serial(ch->soa_to->rrs.rdata); + jch_info("merged changeset %u -> %u (size %zu)", sfrom, sto, + changeset_serialized_size(ch)); + if (!serial_equal(sfrom, txn->shadow_md.merged_serial)) { + jch_warn("merged changeset's serial 'from' is not ok"); + } + if (!serial_equal(sto, first_unflushed)) { + jch_warn("merged changeset's serial 'to' is not ok"); + } + changeset_free(ch); + } + } + txn_commit(txn); + jch_txn("commit2", true); + + if (allok) { + jch_info("passed without errors"); + } + + return (allok ? KNOT_EOK : KNOT_ERROR); +} diff --git a/src/knot/journal/journal.h b/src/knot/journal/journal.h new file mode 100644 index 0000000..113fd71 --- /dev/null +++ b/src/knot/journal/journal.h @@ -0,0 +1,244 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <pthread.h> + +#include "libknot/db/db.h" +#include "contrib/ucw/lists.h" +#include "knot/updates/changesets.h" +#include "knot/journal/serialization.h" +#include "knot/journal/chgset_ctx.h" +#include "knot/zone/serial.h" + +/*! \brief Minimum journal size. */ +#define JOURNAL_MIN_FSLIMIT (1 * 1024 * 1024) + +typedef enum { + JOURNAL_MODE_ROBUST = 0, // Robust journal DB disk synchronization. + JOURNAL_MODE_ASYNC = 1, // Asynchronous journal DB disk synchronization. +} journal_mode_t; + +typedef struct { + knot_db_t *db; + const knot_db_api_t *db_api; + char *path; + size_t fslimit; + journal_mode_t mode; + pthread_mutex_t db_mutex; // please delete this once you move DB opening from journal_open to db_init +} journal_db_t; + +typedef struct { + journal_db_t *db; + knot_dname_t *zone; +} journal_t; + +typedef enum { + JOURNAL_CHECK_SILENT = 0, // No logging, just curious for return value. + JOURNAL_CHECK_WARN = 1, // Log journal inconsistencies. + JOURNAL_CHECK_INFO = 2, // Log journal state. + JOURNAL_CHECK_STDERR = 3, // Log everything and redirect to stderr. +} journal_check_level_t; + +struct journal_txn; + +/*! + * \brief Initialize shared journal DB file. The DB will be open on first use. + * + * \param db Database to be initialized. Must be (*db == NULL) before! + * \param lmdb_dir_path Path to the directory with DB + * \param lmdb_fslimit Maximum size of DB data file + * \param mode Journal DB synchronization mode. + * + * \return KNOT_E* + */ +int journal_db_init(journal_db_t **db, const char *lmdb_dir_path, size_t lmdb_fslimit, + journal_mode_t mode); + +/*! + * \brief Close shared journal DB file. + * + * \param db DB to close. + */ +void journal_db_close(journal_db_t **db); + +/*! + * \brief List the zones contained in journal DB. + * + * \param[in] db Shared journal DB + * \param[out] zones List of strings (char *) of zone names + * + * \return KNOT_EOK ok + * \retval KNOT_ENOMEM no zones found + * \retval KNOT_EMALF different # of zones found than expected + * \retval KNOT_E* other error + */ +int journal_db_list_zones(journal_db_t **db, list_t *zones); + +/*! + * \brief Allocate a new journal structure. + * + * \retval new journal instance if successful. + * \retval NULL on error. + */ +journal_t *journal_new(void); + +/*! + * \brief Free a journal structure. + * + * \param journal A journal structure to free. + */ +void journal_free(journal_t **journal); + +/*! + * \brief Open/create the journal based on the filesystem path to LMDB directory + * + * \param journal Journal struct to use. + * \param db Shared journal database. + * \param zone_name Name of the zone this journal belongs to. + * + * \retval KNOT_EOK on success. + * \return < KNOT_EOK on other errors. + */ +int journal_open(journal_t *journal, journal_db_t **db, + const knot_dname_t *zone_name); + +/*! + * \brief Close journal. + * + * \param journal Journal to close. + */ +void journal_close(journal_t *journal); + +/*! + * \brief Load changesets from journal since "from" serial. + * + * \param journal Journal to load from. + * \param dst Store changesets here. + * \param from Start serial. + * + * \retval KNOT_EOK on success. + * \retval KNOT_ENOENT when the lookup of the first entry fails. + * \return < KNOT_EOK on other error. + */ +int journal_load_changesets(journal_t *journal, list_t *dst, uint32_t from); + +int journal_load_chgset_ctx(journal_t *j, chgset_ctx_list_t *dst, uint32_t from); + +/*! + * \brief Load changesets from journal, starting with bootstrap changeset. + * + * \param journal Journal to load from. + * \param dst Store changesets here, starting with bootstrap changeset. + * + * \retval KNOT_EOK on success. + * \retval KNOT_ENOENT when there is no bootstrap changeset. + * \return < KNOT_EOK on other error. + */ +int journal_load_bootstrap(journal_t *journal, list_t *dst); + +/*! + * \brief Store changesets in journal. + * + * \param journal Journal to store in. + * \param src Changesets to store. + * + * \retval KNOT_EOK on success. + * \retval KNOT_EBUSY when full, asking zone to flush itself to zonefile + * to allow cleaning up history and freeing up space + * \retval KNOT_ESPACE when full and not able to free up any space + * \return < KNOT_EOK on other errors. + */ +int journal_store_changesets(journal_t *journal, list_t *src); + +/*! + * \brief Store changesets in journal. + * + * \param journal Journal to store in. + * \param change Changeset to store. + * + * \retval (same as for journal_store_changesets()) + */ +int journal_store_changeset(journal_t *journal, changeset_t *change); + +/*! + * \brief Open the journal database. + * + * This is an "almost static" function, which is mostly called by other journal + * methods like journal_open() and journal_exists(). However it can be called + * separately just for more precise error resolution. + * + * \param db Journal to be opened. + * \return KNOT_E* + */ +int journal_open_db(journal_db_t **db); + +/*! + * \brief Check if this (zone's) journal is present in shared journal DB. + * + * \param db Shared journal DB + * \param zone_name Name of the zone of the journal in question + * + * \return true or false + */ +bool journal_exists(journal_db_t **db, knot_dname_t *zone_name); + +/*! \brief Tell the journal that zone has been flushed. + * + * \param journal Journal to flush. + * + * \return KNOT_E* + */ +int journal_flush(journal_t *journal); + +/*! \brief Remove completely this (zone's) journal from shared journal DB. + * + * This must be called with opened journal. + * + * \param journal Journal to be deleted + * + * \return KNOT_E* + */ +int journal_scrape(journal_t *journal); + +/*! \brief Obtain public information from journal metadata + */ +void journal_metadata_info(journal_t *journal, bool *is_empty, kserial_t *merged_serial, + kserial_t *first_serial, kserial_t *last_flushed, + kserial_t *serial_to, uint64_t *occupied, uint64_t *occupied_all_zones); + +/*! + * \brief Delete all changesets in zone's journal, keeping metadata. + * + * \param journal Journal to clear. + * + * \return KNOT_E* + */ +int journal_drop_changesets(journal_t *journal); + +/*! \brief Check the journal consistency, errors to stderr. + * + * \param journal Journal to check. + * \param warn_level Journal check level. + * + * \return KNOT_E* + */ +int journal_check(journal_t *journal, journal_check_level_t warn_level); + +void journal_txn_commit(struct journal_txn *txn); + +/*! @} */ diff --git a/src/knot/journal/serialization.c b/src/knot/journal/serialization.c new file mode 100644 index 0000000..75e18a2 --- /dev/null +++ b/src/knot/journal/serialization.c @@ -0,0 +1,380 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> + +#include "knot/journal/serialization.h" +#include "libknot/libknot.h" + +#define SERIALIZE_RRSET_INIT (-1) +#define SERIALIZE_RRSET_DONE ((1L<<16)+1) + +typedef enum { + PHASE_SOA_1, + PHASE_REM, + PHASE_SOA_2, + PHASE_ADD, + PHASE_END, +} serialize_phase_t; + +#define RRSET_BUF_MAXSIZE 256 + +struct serialize_ctx { + const changeset_t *ch; + changeset_iter_t it; + serialize_phase_t changeset_phase; + long rrset_phase; + knot_rrset_t rrset_buf[RRSET_BUF_MAXSIZE]; + size_t rrset_buf_size; +}; + +serialize_ctx_t *serialize_init(const changeset_t *ch) +{ + serialize_ctx_t *ctx = calloc(1, sizeof(*ctx)); + if (ctx == NULL) { + return NULL; + } + + ctx->ch = ch; + ctx->changeset_phase = ch->soa_from != NULL ? PHASE_SOA_1 : PHASE_SOA_2; + ctx->rrset_phase = SERIALIZE_RRSET_INIT; + ctx->rrset_buf_size = 0; + + return ctx; +} + +static knot_rrset_t get_next_rrset(serialize_ctx_t *ctx) +{ + knot_rrset_t res; + knot_rrset_init_empty(&res); + switch (ctx->changeset_phase) { + case PHASE_SOA_1: + changeset_iter_rem(&ctx->it, ctx->ch); + ctx->changeset_phase = PHASE_REM; + return *ctx->ch->soa_from; + case PHASE_REM: + res = changeset_iter_next(&ctx->it); + if (knot_rrset_empty(&res)) { + changeset_iter_clear(&ctx->it); + changeset_iter_add(&ctx->it, ctx->ch); + ctx->changeset_phase = PHASE_ADD; + return *ctx->ch->soa_to; + } + return res; + case PHASE_SOA_2: + if (ctx->it.node != NULL) { + changeset_iter_clear(&ctx->it); + } + changeset_iter_add(&ctx->it, ctx->ch); + ctx->changeset_phase = PHASE_ADD; + return *ctx->ch->soa_to; + case PHASE_ADD: + res = changeset_iter_next(&ctx->it); + if (knot_rrset_empty(&res)) { + changeset_iter_clear(&ctx->it); + ctx->changeset_phase = PHASE_END; + } + return res; + default: + return res; + } +} + +void serialize_prepare(serialize_ctx_t *ctx, size_t max_size, size_t *realsize) +{ + *realsize = 0; + + // check if we are in middle of a rrset + if (ctx->rrset_buf_size > 0) { + ctx->rrset_buf[0] = ctx->rrset_buf[ctx->rrset_buf_size - 1]; + ctx->rrset_buf_size = 1; + } else { + ctx->rrset_buf[0] = get_next_rrset(ctx); + if (ctx->changeset_phase == PHASE_END) { + ctx->rrset_buf_size = 0; + return; + } + ctx->rrset_buf_size = 1; + } + + size_t candidate = 0; + long tmp_phase = ctx->rrset_phase; + while (1) { + if (tmp_phase >= ctx->rrset_buf[ctx->rrset_buf_size - 1].rrs.count) { + if (ctx->rrset_buf_size >= RRSET_BUF_MAXSIZE) { + return; + } + ctx->rrset_buf[ctx->rrset_buf_size++] = get_next_rrset(ctx); + if (ctx->changeset_phase == PHASE_END) { + ctx->rrset_buf_size--; + return; + } + tmp_phase = SERIALIZE_RRSET_INIT; + } + if (tmp_phase == SERIALIZE_RRSET_INIT) { + candidate += 3 * sizeof(uint16_t) + + knot_dname_size(ctx->rrset_buf[ctx->rrset_buf_size - 1].owner); + } else { + candidate += sizeof(uint32_t) + sizeof(uint16_t) + + knot_rdataset_at(&ctx->rrset_buf[ctx->rrset_buf_size - 1].rrs, tmp_phase)->len; + } + if (candidate > max_size) { + return; + } + *realsize = candidate; + tmp_phase++; + } +} + +void serialize_chunk(serialize_ctx_t *ctx, uint8_t *dst_chunk, size_t chunk_size) +{ + wire_ctx_t wire = wire_ctx_init(dst_chunk, chunk_size); + + for (size_t i = 0; ; ) { + if (ctx->rrset_phase >= ctx->rrset_buf[i].rrs.count) { + if (++i >= ctx->rrset_buf_size) { + break; + } + ctx->rrset_phase = SERIALIZE_RRSET_INIT; + } + if (ctx->rrset_phase == SERIALIZE_RRSET_INIT) { + int size = knot_dname_to_wire(wire.position, ctx->rrset_buf[i].owner, + wire_ctx_available(&wire)); + if (size < 0 || wire_ctx_available(&wire) < size + 3 * sizeof(uint16_t)) { + break; + } + wire_ctx_skip(&wire, size); + wire_ctx_write_u16(&wire, ctx->rrset_buf[i].type); + wire_ctx_write_u16(&wire, ctx->rrset_buf[i].rclass); + wire_ctx_write_u16(&wire, ctx->rrset_buf[i].rrs.count); + } else { + const knot_rdata_t *rr = knot_rdataset_at(&ctx->rrset_buf[i].rrs, + ctx->rrset_phase); + assert(rr); + uint16_t rdlen = rr->len; + if (wire_ctx_available(&wire) < sizeof(uint32_t) + sizeof(uint16_t) + rdlen) { + break; + } + // Compatibility, but one TTL per rrset would be enough. + wire_ctx_write_u32(&wire, ctx->rrset_buf[i].ttl); + wire_ctx_write_u16(&wire, rdlen); + wire_ctx_write(&wire, rr->data, rdlen); + } + ctx->rrset_phase++; + } +} + +bool serialize_unfinished(serialize_ctx_t *ctx) +{ + return ctx->changeset_phase < PHASE_END; +} + +void serialize_deinit(serialize_ctx_t *ctx) +{ + if (ctx->it.node != NULL) { + changeset_iter_clear(&ctx->it); + } + free(ctx); +} + +static int deserialize_rrset(wire_ctx_t *wire, knot_rrset_t *rrset, long *phase) +{ + assert(wire != NULL && rrset != NULL && phase != NULL); + assert(*phase >= SERIALIZE_RRSET_INIT && *phase < SERIALIZE_RRSET_DONE); + + if (*phase == SERIALIZE_RRSET_INIT && wire_ctx_available(wire) > 0) { + // Read owner, rtype, rclass and RR count. + size_t size = knot_dname_size(wire->position); + knot_dname_t *owner = knot_dname_copy(wire->position, NULL); + if (owner == NULL || wire_ctx_available(wire) < size + 3 * sizeof(uint16_t)) { + knot_dname_free(owner, NULL); + return KNOT_EMALF; + } + wire_ctx_skip(wire, size); + uint16_t type = wire_ctx_read_u16(wire); + uint16_t rclass = wire_ctx_read_u16(wire); + uint16_t rrcount = wire_ctx_read_u16(wire); + (*phase) = rrcount; + if (wire->error != KNOT_EOK) { + knot_dname_free(owner, NULL); + return wire->error; + } + knot_rrset_init(rrset, owner, type, rclass, 0); + } + + bool first = true; + for ( ; *phase > 0 && wire_ctx_available(wire) > 0; (*phase)--) { + uint32_t ttl = wire_ctx_read_u32(wire); + if (first) { + rrset->ttl = ttl; + first = false; + } + uint32_t rdata_size = wire_ctx_read_u16(wire); + if (wire->error != KNOT_EOK || + wire_ctx_available(wire) < rdata_size || + knot_rrset_add_rdata(rrset, wire->position, rdata_size, + NULL) != KNOT_EOK) { + knot_rrset_clear(rrset, NULL); + return KNOT_EMALF; + } + wire_ctx_skip(wire, rdata_size); + } + + if (*phase == 0) { + *phase = SERIALIZE_RRSET_DONE; + } + return KNOT_EOK; +} + +int deserialize_rrset_chunks(wire_ctx_t *wire, knot_rrset_t *rrset, + uint8_t *src_chunks[], const size_t *chunk_sizes, + size_t chunks_count, size_t *cur_chunk) +{ + long phase = SERIALIZE_RRSET_INIT; + while (1) { + int ret = deserialize_rrset(wire, rrset, &phase); + if (ret != KNOT_EOK || phase == SERIALIZE_RRSET_DONE) { + return ret; + } + // now the rrset wasn't whole on this chunk + if (*cur_chunk >= chunks_count - 1) { + return KNOT_EMALF; + } + if (wire->error != KNOT_EOK) { + return wire->error; + } + (*cur_chunk)++; + assert(chunk_sizes[*cur_chunk] > 0); + *wire = wire_ctx_init(src_chunks[*cur_chunk], chunk_sizes[*cur_chunk]); + } +} + +static uint64_t rrset_binary_size(const knot_rrset_t *rrset) +{ + if (rrset == NULL || rrset->rrs.count == 0) { + return 0; + } + + // Owner size + type + class + RR count. + uint64_t size = knot_dname_size(rrset->owner) + 3 * sizeof(uint16_t); + + // RRs. + knot_rdata_t *rr = rrset->rrs.rdata; + for (uint16_t i = 0; i < rrset->rrs.count; i++) { + // TTL + RR size + RR. + size += sizeof(uint32_t) + sizeof(uint16_t) + rr->len; + rr = knot_rdataset_next(rr); + } + + return size; +} + +size_t changeset_serialized_size(const changeset_t *ch) +{ + if (ch == NULL) { + return 0; + } + + size_t soa_from_size = rrset_binary_size(ch->soa_from); + size_t soa_to_size = rrset_binary_size(ch->soa_to); + + changeset_iter_t it; + changeset_iter_all(&it, ch); + + size_t change_size = 0; + knot_rrset_t rrset = changeset_iter_next(&it); + while (!knot_rrset_empty(&rrset)) { + change_size += rrset_binary_size(&rrset); + rrset = changeset_iter_next(&it); + } + + changeset_iter_clear(&it); + + return soa_from_size + soa_to_size + change_size; +} + +int changeset_deserialize(changeset_t *ch, uint8_t *src_chunks[], + const size_t *chunks_sizes, size_t chunks_count) +{ + if (ch == NULL || src_chunks == NULL || chunks_sizes == NULL || + chunks_count == 0) { + return KNOT_EINVAL; + } + + size_t cur_chunk = 0; + wire_ctx_t wire = wire_ctx_init_const(src_chunks[0], chunks_sizes[0]); + + // Deserialize SOA 'from'. + knot_rrset_t rrset; + int ret = deserialize_rrset_chunks(&wire, &rrset, src_chunks, chunks_sizes, + chunks_count, &cur_chunk); + if (ret != KNOT_EOK) { + return ret; + } + assert(rrset.type == KNOT_RRTYPE_SOA); + + ch->soa_from = knot_rrset_copy(&rrset, NULL); + knot_rrset_clear(&rrset, NULL); + if (ch->soa_from == NULL) { + return KNOT_ENOMEM; + } + + // Read remaining RRSets. + bool in_remove_section = true; + while (cur_chunk < chunks_count - 1 || wire_ctx_available(&wire) > 0) { + // Parse next RRSet. + ret = deserialize_rrset_chunks(&wire, &rrset, src_chunks, chunks_sizes, + chunks_count, &cur_chunk); + if (ret != KNOT_EOK) { + break; + } + + // Check for next (and also last) SOA. + if (rrset.type == KNOT_RRTYPE_SOA) { + // Move to ADD section if in REMOVE. + assert(in_remove_section); + in_remove_section = false; + + ch->soa_to = knot_rrset_copy(&rrset, NULL); + if (ch->soa_to == NULL) { + ret = KNOT_ENOMEM; + } + } else { + if (in_remove_section) { + ret = changeset_add_removal(ch, &rrset, 0); + } else { + ret = changeset_add_addition(ch, &rrset, 0); + } + } + + knot_rrset_clear(&rrset, NULL); + + if (ret != KNOT_EOK) { + return ret; + } + } + + // If there was only one SOA record, we are in the bootstrap changeset. + if (in_remove_section) { + ch->soa_to = ch->soa_from; + ch->soa_from = NULL; + zone_contents_t *tmp = ch->add; + ch->add = ch->remove; + ch->remove = tmp; + } + + return wire.error; +} diff --git a/src/knot/journal/serialization.h b/src/knot/journal/serialization.h new file mode 100644 index 0000000..550bd1c --- /dev/null +++ b/src/knot/journal/serialization.h @@ -0,0 +1,98 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <stdint.h> +#include "libknot/rrset.h" +#include "knot/updates/changesets.h" +#include "contrib/wire_ctx.h" + +typedef struct serialize_ctx serialize_ctx_t; + +/*! + * \brief Init serialization context. + * + * \param ch Changeset to be serialized. + * + * \return Context. + */ +serialize_ctx_t *serialize_init(const changeset_t *ch); + +/*! + * \brief Pre-check and space computation before serializing a chunk. + * + * \note This MUST be called before each serialize_chunk() ! + * + * \param ctx Serializing context. + * \param max_size Maximum size of next chunk. + * \param realsize Output: real exact size of next chunk. + */ +void serialize_prepare(serialize_ctx_t *ctx, size_t max_size, size_t *realsize); + +/*! + * \brief Perform one step of serializiation: fill one chunk. + * + * \param ctx Serializing context. + * \param chunk Pointer on allocated memory to be serialized into. + * \param chunk_size Its size. It MUST be the same as returned from serialize_prepare(). + */ +void serialize_chunk(serialize_ctx_t *ctx, uint8_t *chunk, size_t chunk_size); + +/*! \brief Tells if there remains something of the changeset + * to be serialized into next chunk(s) yet. */ +bool serialize_unfinished(serialize_ctx_t *ctx); + +/*! \brief Free serialization context. */ +void serialize_deinit(serialize_ctx_t *ctx); + +/*! + * \brief Returns size of changeset in serialized form. + * + * \param[in] ch Changeset whose size we want to compute. + * + * \return Size of the changeset. + */ +size_t changeset_serialized_size(const changeset_t *ch); + +/*! + * \brief Deserializes chunked area into ch + * + * \param[out] ch The changeset. + * \param[in] src_chunks The chunks to deserialize. + * \param[in] chunks_sizes The size of each chunk. + * \param[in] chunks_count The number of chunks. + * + * \retval KNOT_E* + */ +int changeset_deserialize(changeset_t *ch, uint8_t *src_chunks[], + const size_t *chunks_sizes, size_t chunks_count); + +/*! + * \brief Deserializes single RRSet being part of a changeset serialized in chunks. + * + * \param wire[in] Current chunk ready to be parsed. + * \param rrset[out] RRSet to be deserialized (empty before). + * \param src_chunks[in] All chunks of the serialized changeset. + * \param chunk_sizes[in] Their sizes. + * \param chunks_count[in] Their count. + * \param cur_chunk[in+out] Index of current chunk. + * + * \return KNOT_E* + */ +int deserialize_rrset_chunks(wire_ctx_t *wire, knot_rrset_t *rrset, + uint8_t *src_chunks[], const size_t *chunk_sizes, + size_t chunks_count, size_t *cur_chunk); diff --git a/src/knot/modules/cookies/Makefile.inc b/src/knot/modules/cookies/Makefile.inc new file mode 100644 index 0000000..c40abed --- /dev/null +++ b/src/knot/modules/cookies/Makefile.inc @@ -0,0 +1,13 @@ +knot_modules_cookies_la_SOURCES = knot/modules/cookies/cookies.c +EXTRA_DIST += knot/modules/cookies/cookies.rst + +if STATIC_MODULE_cookies +libknotd_la_SOURCES += $(knot_modules_cookies_la_SOURCES) +endif + +if SHARED_MODULE_cookies +knot_modules_cookies_la_LDFLAGS = $(KNOTD_MOD_LDFLAGS) +knot_modules_cookies_la_CPPFLAGS = $(KNOTD_MOD_CPPFLAGS) +knot_modules_cookies_la_LIBADD = libcontrib.la +pkglib_LTLIBRARIES += knot/modules/cookies.la +endif diff --git a/src/knot/modules/cookies/cookies.c b/src/knot/modules/cookies/cookies.c new file mode 100644 index 0000000..996c80d --- /dev/null +++ b/src/knot/modules/cookies/cookies.c @@ -0,0 +1,269 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <pthread.h> +#include <unistd.h> + +#include "knot/include/module.h" +#include "libknot/libknot.h" +#include "contrib/string.h" +#include "libdnssec/random.h" + +#ifdef HAVE_ATOMIC +#define ATOMIC_SET(dst, val) __atomic_store_n(&(dst), (val), __ATOMIC_RELAXED) +#define ATOMIC_GET(src) __atomic_load_n(&(src), __ATOMIC_RELAXED) +#define ATOMIC_ADD(dst, val) __atomic_add_fetch(&(dst), (val), __ATOMIC_RELAXED) +#else +#define ATOMIC_SET(dst, val) ((dst) = (val)) +#define ATOMIC_GET(src) (src) +#define ATOMIC_ADD(dst, val) ((dst) += (val)) +#endif + +#define BADCOOKIE_CTR_INIT 1 + +#define MOD_SECRET_LIFETIME "\x0F""secret-lifetime" +#define MOD_BADCOOKIE_SLIP "\x0E""badcookie-slip" + +const yp_item_t cookies_conf[] = { + { MOD_SECRET_LIFETIME, YP_TINT, YP_VINT = { 1, 36*24*3600, 26*3600, YP_STIME } }, + { MOD_BADCOOKIE_SLIP, YP_TINT, YP_VINT = { 1, INT32_MAX, 1 } }, + { NULL } +}; + +typedef struct { + struct { + uint64_t variable; + uint64_t constant; + } secret; + pthread_t update_secret; + uint32_t secret_lifetime; + uint32_t badcookie_slip; + uint16_t badcookie_ctr; // Counter for BADCOOKIE answers. +} cookies_ctx_t; + +static void update_ctr(cookies_ctx_t *ctx) +{ + assert(ctx); + + if (ATOMIC_GET(ctx->badcookie_ctr) < ctx->badcookie_slip) { + ATOMIC_ADD(ctx->badcookie_ctr, 1); + } else { + ATOMIC_SET(ctx->badcookie_ctr, BADCOOKIE_CTR_INIT); + } +} + +static int generate_secret(cookies_ctx_t *ctx) +{ + assert(ctx); + + // Generate a new variable part of the server secret. + uint64_t new_secret; + int ret = dnssec_random_buffer((uint8_t *)&new_secret, sizeof(new_secret)); + if (ret != KNOT_EOK) { + return ret; + } + + ATOMIC_SET(ctx->secret.variable, new_secret); + + return KNOT_EOK; +} + +static void *update_secret(void *data) +{ + knotd_mod_t *mod = (knotd_mod_t *)data; + cookies_ctx_t *ctx = knotd_mod_ctx(mod); + + while (true) { + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); + int ret = generate_secret(ctx); + if (ret != KNOT_EOK) { + knotd_mod_log(mod, LOG_ERR, "failed to generate a secret (%s)", + knot_strerror(ret)); + } + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + sleep(ctx->secret_lifetime); + } + + return NULL; +} + +// Inserts the current cookie option into the answer's OPT RR. +static int put_cookie(knotd_qdata_t *qdata, knot_pkt_t *pkt, + const knot_edns_cookie_t *cc, const knot_edns_cookie_t *sc) +{ + assert(qdata && pkt && cc && sc); + + uint8_t *option = NULL; + uint16_t option_size = knot_edns_cookie_size(cc, sc); + int ret = knot_edns_reserve_option(&qdata->opt_rr, KNOT_EDNS_OPTION_COOKIE, + option_size, &option, qdata->mm); + if (ret != KNOT_EOK) { + return ret; + } + + ret = knot_edns_cookie_write(option, option_size, cc, sc); + if (ret != KNOT_EOK) { + return ret; + } + + // Reserve extra space for the cookie option. + ret = knot_pkt_reserve(pkt, KNOT_EDNS_OPTION_HDRLEN + option_size); + if (ret != KNOT_EOK) { + return ret; + } + + return KNOT_EOK; +} + +static knotd_state_t cookies_process(knotd_state_t state, knot_pkt_t *pkt, + knotd_qdata_t *qdata, knotd_mod_t *mod) +{ + assert(pkt && qdata && mod); + + cookies_ctx_t *ctx = knotd_mod_ctx(mod); + + // DNS cookies are ignored in the case of the TCP connection. + if (!(qdata->params->flags & KNOTD_QUERY_FLAG_LIMIT_SIZE)) { + return state; + } + + // Check if the cookie option is present. + uint8_t *cookie_opt = knot_pkt_edns_option(qdata->query, + KNOT_EDNS_OPTION_COOKIE); + if (cookie_opt == NULL) { + return state; + } + + // Increment the statistics counter. + knotd_mod_stats_incr(mod, 0, 0, 1); + + knot_edns_cookie_t cc; + knot_edns_cookie_t sc; + + // Parse the cookie from wireformat. + const uint8_t *data = knot_edns_opt_get_data(cookie_opt); + uint16_t data_len = knot_edns_opt_get_length(cookie_opt); + int ret = knot_edns_cookie_parse(&cc, &sc, data, data_len); + if (ret != KNOT_EOK) { + qdata->rcode = KNOT_RCODE_FORMERR; + return KNOTD_STATE_FAIL; + } + + // Prepare data for server cookie computation. + knot_edns_cookie_params_t params; + params.client_addr = (struct sockaddr *)qdata->params->remote; + uint64_t current_secret = ATOMIC_GET(ctx->secret.variable); + memcpy(params.secret, ¤t_secret, sizeof(current_secret)); + memcpy(params.secret + sizeof(current_secret), &ctx->secret.constant, + sizeof(ctx->secret.constant)); + + // Compare server cookie. + ret = knot_edns_cookie_server_check(&sc, &cc, ¶ms); + if (ret != KNOT_EOK) { + if (ATOMIC_GET(ctx->badcookie_ctr) > BADCOOKIE_CTR_INIT) { + // Silently drop the response. + update_ctr(ctx); + return KNOTD_STATE_NOOP; + } else { + if (ctx->badcookie_slip > 1) { + update_ctr(ctx); + } + + ret = knot_edns_cookie_server_generate(&sc, &cc, ¶ms); + if (ret != KNOT_EOK) { + return KNOTD_STATE_FAIL; + } + + ret = put_cookie(qdata, pkt, &cc, &sc); + if (ret != KNOT_EOK) { + return KNOTD_STATE_FAIL; + } + + qdata->rcode = KNOT_RCODE_BADCOOKIE; + return KNOTD_STATE_FAIL; + } + } + + // Reuse valid server cookie. + ret = put_cookie(qdata, pkt, &cc, &sc); + if (ret != KNOT_EOK) { + return KNOTD_STATE_FAIL; + } + + // Set the valid cookie flag. + qdata->params->flags |= KNOTD_QUERY_FLAG_COOKIE; + + return state; +} + +int cookies_load(knotd_mod_t *mod) +{ + // Create module context. + cookies_ctx_t *ctx = calloc(1, sizeof(cookies_ctx_t)); + if (ctx == NULL) { + return KNOT_ENOMEM; + } + + // Initialize BADCOOKIE counter. + ctx->badcookie_ctr = BADCOOKIE_CTR_INIT; + + // Set up configurable items. + knotd_conf_t conf = knotd_conf_mod(mod, MOD_SECRET_LIFETIME); + ctx->secret_lifetime = conf.single.integer; + + conf = knotd_conf_mod(mod, MOD_BADCOOKIE_SLIP); + ctx->badcookie_slip = conf.single.integer; + + // Set up statistics counters. + int ret = knotd_mod_stats_add(mod, "presence", 1, NULL); + if (ret != KNOT_EOK) { + free(ctx); + return ret; + } + + // Initialize the server secret. + ret = dnssec_random_buffer((uint8_t *)&ctx->secret, sizeof(ctx->secret)); + if (ret != KNOT_EOK) { + free(ctx); + return ret; + } + + knotd_mod_ctx_set(mod, ctx); + + // Start the secret rollover thread. + if (pthread_create(&ctx->update_secret, NULL, update_secret, (void *)mod)) { + knotd_mod_log(mod, LOG_ERR, "failed to create the secret rollover thread"); + } + +#ifndef HAVE_ATOMIC + knotd_mod_log(mod, LOG_WARNING, "the module might work slightly wrong on this platform"); + ctx->badcookie_slip = 1; +#endif + + return knotd_mod_hook(mod, KNOTD_STAGE_BEGIN, cookies_process); +} + +void cookies_unload(knotd_mod_t *mod) +{ + cookies_ctx_t *ctx = knotd_mod_ctx(mod); + memzero(&ctx->secret, sizeof(ctx->secret)); + (void)pthread_cancel(ctx->update_secret); + (void)pthread_join(ctx->update_secret, NULL); + free(ctx); +} + +KNOTD_MOD_API(cookies, KNOTD_MOD_FLAG_SCOPE_ANY | KNOTD_MOD_FLAG_OPT_CONF, + cookies_load, cookies_unload, cookies_conf, NULL); diff --git a/src/knot/modules/cookies/cookies.rst b/src/knot/modules/cookies/cookies.rst new file mode 100644 index 0000000..2feb270 --- /dev/null +++ b/src/knot/modules/cookies/cookies.rst @@ -0,0 +1,78 @@ +.. _mod-cookies: + +``cookies`` — DNS Cookies +========================= + +DNS Cookies (:rfc:`7873`) is a lightweight security mechanism against +denial-of-service and amplification attacks. The server keeps a secret value +(the server secret), which is used to generate a cookie, which is sent to +the client in the OPT RR. The server then verifies the authenticity of the client +by the presence of a correct cookie. Both the server and the client have to +support DNS Cookies, otherwise they are not used. + +.. NOTE:: + This module introduces a statistics counter: the number of queries + containing the COOKIE option. + +.. WARNING:: + For effective module operation the :ref:`RRL<mod-rrl>` module must also + be enabled. + +Example +------- + +It is recommended to enable DNS Cookies globally, not per zone. + +:: + + mod-cookies: + - id: default + secret-lifetime: 30h # The server secret is regenerated every 30 hours + badcookie-slip: 3 # The server replies only to every third query with a wrong cookie + + template: + - id: default + global-module: mod-cookies/default # Enable DNS Cookies globally + +Module reference +---------------- + +:: + + mod-cookies: + - id: STR + secret-lifetime: TIME + badcookie-slip: INT + +.. _mod-cookies_id: + +id +.. + +A module identifier. + +.. _mod-cookies_secret-lifetime: + +secret-lifetime +............... + +This option configures how often the server secret is regenerated. +The maximum allowed value is 36 days (:rfc:`7873#section-7.1`). + +*Default:* 26 hours + +.. _mod-cookies_badcookie-slip: + +badcookie-slip +.............. + +This option configures how often the server responds to queries containing +an invalid cookie by sending them the correct cookie. + +- The value **1** means that the server responds to every query. +- The value **2** means that the server responds to every second query with + an invalid cookie, the rest of the queries is dropped. +- The value **N > 2** means that the server responds to every N\ :sup:`th` + query with an invalid cookie, the rest of the queries is dropped. + +*Default:* 1 diff --git a/src/knot/modules/dnsproxy/Makefile.inc b/src/knot/modules/dnsproxy/Makefile.inc new file mode 100644 index 0000000..cda2a43 --- /dev/null +++ b/src/knot/modules/dnsproxy/Makefile.inc @@ -0,0 +1,13 @@ +knot_modules_dnsproxy_la_SOURCES = knot/modules/dnsproxy/dnsproxy.c +EXTRA_DIST += knot/modules/dnsproxy/dnsproxy.rst + +if STATIC_MODULE_dnsproxy +libknotd_la_SOURCES += $(knot_modules_dnsproxy_la_SOURCES) +endif + +if SHARED_MODULE_dnsproxy +knot_modules_dnsproxy_la_LDFLAGS = $(KNOTD_MOD_LDFLAGS) +knot_modules_dnsproxy_la_CPPFLAGS = $(KNOTD_MOD_CPPFLAGS) +knot_modules_dnsproxy_la_LIBADD = libcontrib.la +pkglib_LTLIBRARIES += knot/modules/dnsproxy.la +endif diff --git a/src/knot/modules/dnsproxy/dnsproxy.c b/src/knot/modules/dnsproxy/dnsproxy.c new file mode 100644 index 0000000..7d378a3 --- /dev/null +++ b/src/knot/modules/dnsproxy/dnsproxy.c @@ -0,0 +1,165 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "contrib/net.h" +#include "knot/include/module.h" +#include "knot/conf/schema.h" +#include "knot/query/capture.h" // Forces static module! +#include "knot/query/requestor.h" // Forces static module! +#include "knot/nameserver/process_query.h" // Forces static module! + +#define MOD_REMOTE "\x06""remote" +#define MOD_TIMEOUT "\x07""timeout" +#define MOD_FALLBACK "\x08""fallback" +#define MOD_CATCH_NXDOMAIN "\x0E""catch-nxdomain" + +const yp_item_t dnsproxy_conf[] = { + { MOD_REMOTE, YP_TREF, YP_VREF = { C_RMT }, YP_FNONE, + { knotd_conf_check_ref } }, + { MOD_TIMEOUT, YP_TINT, YP_VINT = { 0, INT32_MAX, 500 } }, + { MOD_FALLBACK, YP_TBOOL, YP_VBOOL = { true } }, + { MOD_CATCH_NXDOMAIN, YP_TBOOL, YP_VNONE }, + { NULL } +}; + +int dnsproxy_conf_check(knotd_conf_check_args_t *args) +{ + knotd_conf_t rmt = knotd_conf_check_item(args, MOD_REMOTE); + if (rmt.count == 0) { + args->err_str = "no remote server specified"; + return KNOT_EINVAL; + } + + return KNOT_EOK; +} + +typedef struct { + struct sockaddr_storage remote; + struct sockaddr_storage via; + bool fallback; + bool catch_nxdomain; + int timeout; +} dnsproxy_t; + +static knotd_state_t dnsproxy_fwd(knotd_state_t state, knot_pkt_t *pkt, + knotd_qdata_t *qdata, knotd_mod_t *mod) +{ + assert(pkt && qdata && mod); + + dnsproxy_t *proxy = knotd_mod_ctx(mod); + + /* Forward only queries ending with REFUSED (no zone) or NXDOMAIN (if configured) */ + if (proxy->fallback && !(qdata->rcode == KNOT_RCODE_REFUSED || + (qdata->rcode == KNOT_RCODE_NXDOMAIN && proxy->catch_nxdomain))) { + return state; + } + + /* Forward also original TSIG. */ + if (qdata->query->tsig_rr != NULL && !proxy->fallback) { + knot_tsig_append(qdata->query->wire, &qdata->query->size, + qdata->query->max_size, qdata->query->tsig_rr); + } + + /* Capture layer context. */ + const knot_layer_api_t *capture = query_capture_api(); + struct capture_param capture_param = { + .sink = pkt, + .orig_qname = qdata->extra->orig_qname + }; + + /* Create a forwarding request. */ + struct knot_requestor re; + int ret = knot_requestor_init(&re, capture, &capture_param, qdata->mm); + if (ret != KNOT_EOK) { + return state; /* Ignore, not enough memory. */ + } + + bool is_tcp = net_is_stream(qdata->params->socket); + const struct sockaddr *dst = (const struct sockaddr *)&proxy->remote; + const struct sockaddr *src = (const struct sockaddr *)&proxy->via; + struct knot_request *req = knot_request_make(re.mm, dst, src, qdata->query, NULL, + is_tcp ? 0 : KNOT_RQ_UDP); + if (req == NULL) { + knot_requestor_clear(&re); + return state; /* Ignore, not enough memory. */ + } + + /* Forward request. */ + ret = knot_requestor_exec(&re, req, proxy->timeout); + + knot_request_free(req, re.mm); + knot_requestor_clear(&re); + + /* Check result. */ + if (ret != KNOT_EOK) { + qdata->rcode = KNOT_RCODE_SERVFAIL; + return KNOTD_STATE_FAIL; /* Forwarding failed, SERVFAIL. */ + } else { + qdata->rcode = knot_pkt_ext_rcode(pkt); + } + + /* Respond also with TSIG. */ + if (pkt->tsig_rr != NULL && !proxy->fallback) { + knot_tsig_append(pkt->wire, &pkt->size, pkt->max_size, pkt->tsig_rr); + } + + return (proxy->fallback ? KNOTD_STATE_DONE : KNOTD_STATE_FINAL); +} + +int dnsproxy_load(knotd_mod_t *mod) +{ + dnsproxy_t *proxy = calloc(1, sizeof(*proxy)); + if (proxy == NULL) { + return KNOT_ENOMEM; + } + + knotd_conf_t remote = knotd_conf_mod(mod, MOD_REMOTE); + knotd_conf_t conf = knotd_conf(mod, C_RMT, C_ADDR, &remote); + if (conf.count > 0) { + proxy->remote = conf.multi[0].addr; + knotd_conf_free(&conf); + } + conf = knotd_conf(mod, C_RMT, C_VIA, &remote); + if (conf.count > 0) { + proxy->via = conf.multi[0].addr; + knotd_conf_free(&conf); + } + + conf = knotd_conf_mod(mod, MOD_TIMEOUT); + proxy->timeout = conf.single.integer; + + conf = knotd_conf_mod(mod, MOD_FALLBACK); + proxy->fallback = conf.single.boolean; + + conf = knotd_conf_mod(mod, MOD_CATCH_NXDOMAIN); + proxy->catch_nxdomain = conf.single.boolean; + + knotd_mod_ctx_set(mod, proxy); + + if (proxy->fallback) { + return knotd_mod_hook(mod, KNOTD_STAGE_END, dnsproxy_fwd); + } else { + return knotd_mod_hook(mod, KNOTD_STAGE_BEGIN, dnsproxy_fwd); + } +} + +void dnsproxy_unload(knotd_mod_t *mod) +{ + free(knotd_mod_ctx(mod)); +} + +KNOTD_MOD_API(dnsproxy, KNOTD_MOD_FLAG_SCOPE_ANY, + dnsproxy_load, dnsproxy_unload, dnsproxy_conf, dnsproxy_conf_check); diff --git a/src/knot/modules/dnsproxy/dnsproxy.rst b/src/knot/modules/dnsproxy/dnsproxy.rst new file mode 100644 index 0000000..92057de --- /dev/null +++ b/src/knot/modules/dnsproxy/dnsproxy.rst @@ -0,0 +1,102 @@ +.. _mod-dnsproxy: + +``dnsproxy`` – Tiny DNS proxy +============================= + +The module forwards all queries, or all specific zone queries if configured +per zone, to the indicated server for resolution. If configured in the fallback +mode, only localy unsatisfied queries are forwarded. I.e. a tiny DNS proxy. +There are several uses of this feature: + +* A substitute public-facing server in front of the real one +* Local zones (poor man's "views"), rest is forwarded to the public-facing server +* etc. + +.. NOTE:: + The module does not alter the query/response as the resolver would, + and the original transport protocol is kept as well. + +Example +------- + +The configuration is straightforward and just a single remote server is +required:: + + remote: + - id: hidden + address: 10.0.1.1 + + mod-dnsproxy: + - id: default + remote: hidden + fallback: on + + template: + - id: default + global-module: mod-dnsproxy/default + + zone: + - domain: local.zone + +When clients query for anything in the ``local.zone``, they will be +responded to locally. The rest of the requests will be forwarded to the +specified server (``10.0.1.1`` in this case). + +Module reference +---------------- + +:: + + mod-dnsproxy: + - id: STR + remote: remote_id + timeout: INT + fallback: BOOL + catch-nxdomain: BOOL + +.. _mod-dnsproxy_id: + +id +.. + +A module identifier. + +.. _mod-dnsproxy_remote: + +remote +...... + +A :ref:`reference<remote_id>` to a remote server where the queries are +forwarded to. + +*Required* + +.. _mod-dnsproxy_timeout: + +timeout +....... + +A remote response timeout in milliseconds. + +*Default:* 500 + +.. _mod-dnsproxy_fallback: + +fallback +........ + +If enabled, localy unsatisfied queries leading to REFUSED (no zone) are forwarded. +If disabled, all queries are directly forwarded without any local attempts +to resolve them. + +*Default:* on + +.. _mod-dnsproxy_catch-nxdomain: + +catch-nxdomain +.............. + +If enabled, localy unsatisfied queries leading to NXDOMAIN are forwarded. +This option is only relevant in the fallback mode. + +*Default:* off diff --git a/src/knot/modules/dnstap/Makefile.inc b/src/knot/modules/dnstap/Makefile.inc new file mode 100644 index 0000000..19f6043 --- /dev/null +++ b/src/knot/modules/dnstap/Makefile.inc @@ -0,0 +1,15 @@ +knot_modules_dnstap_la_SOURCES = knot/modules/dnstap/dnstap.c +EXTRA_DIST += knot/modules/dnstap/dnstap.rst + +if STATIC_MODULE_dnstap +libknotd_la_SOURCES += $(knot_modules_dnstap_la_SOURCES) +libknotd_la_CPPFLAGS += $(DNSTAP_CFLAGS) +libknotd_la_LIBADD += $(DNSTAP_LIBS) libdnstap.la +endif + +if SHARED_MODULE_dnstap +knot_modules_dnstap_la_LDFLAGS = $(KNOTD_MOD_LDFLAGS) +knot_modules_dnstap_la_CPPFLAGS = $(KNOTD_MOD_CPPFLAGS) $(DNSTAP_CFLAGS) +knot_modules_dnstap_la_LIBADD = $(DNSTAP_LIBS) libdnstap.la +pkglib_LTLIBRARIES += knot/modules/dnstap.la +endif diff --git a/src/knot/modules/dnstap/dnstap.c b/src/knot/modules/dnstap/dnstap.c new file mode 100644 index 0000000..976f43f --- /dev/null +++ b/src/knot/modules/dnstap/dnstap.c @@ -0,0 +1,320 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <netinet/in.h> + +#include "contrib/dnstap/dnstap.h" +#include "contrib/dnstap/dnstap.pb-c.h" +#include "contrib/dnstap/message.h" +#include "contrib/dnstap/writer.h" +#include "contrib/time.h" +#include "knot/include/module.h" + +#define MOD_SINK "\x04""sink" +#define MOD_IDENTITY "\x08""identity" +#define MOD_VERSION "\x07""version" +#define MOD_QUERIES "\x0B""log-queries" +#define MOD_RESPONSES "\x0D""log-responses" + +const yp_item_t dnstap_conf[] = { + { MOD_SINK, YP_TSTR, YP_VNONE }, + { MOD_IDENTITY, YP_TSTR, YP_VNONE }, + { MOD_VERSION, YP_TSTR, YP_VNONE }, + { MOD_QUERIES, YP_TBOOL, YP_VBOOL = { true } }, + { MOD_RESPONSES, YP_TBOOL, YP_VBOOL = { true } }, + { NULL } +}; + +int dnstap_conf_check(knotd_conf_check_args_t *args) +{ + knotd_conf_t sink = knotd_conf_check_item(args, MOD_SINK); + if (sink.count == 0 || sink.single.string[0] == '\0') { + args->err_str = "no sink specified"; + return KNOT_EINVAL; + } + + return KNOT_EOK; +} + +typedef struct { + struct fstrm_iothr *iothread; + char *identity; + size_t identity_len; + char *version; + size_t version_len; +} dnstap_ctx_t; + +static knotd_state_t log_message(knotd_state_t state, const knot_pkt_t *pkt, + knotd_qdata_t *qdata, knotd_mod_t *mod) +{ + assert(pkt && qdata && mod); + + /* Skip empty packet. */ + if (state == KNOTD_STATE_NOOP) { + return state; + } + + dnstap_ctx_t *ctx = knotd_mod_ctx(mod); + + struct fstrm_iothr_queue *ioq = + fstrm_iothr_get_input_queue_idx(ctx->iothread, qdata->params->thread_id); + + /* Unless we want to measure the time it takes to process each query, + * we can treat Q/R times the same. */ + struct timespec tv = { .tv_sec = time(NULL) }; + + /* Determine query / response. */ + Dnstap__Message__Type msgtype = DNSTAP__MESSAGE__TYPE__AUTH_QUERY; + if (knot_wire_get_qr(pkt->wire)) { + msgtype = DNSTAP__MESSAGE__TYPE__AUTH_RESPONSE; + } + + /* Determine whether we run on UDP/TCP. */ + int protocol = IPPROTO_TCP; + if (qdata->params->flags & KNOTD_QUERY_FLAG_LIMIT_SIZE) { + protocol = IPPROTO_UDP; + } + + /* Create a dnstap message. */ + Dnstap__Message msg; + int ret = dt_message_fill(&msg, msgtype, + (const struct sockaddr *)qdata->params->remote, + NULL, /* todo: fill me! */ + protocol, pkt->wire, pkt->size, &tv); + if (ret != KNOT_EOK) { + return state; + } + + Dnstap__Dnstap dnstap = DNSTAP__DNSTAP__INIT; + dnstap.type = DNSTAP__DNSTAP__TYPE__MESSAGE; + dnstap.message = &msg; + + /* Set message version and identity. */ + if (ctx->identity_len > 0) { + dnstap.identity.data = (uint8_t *)ctx->identity; + dnstap.identity.len = ctx->identity_len; + dnstap.has_identity = 1; + } + if (ctx->version_len > 0) { + dnstap.version.data = (uint8_t *)ctx->version; + dnstap.version.len = ctx->version_len; + dnstap.has_version = 1; + } + + /* Pack the message. */ + uint8_t *frame = NULL; + size_t size = 0; + dt_pack(&dnstap, &frame, &size); + if (frame == NULL) { + return state; + } + + /* Submit a request. */ + fstrm_res res = fstrm_iothr_submit(ctx->iothread, ioq, frame, size, + fstrm_free_wrapper, NULL); + if (res != fstrm_res_success) { + free(frame); + return state; + } + + return state; +} + +/*! \brief Submit message - query. */ +static knotd_state_t dnstap_message_log_query(knotd_state_t state, knot_pkt_t *pkt, + knotd_qdata_t *qdata, knotd_mod_t *mod) +{ + assert(qdata); + + return log_message(state, qdata->query, qdata, mod); +} + +/*! \brief Submit message - response. */ +static knotd_state_t dnstap_message_log_response(knotd_state_t state, knot_pkt_t *pkt, + knotd_qdata_t *qdata, knotd_mod_t *mod) +{ + return log_message(state, pkt, qdata, mod); +} + +/*! \brief Create a UNIX socket sink. */ +static struct fstrm_writer* dnstap_unix_writer(const char *path) +{ + struct fstrm_unix_writer_options *opt = NULL; + struct fstrm_writer_options *wopt = NULL; + struct fstrm_writer *writer = NULL; + + opt = fstrm_unix_writer_options_init(); + if (opt == NULL) { + goto finish; + } + fstrm_unix_writer_options_set_socket_path(opt, path); + + wopt = fstrm_writer_options_init(); + if (wopt == NULL) { + goto finish; + } + fstrm_writer_options_add_content_type(wopt, DNSTAP_CONTENT_TYPE, + strlen(DNSTAP_CONTENT_TYPE)); + writer = fstrm_unix_writer_init(opt, wopt); + +finish: + fstrm_unix_writer_options_destroy(&opt); + fstrm_writer_options_destroy(&wopt); + return writer; +} + +/*! \brief Create a basic file writer sink. */ +static struct fstrm_writer* dnstap_file_writer(const char *path) +{ + struct fstrm_file_options *fopt = NULL; + struct fstrm_writer_options *wopt = NULL; + struct fstrm_writer *writer = NULL; + + fopt = fstrm_file_options_init(); + if (fopt == NULL) { + goto finish; + } + fstrm_file_options_set_file_path(fopt, path); + + wopt = fstrm_writer_options_init(); + if (wopt == NULL) { + goto finish; + } + fstrm_writer_options_add_content_type(wopt, DNSTAP_CONTENT_TYPE, + strlen(DNSTAP_CONTENT_TYPE)); + writer = fstrm_file_writer_init(fopt, wopt); + +finish: + fstrm_file_options_destroy(&fopt); + fstrm_writer_options_destroy(&wopt); + return writer; +} + +/*! \brief Create a log sink according to the path string. */ +static struct fstrm_writer* dnstap_writer(const char *path) +{ + const char *prefix = "unix:"; + const size_t prefix_len = strlen(prefix); + + /* UNIX socket prefix. */ + if (strlen(path) > prefix_len && strncmp(path, prefix, prefix_len) == 0) { + return dnstap_unix_writer(path + prefix_len); + } + + return dnstap_file_writer(path); +} + +int dnstap_load(knotd_mod_t *mod) +{ + /* Create dnstap context. */ + dnstap_ctx_t *ctx = calloc(1, sizeof(*ctx)); + if (ctx == NULL) { + return KNOT_ENOMEM; + } + + /* Set identity. */ + knotd_conf_t conf = knotd_conf_mod(mod, MOD_IDENTITY); + if (conf.count == 1) { + ctx->identity = (conf.single.string != NULL) ? + strdup(conf.single.string) : NULL; + } else { + knotd_conf_t host = knotd_conf_env(mod, KNOTD_CONF_ENV_HOSTNAME); + ctx->identity = strdup(host.single.string); + } + ctx->identity_len = (ctx->identity != NULL) ? strlen(ctx->identity) : 0; + + /* Set version. */ + conf = knotd_conf_mod(mod, MOD_VERSION); + if (conf.count == 1) { + ctx->version = (conf.single.string != NULL) ? + strdup(conf.single.string) : NULL; + } else { + knotd_conf_t version = knotd_conf_env(mod, KNOTD_CONF_ENV_VERSION); + ctx->version = strdup(version.single.string); + } + ctx->version_len = (ctx->version != NULL) ? strlen(ctx->version) : 0; + + /* Set sink. */ + conf = knotd_conf_mod(mod, MOD_SINK); + const char *sink = conf.single.string; + + /* Set log_queries. */ + conf = knotd_conf_mod(mod, MOD_QUERIES); + const bool log_queries = conf.single.boolean; + + /* Set log_responses. */ + conf = knotd_conf_mod(mod, MOD_RESPONSES); + const bool log_responses = conf.single.boolean; + + /* Initialize the writer and the options. */ + struct fstrm_writer *writer = dnstap_writer(sink); + if (writer == NULL) { + goto fail; + } + + struct fstrm_iothr_options *opt = fstrm_iothr_options_init(); + if (opt == NULL) { + fstrm_writer_destroy(&writer); + goto fail; + } + + /* Initialize queues. */ + knotd_conf_t udp = knotd_conf_env(mod, KNOTD_CONF_ENV_WORKERS_UDP); + knotd_conf_t tcp = knotd_conf_env(mod, KNOTD_CONF_ENV_WORKERS_TCP); + size_t qcount = udp.single.integer + tcp.single.integer; + fstrm_iothr_options_set_num_input_queues(opt, qcount); + + /* Create the I/O thread. */ + ctx->iothread = fstrm_iothr_init(opt, &writer); + fstrm_iothr_options_destroy(&opt); + if (ctx->iothread == NULL) { + fstrm_writer_destroy(&writer); + goto fail; + } + + knotd_mod_ctx_set(mod, ctx); + + /* Hook to the query plan. */ + if (log_queries) { + knotd_mod_hook(mod, KNOTD_STAGE_BEGIN, dnstap_message_log_query); + } + if (log_responses) { + knotd_mod_hook(mod, KNOTD_STAGE_END, dnstap_message_log_response); + } + + return KNOT_EOK; +fail: + knotd_mod_log(mod, LOG_ERR, "failed to init sink '%s'", sink); + + free(ctx->identity); + free(ctx->version); + free(ctx); + + return KNOT_ENOMEM; +} + +void dnstap_unload(knotd_mod_t *mod) +{ + dnstap_ctx_t *ctx = knotd_mod_ctx(mod); + + fstrm_iothr_destroy(&ctx->iothread); + free(ctx->identity); + free(ctx->version); + free(ctx); +} + +KNOTD_MOD_API(dnstap, KNOTD_MOD_FLAG_SCOPE_ANY, + dnstap_load, dnstap_unload, dnstap_conf, dnstap_conf_check); diff --git a/src/knot/modules/dnstap/dnstap.rst b/src/knot/modules/dnstap/dnstap.rst new file mode 100644 index 0000000..d37f69a --- /dev/null +++ b/src/knot/modules/dnstap/dnstap.rst @@ -0,0 +1,106 @@ +.. _mod-dnstap: + +``dnstap`` – Dnstap traffic logging +=================================== + +A module for query and response logging based on the dnstap_ library. +You can capture either all or zone-specific queries and responses; usually +you want to do the former. + +Example +------- + +The configuration comprises only a :ref:`mod-dnstap_sink` path parameter, +which can be either a file or a UNIX socket:: + + mod-dnstap: + - id: capture_all + sink: /tmp/capture.tap + + template: + - id: default + global-module: mod-dnstap/capture_all + +.. NOTE:: + To be able to use a Unix socket you need an external program to create it. + Knot DNS connects to it as a client using the libfstrm library. It operates + exactly like syslog. See `here + <https://www.nlnetlabs.nl/bugs-script/show_bug.cgi?id=741#c10>`_ for + more details. + +.. NOTE:: + Dnstap log files can also be created or read using ``kdig``. + +.. _dnstap: http://dnstap.info/ + +Module reference +---------------- + +For all queries logging, use this module in the *default* template. For +zone-specific logging, use this module in the proper zone configuration. + +:: + + mod-dnstap: + - id: STR + sink: STR + identity: STR + version: STR + log-queries: BOOL + log-responses: BOOL + +.. _mod-dnstap_id: + +id +.. + +A module identifier. + +.. _mod-dnstap_sink: + +sink +.... + +A sink path, which can be either a file or a UNIX socket when prefixed with +``unix:``. + +*Required* + +.. WARNING:: + File is overwritten on server startup or reload. + +.. _mod-dnstap_identity: + +identity +........ + +A DNS server identity. Set empty value to disable. + +*Default:* FQDN hostname + +.. _mod-dnstap_version: + +version +....... + +A DNS server version. Set empty value to disable. + +*Default:* server version + +.. _mod-dnstap_log-queries: + +log-queries +........... + +If enabled, query messages will be logged. + +*Default:* on + +.. _mod-dnstap_log-responses: + +log-responses +............. + +If enabled, response messages will be logged. + +*Default:* on diff --git a/src/knot/modules/geoip/Makefile.inc b/src/knot/modules/geoip/Makefile.inc new file mode 100644 index 0000000..395cf9f --- /dev/null +++ b/src/knot/modules/geoip/Makefile.inc @@ -0,0 +1,16 @@ +knot_modules_geoip_la_SOURCES = knot/modules/geoip/geoip.c \ + knot/modules/geoip/geodb.c \ + knot/modules/geoip/geodb.h +EXTRA_DIST += knot/modules/geoip/geoip.rst + +if STATIC_MODULE_geoip +libknotd_la_SOURCES += $(knot_modules_geoip_la_SOURCES) +libknotd_la_CPPFLAGS += $(libmaxminddb_CFLAGS) +libknotd_la_LIBADD += $(libmaxminddb_LIBS) +endif + +if SHARED_MODULE_geoip +knot_modules_geoip_la_LDFLAGS = $(KNOTD_MOD_LDFLAGS) +knot_modules_geoip_la_CPPFLAGS = $(KNOTD_MOD_CPPFLAGS) +pkglib_LTLIBRARIES += knot/modules/geoip.la +endif diff --git a/src/knot/modules/geoip/geodb.c b/src/knot/modules/geoip/geodb.c new file mode 100644 index 0000000..899c4d1 --- /dev/null +++ b/src/knot/modules/geoip/geodb.c @@ -0,0 +1,222 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "knot/modules/geoip/geodb.h" +#include "contrib/strtonum.h" +#include "contrib/string.h" + +#if HAVE_MAXMINDDB +static const uint16_t type_map[] = { + [GEODB_KEY_ID] = MMDB_DATA_TYPE_UINT32, + [GEODB_KEY_TXT] = MMDB_DATA_TYPE_UTF8_STRING +}; +#endif + +int parse_geodb_path(geodb_path_t *path, const char *input) +{ + if (path == NULL || input == NULL) { + return -1; + } + + // Parse optional type of key. + path->type = GEODB_KEY_TXT; + const char *delim = input; + if (input[0] == '(') { + delim = strchr(input, ')'); + if (delim == NULL) { + return -1; + } + input++; + char *type = sprintf_alloc("%.*s", delim - input, input); + const knot_lookup_t *table = knot_lookup_by_name(geodb_key_types, type); + free(type); + if (table == NULL) { + return -1; + } + path->type = table->id; + input = delim + 1; + } + + // Parse the path. + uint16_t len = 0; + while (1) { + delim = strchr(input, '/'); + if (delim == NULL) { + delim = input + strlen(input); + } + path->path[len] = malloc(delim - input + 1); + if (path->path[len] == NULL) { + return -1; + } + memcpy(path->path[len], input, delim - input); + path->path[len][delim - input] = '\0'; + len++; + if (*delim == 0 || len == GEODB_MAX_PATH_LEN) { + break; + } + input = delim + 1; + } + + return 0; +} + +int parse_geodb_data(const char *input, void **geodata, uint32_t *geodata_len, + uint8_t *geodepth, geodb_path_t *path, uint16_t path_cnt) +{ + for (uint16_t i = 0; i < path_cnt; i++) { + const char *delim = strchr(input, ';'); + if (delim == NULL) { + delim = input + strlen(input); + } + uint16_t key_len = delim - input; + if (key_len > 0 && !(key_len == 1 && *input == '*')) { + *geodepth = i + 1; + switch (path[i].type) { + case GEODB_KEY_TXT: + geodata[i] = malloc(key_len + 1); + if (geodata[i] == NULL) { + return -1; + } + memcpy(geodata[i], input, key_len); + ((char *)geodata[i])[key_len] = '\0'; + geodata_len[i] = key_len; + break; + case GEODB_KEY_ID: + geodata[i] = malloc(sizeof(uint32_t)); + if (geodata[i] == NULL) { + return -1; + } + if (str_to_u32(input, (uint32_t *)geodata[i]) != KNOT_EOK) { + return -1; + } + geodata_len[i] = sizeof(uint32_t); + break; + default: + assert(0); + return -1; + } + } + if (*delim == '\0') { + break; + } + input = delim + 1; + } + + return 0; +} + +bool geodb_available(void) +{ +#if HAVE_MAXMINDDB + return true; +#else + return false; +#endif +} + +geodb_t *geodb_open(const char *filename) +{ +#if HAVE_MAXMINDDB + MMDB_s *db = calloc(1, sizeof(MMDB_s)); + if (db == NULL) { + return NULL; + } + int mmdb_error = MMDB_open(filename, MMDB_MODE_MMAP, db); + if (mmdb_error != MMDB_SUCCESS) { + free(db); + return NULL; + } + return db; +#else + return NULL; +#endif +} + +void geodb_close(geodb_t *geodb) +{ +#if HAVE_MAXMINDDB + MMDB_close(geodb); +#endif +} + +int geodb_query(geodb_t *geodb, geodb_data_t *entries, struct sockaddr *remote, + geodb_path_t *paths, uint16_t path_cnt, uint16_t *netmask) +{ +#if HAVE_MAXMINDDB + int mmdb_error = 0; + MMDB_lookup_result_s res; + res = MMDB_lookup_sockaddr(geodb, remote, &mmdb_error); + if (mmdb_error != MMDB_SUCCESS || !res.found_entry) { + return -1; + } + + // Save netmask. + *netmask = res.netmask; + + for (uint16_t i = 0; i < path_cnt; i++) { + // Get the value of the next key. + mmdb_error = MMDB_aget_value(&res.entry, &entries[i], (const char *const*)paths[i].path); + if (mmdb_error != MMDB_SUCCESS && mmdb_error != MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR) { + return -1; + } + if (mmdb_error == MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR || !entries[i].has_data) { + entries[i].has_data = false; + continue; + } + // Check the type. + if (entries[i].type != type_map[paths[i].type]) { + entries[i].has_data = false; + continue; + } + } + return 0; +#else + return -1; +#endif +} + +bool remote_in_geo(void **geodata, uint32_t *geodata_len, uint16_t geodepth, geodb_data_t *entries) +{ +#if HAVE_MAXMINDDB + for (int i = 0; i < geodepth; i++) { + // Nothing to do if current geodata do not specify this key. + if (geodata[i] == NULL) { + continue; + } + if (!entries[i].has_data) { + return false; + } + switch (entries[i].type) { + case MMDB_DATA_TYPE_UTF8_STRING: + if (geodata_len[i] != entries[i].data_size || + memcmp(geodata[i], entries[i].utf8_string, geodata_len[i]) != 0) { + return false; + } + break; + case MMDB_DATA_TYPE_UINT32: + if (*((uint32_t *)geodata[i]) != entries[i].uint32) { + return false; + } + break; + default: + return false; + } + } + return true; +#else + return false; +#endif +} diff --git a/src/knot/modules/geoip/geodb.h b/src/knot/modules/geoip/geodb.h new file mode 100644 index 0000000..6995883 --- /dev/null +++ b/src/knot/modules/geoip/geodb.h @@ -0,0 +1,65 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <libknot/libknot.h> +#if HAVE_MAXMINDDB +#include <maxminddb.h> +#endif + +#if HAVE_MAXMINDDB +#define geodb_t MMDB_s +#define geodb_data_t MMDB_entry_data_s +#else +#define geodb_t void +#define geodb_data_t char +#endif + +// MaxMind DB related constants. +#define GEODB_MAX_PATH_LEN 8 +#define GEODB_MAX_DEPTH 8 + +typedef enum { + GEODB_KEY_ID, + GEODB_KEY_TXT +} geodb_key_type_t; + +static const knot_lookup_t geodb_key_types[] = { + { GEODB_KEY_ID, "id" }, + { GEODB_KEY_TXT, "" } +}; + +typedef struct { + geodb_key_type_t type; + char *path[GEODB_MAX_PATH_LEN + 1]; // MMDB_aget_value() requires last member to be NULL. +} geodb_path_t; + +int parse_geodb_path(geodb_path_t *path, const char *input); + +int parse_geodb_data(const char *input, void **geodata, uint32_t *geodata_len, + uint8_t *geodepth, geodb_path_t *path, uint16_t path_cnt); + +bool geodb_available(void); + +geodb_t *geodb_open(const char *filename); + +void geodb_close(geodb_t *geodb); + +int geodb_query(geodb_t *geodb, geodb_data_t *entries, struct sockaddr *remote, + geodb_path_t *paths, uint16_t path_cnt, uint16_t *netmask); + +bool remote_in_geo(void **geodata, uint32_t *geodata_len, uint16_t geodepth, geodb_data_t *entries); diff --git a/src/knot/modules/geoip/geoip.c b/src/knot/modules/geoip/geoip.c new file mode 100644 index 0000000..01e73e7 --- /dev/null +++ b/src/knot/modules/geoip/geoip.c @@ -0,0 +1,779 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdio.h> +#include <string.h> +#include <arpa/inet.h> + +#include "knot/conf/schema.h" +#include "knot/include/module.h" +#include "knot/modules/geoip/geodb.h" +#include "libknot/libknot.h" +#include "contrib/qp-trie/trie.h" +#include "contrib/ucw/lists.h" +#include "contrib/sockaddr.h" +#include "contrib/string.h" +#include "contrib/strtonum.h" +#include "libzscanner/scanner.h" + +#define MOD_CONFIG_FILE "\x0B""config-file" +#define MOD_TTL "\x03""ttl" +#define MOD_MODE "\x04""mode" +#define MOD_GEODB_FILE "\x0A""geodb-file" +#define MOD_GEODB_KEY "\x09""geodb-key" + +enum operation_mode { + MODE_SUBNET, + MODE_GEODB +}; + +static const knot_lookup_t modes[] = { + { MODE_SUBNET, "subnet" }, + { MODE_GEODB, "geodb" }, + { 0, NULL } +}; + +static const char* mode_key[] = { + [MODE_SUBNET] = "net", + [MODE_GEODB] = "geo" +}; + +const yp_item_t geoip_conf[] = { + { MOD_CONFIG_FILE, YP_TSTR, YP_VNONE }, + { MOD_TTL, YP_TINT, YP_VINT = { 0, UINT32_MAX, 60, YP_STIME } }, + { MOD_MODE, YP_TOPT, YP_VOPT = { modes, MODE_SUBNET} }, + { MOD_GEODB_FILE, YP_TSTR, YP_VNONE }, + { MOD_GEODB_KEY, YP_TSTR, YP_VSTR = { "country/iso_code" }, YP_FMULTI }, + { NULL } +}; + +int geoip_conf_check(knotd_conf_check_args_t *args) +{ + knotd_conf_t conf = knotd_conf_check_item(args, MOD_CONFIG_FILE); + if (conf.count == 0) { + args->err_str = "no configuration file specified"; + return KNOT_EINVAL; + } + conf = knotd_conf_check_item(args, MOD_MODE); + if (conf.count == 1 && conf.single.option == MODE_GEODB) { + if (!geodb_available()) { + args->err_str = "geodb mode not available"; + return KNOT_EINVAL; + } + conf = knotd_conf_check_item(args, MOD_GEODB_FILE); + if (conf.count == 0) { + args->err_str = "no geodb file specified while in geodb mode"; + return KNOT_EINVAL; + } + } + return KNOT_EOK; +} + +typedef struct { + enum operation_mode mode; + uint32_t ttl; + trie_t *geo_trie; + bool dnssec; + + geodb_t *geodb; + geodb_path_t paths[GEODB_MAX_DEPTH]; + uint16_t path_count; +} geoip_ctx_t; + +typedef struct { + struct sockaddr_storage *subnet; + uint8_t subnet_prefix; + + void *geodata[GEODB_MAX_DEPTH]; + uint32_t geodata_len[GEODB_MAX_DEPTH]; + uint8_t geodepth; + + size_t count, avail; + knot_rrset_t *rrsets; + knot_rrset_t *rrsigs; +} geo_view_t; + +typedef struct { + size_t count, avail; + geo_view_t *views; +} geo_trie_val_t; + +static int add_view_to_trie(knot_dname_t *owner, geo_view_t *view, geoip_ctx_t *ctx) +{ + int ret = KNOT_EOK; + + // Find the node belonging to the owner. + knot_dname_storage_t lf_storage; + uint8_t *lf = knot_dname_lf(owner, lf_storage); + assert(lf); + trie_val_t *val = trie_get_ins(ctx->geo_trie, (char *)lf + 1, *lf); + geo_trie_val_t *cur_val = *val; + + if (cur_val == NULL) { + // Create new node value. + geo_trie_val_t *new_val = calloc(1, sizeof(geo_trie_val_t)); + new_val->avail = 1; + new_val->count = 1; + new_val->views = malloc(sizeof(geo_view_t)); + new_val->views[0] = *view; + + // Add new value to trie. + *val = new_val; + } else { + // Double the views array in size if necessary. + if (cur_val->avail == cur_val->count) { + void *alloc_ret = realloc(cur_val->views, + 2 * cur_val->avail * sizeof(geo_view_t)); + if (alloc_ret == NULL) { + return KNOT_ENOMEM; + } + cur_val->views = alloc_ret; + cur_val->avail *= 2; + } + + // Insert new element. + cur_val->views[cur_val->count++] = *view; + } + + return ret; +} + +static bool remote_in_subnet(const struct sockaddr_storage *remote, geo_view_t *view) +{ + if (view->subnet->ss_family != remote->ss_family) { + return false; + } + uint8_t *raw_addr = NULL; + uint8_t *raw_subnet = NULL; + switch(remote->ss_family) { + case AF_INET: + raw_addr = (uint8_t *)&((const struct sockaddr_in *)remote)->sin_addr; + raw_subnet = (uint8_t *)&((const struct sockaddr_in *)view->subnet)->sin_addr; + break; + case AF_INET6: + raw_addr = (uint8_t *)&((const struct sockaddr_in6 *)remote)->sin6_addr; + raw_subnet = (uint8_t *)&((const struct sockaddr_in6 *)view->subnet)->sin6_addr; + break; + default: + return false; + } + uint8_t nbytes = view->subnet_prefix / 8; + uint8_t nbits = view->subnet_prefix % 8; + for (int i = 0; i < nbytes; i++) { + if (raw_addr[i] != raw_subnet[i]) { + return false; + } + } + if (nbits != 0) { + uint8_t mask = ((1 << nbits) - 1) << (8 - nbits); + if ((raw_addr[nbytes] & mask) != raw_subnet[nbytes]) { + return false; + } + } + return true; +} + +static int finalize_geo_view(knotd_mod_t *mod, geo_view_t *view, knot_dname_t *owner, + geoip_ctx_t *ctx) +{ + if (view == NULL || view->count == 0) { + return KNOT_EOK; + } + + int ret = KNOT_EOK; + if (ctx->dnssec) { + view->rrsigs = malloc(sizeof(knot_rrset_t) * view->count); + if (view->rrsigs == NULL) { + return KNOT_ENOMEM; + } + for (size_t i = 0; i < view->count; i++) { + knot_dname_t *owner_cpy = knot_dname_copy(owner, NULL); + if (owner_cpy == NULL) { + return KNOT_ENOMEM; + } + knot_rrset_init(&view->rrsigs[i], owner_cpy, KNOT_RRTYPE_RRSIG, + KNOT_CLASS_IN, ctx->ttl); + ret = knotd_mod_dnssec_sign_rrset(mod, &view->rrsigs[i], + &view->rrsets[i], NULL); + if (ret != KNOT_EOK) { + return ret; + } + } + } + + ret = add_view_to_trie(owner, view, ctx); + if (ret != KNOT_EOK) { + return ret; + } + + memset(view, 0, sizeof(*view)); + return ret; +} + +static int init_geo_view(geo_view_t *view) +{ + if (view == NULL) { + return KNOT_EINVAL; + } + + view->count = 0; + view->avail = 1; + view->rrsigs = NULL; + view->rrsets = malloc(sizeof(knot_rrset_t)); + if (view->rrsets == NULL) { + return KNOT_ENOMEM; + } + return KNOT_EOK; +} + +static void clear_geo_view(geo_view_t *view) +{ + if (view == NULL) { + return; + } + for (int i = 0; i < GEODB_MAX_DEPTH; i++) { + free(view->geodata[i]); + } + free(view->subnet); + for (int j = 0; j < view->count; j++) { + knot_rrset_clear(&view->rrsets[j], NULL); + if (view->rrsigs != NULL) { + knot_rrset_clear(&view->rrsigs[j], NULL); + } + } + free(view->rrsets); + view->rrsets = NULL; + free(view->rrsigs); + view->rrsigs = NULL; +} + +static int parse_origin(yp_parser_t *yp, zs_scanner_t *scanner) +{ + char *set_origin = sprintf_alloc("$ORIGIN %s%s\n", yp->key, + (yp->key[yp->key_len - 1] == '.') ? "" : "."); + if (set_origin == NULL) { + return KNOT_ENOMEM; + } + + // Set owner as origin for future record parses. + if (zs_set_input_string(scanner, set_origin, strlen(set_origin)) != 0 || + zs_parse_record(scanner) != 0) { + free(set_origin); + return KNOT_EPARSEFAIL; + } + free(set_origin); + return KNOT_EOK; +} + +static int parse_view(knotd_mod_t *mod, geoip_ctx_t *ctx, yp_parser_t *yp, geo_view_t *view) +{ + // Initialize new geo view. + memset(view, 0, sizeof(*view)); + int ret = init_geo_view(view); + if (ret != KNOT_EOK) { + return ret; + } + + // Check view type syntax. + int key_len = strlen(mode_key[ctx->mode]); + if (yp->key_len != key_len || memcmp(yp->key, mode_key[ctx->mode], key_len) != 0) { + knotd_mod_log(mod, LOG_ERR, "invalid key type (%s) on line %zu", + yp->key, yp->line_count); + return KNOT_EINVAL; + } + + // Parse geodata/subnet. + if (ctx->mode == MODE_GEODB) { + if (parse_geodb_data((char *)yp->data, view->geodata, view->geodata_len, + &view->geodepth, ctx->paths, ctx->path_count) != 0) { + knotd_mod_log(mod, LOG_ERR, "invalid geo format (%s) on line %zu", + yp->data, yp->line_count); + return KNOT_EINVAL; + } + } else if (ctx->mode == MODE_SUBNET) { + // Locate the optional slash in the subnet string. + char *slash = strchr(yp->data, '/'); + if (slash == NULL) { + slash = yp->data + yp->data_len; + } + *slash = '\0'; + + // Parse address. + view->subnet = calloc(1, sizeof(struct sockaddr_storage)); + if (view->subnet == NULL) { + return KNOT_ENOMEM; + } + void *write_addr = &((struct sockaddr_in *)view->subnet)->sin_addr; + // Try to parse as IPv4 address. + ret = inet_pton(AF_INET, yp->data, write_addr); + if (ret == 1) { + view->subnet->ss_family = AF_INET; + view->subnet_prefix = 32; + } else { // Try to parse as IPv6 address. + write_addr = &((struct sockaddr_in6 *)view->subnet)->sin6_addr; + ret = inet_pton(AF_INET6, yp->data, write_addr); + if (ret != 1) { + knotd_mod_log(mod, LOG_ERR, "invalid address format (%s) on line %zu", + yp->data, yp->line_count); + return KNOT_EINVAL; + } + view->subnet->ss_family = AF_INET6; + view->subnet_prefix = 128; + } + + // Parse subnet prefix. + if (slash < yp->data + yp->data_len - 1) { + ret = str_to_u8(slash + 1, &view->subnet_prefix); + if (ret != KNOT_EOK) { + knotd_mod_log(mod, LOG_ERR, "invalid prefix (%s) on line %zu", + slash + 1, yp->line_count); + return ret; + } + if (view->subnet->ss_family == AF_INET && view->subnet_prefix > 32) { + view->subnet_prefix = 32; + knotd_mod_log(mod, LOG_WARNING, "IPv4 prefix too large on line %zu, set to 32", + yp->line_count); + } + if (view->subnet->ss_family == AF_INET6 && view->subnet_prefix > 128) { + view->subnet_prefix = 128; + knotd_mod_log(mod, LOG_WARNING, "IPv6 prefix too large on line %zu, set to 128", + yp->line_count); + } + } + } + + return KNOT_EOK; +} + +static int parse_rr(knotd_mod_t *mod, yp_parser_t *yp, zs_scanner_t *scanner, + knot_dname_t *owner, geo_view_t *view, uint32_t ttl) +{ + uint16_t rr_type = KNOT_RRTYPE_A; + if (knot_rrtype_from_string(yp->key, &rr_type) != 0) { + knotd_mod_log(mod, LOG_ERR, "invalid RR type (%s) on line %zu", + yp->key, yp->line_count); + return KNOT_EINVAL; + } + + if (knot_rrtype_is_dnssec(rr_type)) { + knotd_mod_log(mod, LOG_ERR, "DNSSEC record (%s) not allowed on line %zu", + yp->key, yp->line_count); + return KNOT_EINVAL; + } + + knot_rrset_t *add_rr = NULL; + for (size_t i = 0; i < view->count; i++) { + if (view->rrsets[i].type == rr_type) { + add_rr = &view->rrsets[i]; + break; + } + } + + if (add_rr == NULL) { + if (view->count == view->avail) { + void *alloc_ret = realloc(view->rrsets, + 2 * view->avail * sizeof(knot_rrset_t)); + if (alloc_ret == NULL) { + return KNOT_ENOMEM; + } + view->rrsets = alloc_ret; + view->avail *= 2; + } + add_rr = &view->rrsets[view->count++]; + knot_dname_t *owner_cpy = knot_dname_copy(owner, NULL); + if (owner_cpy == NULL) { + return KNOT_ENOMEM; + } + knot_rrset_init(add_rr, owner_cpy, rr_type, KNOT_CLASS_IN, ttl); + } + + // Parse record. + char *input_string = sprintf_alloc("@ %s %s\n", yp->key, yp->data); + if (input_string == NULL) { + return KNOT_ENOMEM; + } + + if (zs_set_input_string(scanner, input_string, strlen(input_string)) != 0 || + zs_parse_record(scanner) != 0 || + scanner->state != ZS_STATE_DATA) { + free(input_string); + return KNOT_EPARSEFAIL; + } + free(input_string); + + // Add new rdata to current rrset. + return knot_rrset_add_rdata(add_rr, scanner->r_data, scanner->r_data_length, NULL); +} + +static int geo_conf_yparse(knotd_mod_t *mod, geoip_ctx_t *ctx) +{ + int ret = KNOT_EOK; + yp_parser_t *yp = NULL; + zs_scanner_t *scanner = NULL; + knot_dname_t owner_buff[KNOT_DNAME_MAXLEN]; + knot_dname_t *owner = NULL; + geo_view_t *view = calloc(1, sizeof(geo_view_t)); + if (view == NULL) { + return KNOT_ENOMEM; + } + + // Initialize yparser. + yp = malloc(sizeof(yp_parser_t)); + if (yp == NULL) { + ret = KNOT_ENOMEM; + goto cleanup; + } + yp_init(yp); + knotd_conf_t conf = knotd_conf_mod(mod, MOD_CONFIG_FILE); + ret = yp_set_input_file(yp, conf.single.string); + if (ret != KNOT_EOK) { + knotd_mod_log(mod, LOG_ERR, "failed to load configuration file"); + goto cleanup; + } + + // Initialize zscanner. + scanner = malloc(sizeof(zs_scanner_t)); + if (scanner == NULL) { + ret = KNOT_ENOMEM; + goto cleanup; + } + if (zs_init(scanner, NULL, KNOT_CLASS_IN, ctx->ttl) != 0) { + ret = KNOT_EPARSEFAIL; + goto cleanup; + } + + // Main loop. + while (1) { + // Get the next item in config. + ret = yp_parse(yp); + if (ret == KNOT_EOF) { + ret = finalize_geo_view(mod, view, owner, ctx); + goto cleanup; + } + if (ret != KNOT_EOK) { + knotd_mod_log(mod, LOG_ERR, "failed to parse configuration file (%s)", + knot_strerror(ret)); + goto cleanup; + } + + // If the next item is not a rrset, the current view is finished. + if (yp->event != YP_EKEY1) { + ret = finalize_geo_view(mod, view, owner, ctx); + if (ret != KNOT_EOK) { + goto cleanup; + } + } + + // Next domain. + if (yp->event == YP_EKEY0) { + owner = knot_dname_from_str(owner_buff, yp->key, sizeof(owner_buff)); + if (owner == NULL) { + knotd_mod_log(mod, LOG_ERR, "invalid domain name in config on line %zu", + yp->line_count); + ret = KNOT_EINVAL; + goto cleanup; + } + ret = parse_origin(yp, scanner); + if (ret != KNOT_EOK) { + goto cleanup; + } + } + + // Next view. + if (yp->event == YP_EID) { + ret = parse_view(mod, ctx, yp, view); + if (ret != KNOT_EOK) { + goto cleanup; + } + } + + // Next RR of the current view. + if (yp->event == YP_EKEY1) { + // Check whether we really are in a view. + if (view->avail <= 0) { + knotd_mod_log(mod, LOG_ERR, "missing '%s' in config before line %zu", + (ctx->mode == MODE_SUBNET) ? "- net: SUBNET" : "- geo: LOCATION", + yp->line_count); + ret = KNOT_EINVAL; + goto cleanup; + } + ret = parse_rr(mod, yp, scanner, owner, view, ctx->ttl); + if (ret != KNOT_EOK) { + goto cleanup; + } + } + } + +cleanup: + if (ret != KNOT_EOK) { + clear_geo_view(view); + } + free(view); + zs_deinit(scanner); + free(scanner); + yp_deinit(yp); + free(yp); + return ret; +} + +static void clear_geo_trie(trie_t *trie) +{ + trie_it_t *it = trie_it_begin(trie); + while (!trie_it_finished(it)) { + geo_trie_val_t *val = (geo_trie_val_t *) (*trie_it_val(it)); + for (int i = 0; i < val->count; i++) { + clear_geo_view(&val->views[i]); + } + free(val->views); + free(val); + trie_it_next(it); + } + trie_it_free(it); + trie_clear(trie); +} + +static void free_geoip_ctx(geoip_ctx_t *ctx) +{ + geodb_close(ctx->geodb); + free(ctx->geodb); + clear_geo_trie(ctx->geo_trie); + trie_free(ctx->geo_trie); + for (int i = 0; i < ctx->path_count; i++) { + for (int j = 0; j < GEODB_MAX_PATH_LEN; j++) { + free(ctx->paths[i].path[j]); + } + } + free(ctx); +} + +static knotd_in_state_t geoip_process(knotd_in_state_t state, knot_pkt_t *pkt, + knotd_qdata_t *qdata, knotd_mod_t *mod) +{ + assert(pkt && qdata && mod); + + // Nothing to do if the query was already resolved by a previous module. + if (state == KNOTD_IN_STATE_HIT) { + return state; + } + + geoip_ctx_t *ctx = (geoip_ctx_t *)knotd_mod_ctx(mod); + + // Save the query type. + uint16_t qtype = knot_pkt_qtype(qdata->query); + + // Check if geolocation is available for given query. + knot_dname_storage_t lf_storage; + uint8_t *lf = knot_dname_lf(knot_pkt_qname(qdata->query), lf_storage); + // Exit if no qname. + if (lf == NULL) { + return state; + } + trie_val_t *val = trie_get_try(ctx->geo_trie, (char *)lf + 1, *lf); + if (val == NULL) { + // Nothing to do in this module. + return state; + } + + geo_trie_val_t *data = *val; + + // Check if EDNS Client Subnet is available. + struct sockaddr_storage ecs_addr = { 0 }; + const struct sockaddr_storage *remote = qdata->params->remote; + if (knot_edns_client_subnet_get_addr(&ecs_addr, qdata->ecs) == KNOT_EOK) { + remote = &ecs_addr; + } + + knot_rrset_t *rr = NULL; + knot_rrset_t *rrsig = NULL; + knot_rrset_t *cname = NULL; + knot_rrset_t *cnamesig = NULL; + uint16_t netmask = 0; + if (ctx->mode == MODE_SUBNET) { + // Find the best subnet containing the remote and queried rrset. + uint8_t best_prefix = 0; + for (int i = 0; i < data->count; i++) { + geo_view_t *view = &data->views[i]; + if ((rr == NULL || view->subnet_prefix > best_prefix) && remote_in_subnet(remote, view)) { + for (int j = 0; j < view->count; j++) { + if (view->rrsets[j].type == qtype) { + best_prefix = view->subnet_prefix; + rr = &view->rrsets[j]; + rrsig = (view->rrsigs) ? &view->rrsigs[j] : NULL; + break; + } else if (view->rrsets[j].type == KNOT_RRTYPE_CNAME) { + best_prefix = view->subnet_prefix; + cname = &view->rrsets[j]; + cnamesig = (view->rrsigs) ? &view->rrsigs[j] : NULL; + } + } + } + } + netmask = best_prefix; + } else if (ctx->mode == MODE_GEODB) { + geodb_data_t entries[ctx->path_count]; + + int ret = geodb_query(ctx->geodb, entries, (struct sockaddr *)remote, + ctx->paths, ctx->path_count, &netmask); + // MMDB may supply IPv6 prefixes even for IPv4 address, see man libmaxminddb. + if (remote->ss_family == AF_INET && netmask > 32) { + netmask -= 96; + } + + if (ret != 0) { + return state; + } + + uint8_t best_depth = 0; + // Find the best geo view containing the remote and queried rrset. + for (int i = 0; i < data->count; i++) { + geo_view_t *view = &data->views[i]; + if ((rr == NULL || view->geodepth > best_depth) && + remote_in_geo(view->geodata, view->geodata_len, view->geodepth, entries)) { + for (int j = 0; j < view->count; j++) { + if (view->rrsets[j].type == qtype) { + best_depth = view->geodepth; + rr = &view->rrsets[j]; + rrsig = (view->rrsigs) ? &view->rrsigs[j] : NULL; + break; + } else if (view->rrsets[j].type == KNOT_RRTYPE_CNAME) { + best_depth = view->geodepth; + cname = &view->rrsets[j]; + cnamesig = (view->rrsigs) ? &view->rrsigs[j] : NULL; + } + } + } + } + } + + // Return CNAME if only CNAME is found. + if (rr == NULL && cname != NULL) { + rr = cname; + rrsig = cnamesig; + } + + // Answer the query if possible. + if (rr != NULL) { + // Update ECS if used. + if (qdata->ecs != NULL && netmask > 0) { + qdata->ecs->scope_len = netmask; + } + + knot_pkt_put(pkt, KNOT_COMPR_HINT_QNAME, rr, 0); + if (ctx->dnssec && knot_pkt_has_dnssec(qdata->query) && rrsig != NULL) { + knot_pkt_put(pkt, KNOT_COMPR_HINT_QNAME, rrsig, 0); + } + + // We've got an answer, set the AA bit. + knot_wire_set_aa(pkt->wire); + + return KNOTD_IN_STATE_HIT; + } + + return state; +} + +int geoip_load(knotd_mod_t *mod) +{ + // Create module context. + geoip_ctx_t *ctx = calloc(1, sizeof(geoip_ctx_t)); + if (ctx == NULL) { + return KNOT_ENOMEM; + } + + knotd_conf_t conf = knotd_conf_mod(mod, MOD_TTL); + ctx->ttl = conf.single.integer; + conf = knotd_conf_mod(mod, MOD_MODE); + ctx->mode = conf.single.option; + + // Initialize the dname trie. + ctx->geo_trie = trie_create(NULL); + if (ctx->geo_trie == NULL) { + free_geoip_ctx(ctx); + return KNOT_ENOMEM; + } + + if (ctx->mode == MODE_GEODB) { + // Initialize geodb. + conf = knotd_conf_mod(mod, MOD_GEODB_FILE); + ctx->geodb = geodb_open(conf.single.string); + if (ctx->geodb == NULL) { + knotd_mod_log(mod, LOG_ERR, "failed to open geo DB"); + free_geoip_ctx(ctx); + return KNOT_EINVAL; + } + + // Load configured geodb keys. + conf = knotd_conf_mod(mod, MOD_GEODB_KEY); + ctx->path_count = conf.count; + if (ctx->path_count > GEODB_MAX_DEPTH) { + knotd_mod_log(mod, LOG_ERR, "maximal number of geodb-key items (%d) exceeded", + GEODB_MAX_DEPTH); + knotd_conf_free(&conf); + free_geoip_ctx(ctx); + return KNOT_EINVAL; + } + for (size_t i = 0; i < conf.count; i++) { + if (parse_geodb_path(&ctx->paths[i], (char *)conf.multi[i].string) != 0) { + knotd_mod_log(mod, LOG_ERR, "unrecognized geodb-key format"); + knotd_conf_free(&conf); + free_geoip_ctx(ctx); + return KNOT_EINVAL; + } + } + knotd_conf_free(&conf); + } + + // Is DNSSEC used on this zone? + conf = knotd_conf_zone(mod, C_DNSSEC_SIGNING, knotd_mod_zone(mod)); + ctx->dnssec = conf.single.boolean; + if (ctx->dnssec) { + int ret = knotd_mod_dnssec_init(mod); + if (ret != KNOT_EOK) { + knotd_mod_log(mod, LOG_ERR, "failed to initialize DNSSEC"); + free_geoip_ctx(ctx); + return ret; + } + ret = knotd_mod_dnssec_load_keyset(mod, false); + if (ret != KNOT_EOK) { + knotd_mod_log(mod, LOG_ERR, "failed to load DNSSEC keys"); + free_geoip_ctx(ctx); + return ret; + } + } + + // Parse geo configuration file. + int ret = geo_conf_yparse(mod, ctx); + if (ret != KNOT_EOK) { + knotd_mod_log(mod, LOG_ERR, "failed to load geo configuration"); + free_geoip_ctx(ctx); + return ret; + } + + knotd_mod_ctx_set(mod, ctx); + + return knotd_mod_in_hook(mod, KNOTD_STAGE_PREANSWER, geoip_process); +} + +void geoip_unload(knotd_mod_t *mod) +{ + geoip_ctx_t *ctx = knotd_mod_ctx(mod); + if (ctx != NULL) { + free_geoip_ctx(ctx); + } +} + +KNOTD_MOD_API(geoip, KNOTD_MOD_FLAG_SCOPE_ZONE, + geoip_load, geoip_unload, geoip_conf, geoip_conf_check); diff --git a/src/knot/modules/geoip/geoip.rst b/src/knot/modules/geoip/geoip.rst new file mode 100644 index 0000000..745a6ed --- /dev/null +++ b/src/knot/modules/geoip/geoip.rst @@ -0,0 +1,211 @@ +.. _mod-geoip: + +``geoip`` — Geography-based responses +===================================== + +This module offers response tailoring based on client's +subnet or geographic location. It supports GeoIP databases +in the MaxMind DB format, such as `GeoIP2 <https://dev.maxmind.com/geoip/geoip2/downloadable/>`_ +or the free version `GeoLite2 <https://dev.maxmind.com/geoip/geoip2/geolite2/>`_. + +The module can be enabled only per zone. + +.. NOTE:: + If :ref:`EDNS Client Subnet<server_edns-client-subnet>` support is enabled + and if a query contains this option, the module takes advantage of this + information to provide a more accurate response. + +DNSSEC support +-------------- + +There are two ways to enable DNSSEC signing of tailored responses. +If automatic DNSSEC signing is enabled, record signatures are precomputed when the module is loaded. +This has a speed benefit, however note that every RRset configured in the module should +have a **default** RRset of the same type contained in the zone, so that the NSEC(3) +chain can be built correctly. Also, it is STRONGLY RECOMMENDED to use +:ref:`manual key management <dnssec-manual-key-management>` in this setting, +as the corresponding zone has to be reloaded when the signing key changes and to +have better control over key synchronization to all instances of the server. + +.. NOTE:: + If the GeoIP module is used with automatic DNSSEC signing, the keys for computing record signatures + MUST exist or be generated before the server is launched, otherwise the module fails to + compute the signatures and does not load. + +Alternatively, the :ref:`geoip<mod-geoip>` module may be combined with the +:ref:`onlinesign<mod-onlinesign>` module and the tailored responses can be signed +on the fly. This approach is much more computationally demanding for the server. + +.. NOTE:: + If the GeoIP module is used with online signing, it is recommended to set the :ref:`nsec-bitmap<mod-onlinesign_nsec-bitmap>` + option of the onlinesign module to contain all Resource Record types potentially generated by the module. + +Example +------- +* An example configuration.:: + + mod-geoip: + - id: default + config-file: /path/to/geo.conf + ttl: 20 + mode: geodb + geodb-file: /path/to/GeoLite2-City.mmdb + geodb-key: [ country/iso_code, city/names/en ] + + zone: + - domain: example.com. + module: mod-geoip/default + + +Configuration file +------------------ + +Every instance of the module requires an additional :ref:`mod-geoip_config-file` +in which the desired responses to queries from various locations are configured. +This file has the following simple format: + +:: + + domain-name1: + - geo|net: location1 + RR-Type1: RDATA + RR-Type2: RDATA + ... + - geo|net: location2 + RR-Type1: RDATA + ... + domain-name2: + ... + + +Example +------- + +* Example :ref:`mod-geoip_config-file` for subnets + +:: + + foo.example.com: + - net: 10.0.0.0/24 + A: [ 192.168.1.1, 192.168.1.2 ] + AAAA: [ 2001:DB8::1, 2001:DB8::2 ] + TXT: "subnet\ 10.0.0.0/24" + ... + bar.example.com: + - net: 2001:DB8::/32 + A: 192.168.1.3 + AAAA: 2001:DB8::3 + TXT: "subnet\ 2001:DB8::/32" + ... + +.. NOTE:: + If a space or a quotation mark is a part of record data, such a character + must be prefixed with a backslash. The following notations are equivalent:: + + Multi-word\ string + "Multi-word\ string" + "\"Multi-word string\"" + +* Example :ref:`mod-geoip_config-file` for geographic locations + +:: + + foo.example.com: + - geo: "CZ;Prague" + CNAME: cz.foo.example.com + - geo: "US;Las Vegas" + CNAME: vegas.foo.example.net + - geo: "US;*" + CNAME: us.foo.example.net + ... + + +Module reference +---------------- + +:: + + mod-geoip: + - id: STR + config-file: STR + ttl: TIME + mode: geodb | subnet + geodb-file: STR + geodb-key: STR ... + +.. _mod-geoip_id: + +id +.. + +A module identifier. + +.. _mod-geoip_config-file: + +config-file +........... + +Full path to the response configuration file as described above. + +*Required* + +.. _mod-geoip_ttl: + +ttl +... + +The time to live of Resource Records returned by the module. + +*Default:* 60 + +.. _mod-geoip_mode: + +mode +.... + +The mode of operation of the module. + +Possible values: + +- ``subnet`` – Responses are tailored according to subnets. +- ``geodb`` – Responses are tailored according to geographic data retrieved + from the configured database. + +*Default:* subnet + +.. _mod-geoip_geodb-file: + +geodb-file +.......... + +Full path to a .mmdb file containing the GeoIP database. + +*Reqired if* :ref:`mod-geoip_mode` *is set to* **geodb** + +.. _mod-geoip_geodb-key: + +geodb-key +......... + +Multi-valued item, can be specified up to **8** times. Each **geodb-key** specifies +a path to a key in a node in the supplied GeoIP database. The module currently supports +two types of values: **string** or **32-bit unsigned int**. In the latter +case, the key has to be prefixed with **(id)**. Common choices of keys include: + +* **continent/code** + +* **country/iso_code** + +* **(id)country/geoname_id** + +* **city/names/en** + +* **(id)city/geoname_id** + +* **isp** + +* ... + +In the zone's config file for the module the values of the keys are entered in the same order +as the keys in the module's configuration, separated by a semicolon. Enter the value **"*"** +if the key is allowed to have any value. diff --git a/src/knot/modules/noudp/Makefile.inc b/src/knot/modules/noudp/Makefile.inc new file mode 100644 index 0000000..cf26a35 --- /dev/null +++ b/src/knot/modules/noudp/Makefile.inc @@ -0,0 +1,12 @@ +knot_modules_noudp_la_SOURCES = knot/modules/noudp/noudp.c +EXTRA_DIST += knot/modules/noudp/noudp.rst + +if STATIC_MODULE_noudp +libknotd_la_SOURCES += $(knot_modules_noudp_la_SOURCES) +endif + +if SHARED_MODULE_noudp +knot_modules_noudp_la_LDFLAGS = $(KNOTD_MOD_LDFLAGS) +knot_modules_noudp_la_CPPFLAGS = $(KNOTD_MOD_CPPFLAGS) +pkglib_LTLIBRARIES += knot/modules/noudp.la +endif diff --git a/src/knot/modules/noudp/noudp.c b/src/knot/modules/noudp/noudp.c new file mode 100644 index 0000000..c2f064a --- /dev/null +++ b/src/knot/modules/noudp/noudp.c @@ -0,0 +1,43 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "knot/include/module.h" + +static bool is_udp(knotd_qdata_t *qdata) +{ + return qdata->params->flags & KNOTD_QUERY_FLAG_LIMIT_SIZE; +} + +static knotd_state_t noudp_begin(knotd_state_t state, knot_pkt_t *pkt, + knotd_qdata_t *qdata, knotd_mod_t *mod) +{ + if (is_udp(qdata)) { + knot_wire_set_tc(pkt->wire); + return KNOTD_STATE_DONE; + } + + return state; +} + +int noudp_load(knotd_mod_t *mod) +{ + knotd_mod_hook(mod, KNOTD_STAGE_BEGIN, noudp_begin); + + return KNOT_EOK; +} + +KNOTD_MOD_API(noudp, KNOTD_MOD_FLAG_SCOPE_ANY | KNOTD_MOD_FLAG_OPT_CONF, + noudp_load, NULL, NULL, NULL); diff --git a/src/knot/modules/noudp/noudp.rst b/src/knot/modules/noudp/noudp.rst new file mode 100644 index 0000000..03f790d --- /dev/null +++ b/src/knot/modules/noudp/noudp.rst @@ -0,0 +1,20 @@ +.. _mod-noudp: + +``noudp`` — No UDP response +=========================== + +The module sends empty truncated response to any UDP query. TCP queries are +not affected. + +Example +------- + +To enable this module globally, you need to add something like the following +to the configuration file:: + + template: + - id: default + global-module: mod-noudp + +.. NOTE:: + This module is not configurable. diff --git a/src/knot/modules/onlinesign/Makefile.inc b/src/knot/modules/onlinesign/Makefile.inc new file mode 100644 index 0000000..bfccf3e --- /dev/null +++ b/src/knot/modules/onlinesign/Makefile.inc @@ -0,0 +1,15 @@ +knot_modules_onlinesign_la_SOURCES = knot/modules/onlinesign/onlinesign.c \ + knot/modules/onlinesign/nsec_next.c \ + knot/modules/onlinesign/nsec_next.h +EXTRA_DIST += knot/modules/onlinesign/onlinesign.rst + +if STATIC_MODULE_onlinesign +libknotd_la_SOURCES += $(knot_modules_onlinesign_la_SOURCES) +endif + +if SHARED_MODULE_onlinesign +knot_modules_onlinesign_la_LDFLAGS = $(KNOTD_MOD_LDFLAGS) +knot_modules_onlinesign_la_CPPFLAGS = $(KNOTD_MOD_CPPFLAGS) +knot_modules_onlinesign_la_LIBADD = libcontrib.la +pkglib_LTLIBRARIES += knot/modules/onlinesign.la +endif diff --git a/src/knot/modules/onlinesign/nsec_next.c b/src/knot/modules/onlinesign/nsec_next.c new file mode 100644 index 0000000..1314154 --- /dev/null +++ b/src/knot/modules/onlinesign/nsec_next.c @@ -0,0 +1,113 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <assert.h> +#include <stdbool.h> +#include <stdlib.h> + +#include "knot/modules/onlinesign/nsec_next.h" +#include "libknot/libknot.h" + +static bool inc_label(const uint8_t *buffer, uint8_t **label_ptr) +{ + assert(buffer); + assert(label_ptr && *label_ptr); + assert(buffer <= *label_ptr && *label_ptr < buffer + KNOT_DNAME_MAXLEN); + + const uint8_t *label = *label_ptr; + const uint8_t len = *label; + const uint8_t *first = *label_ptr + 1; + const uint8_t *last = *label_ptr + len; + + assert(len <= KNOT_DNAME_MAXLABELLEN); + + // jump over trailing 0xff chars + uint8_t *scan = (uint8_t *)last; + while (scan >= first && *scan == 0xff) { + scan -= 1; + } + + // increase in place + if (scan >= first) { + if (*scan == 'A' - 1) { + *scan = 'Z' + 1; + } else { + *scan += 1; + } + memset(scan + 1, 0x00, last - scan); + return true; + } + + // check name and label boundaries + if (scan - 1 < buffer || len == KNOT_DNAME_MAXLABELLEN) { + return false; + } + + // append a zero byte at the end of the label + scan -= 1; + scan[0] = len + 1; + memmove(scan + 1, first, len); + scan[len + 1] = 0x00; + + *label_ptr = scan; + + return true; +} + +static void strip_label(uint8_t **name_ptr) +{ + assert(name_ptr && *name_ptr); + + uint8_t len = **name_ptr; + *name_ptr += 1 + len; +} + +knot_dname_t *online_nsec_next(const knot_dname_t *dname, const knot_dname_t *apex) +{ + assert(dname); + assert(apex); + + // right aligned copy of the domain name + uint8_t copy[KNOT_DNAME_MAXLEN] = { 0 }; + size_t dname_len = knot_dname_size(dname); + size_t empty_len = sizeof(copy) - dname_len; + uint8_t *pos = copy + empty_len; + memmove(pos, dname, dname_len); + + // add new zero-byte label + if (empty_len >= 2) { + pos -= 2; + pos[0] = 0x01; + pos[1] = 0x00; + return knot_dname_copy(pos, NULL); + } + + // find apex position in the buffer + size_t apex_len = knot_dname_size(apex); + const uint8_t *apex_pos = copy + sizeof(copy) - apex_len; + assert(knot_dname_is_equal(apex, apex_pos)); + + // find first label which can be incremented + while (pos != apex_pos) { + if (inc_label(copy, &pos)) { + return knot_dname_copy(pos, NULL); + } + strip_label(&pos); + } + + // apex completes the chain + return knot_dname_copy(pos, NULL); +} diff --git a/src/knot/modules/onlinesign/nsec_next.h b/src/knot/modules/onlinesign/nsec_next.h new file mode 100644 index 0000000..4d9a6c4 --- /dev/null +++ b/src/knot/modules/onlinesign/nsec_next.h @@ -0,0 +1,29 @@ +/* Copyright (C) 2015 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include "libknot/dname.h" + +/*! + * \brief Get the very next possible name in NSEC chain. + * + * \param dname Current dname in the NSEC chain. + * \param apex Zone apex name, used when we reach the end of the chain. + * + * \return Successor of dname in the NSEC chain. + */ +knot_dname_t *online_nsec_next(const knot_dname_t *dname, const knot_dname_t *apex); diff --git a/src/knot/modules/onlinesign/onlinesign.c b/src/knot/modules/onlinesign/onlinesign.c new file mode 100644 index 0000000..8dc9d50 --- /dev/null +++ b/src/knot/modules/onlinesign/onlinesign.c @@ -0,0 +1,776 @@ +/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <assert.h> +#include <stddef.h> +#include <string.h> + +#include "contrib/string.h" +#include "libdnssec/error.h" +#include "knot/include/module.h" +#include "knot/modules/onlinesign/nsec_next.h" +// Next dependencies force static module! +#include "knot/dnssec/ds_query.h" +#include "knot/dnssec/key-events.h" +#include "knot/dnssec/policy.h" +#include "knot/dnssec/rrset-sign.h" +#include "knot/dnssec/zone-events.h" +#include "knot/nameserver/query_module.h" +#include "knot/nameserver/process_query.h" + +#define MOD_POLICY "\x06""policy" +#define MOD_NSEC_BITMAP "\x0B""nsec-bitmap" + +int policy_check(knotd_conf_check_args_t *args) +{ + int ret = knotd_conf_check_ref(args); + if (ret != KNOT_EOK && strcmp((const char *)args->data, "default") == 0) { + return KNOT_EOK; + } + + return ret; +} + +int bitmap_check(knotd_conf_check_args_t *args) +{ + uint16_t num; + int ret = knot_rrtype_from_string((const char *)args->data, &num); + if (ret != 0) { + args->err_str = "invalid RR type"; + return KNOT_EINVAL; + } + + return KNOT_EOK; +} + +const yp_item_t online_sign_conf[] = { + { MOD_POLICY, YP_TREF, YP_VREF = { C_POLICY }, YP_FNONE, { policy_check } }, + { MOD_NSEC_BITMAP, YP_TSTR, YP_VNONE, YP_FMULTI, { bitmap_check } }, + { NULL } +}; + +/*! + * We cannot determine the true NSEC bitmap because of dynamic modules which + * can synthesize some types on-the-fly. The base NSEC map will be determined + * from zone content and this list of types. + * + * The types in the NSEC bitmap really don't have to exist. Only the QTYPE + * must not be present. This will make the validation work with resolvers + * performing negative caching. + */ + +static const uint16_t NSEC_FORCE_TYPES[] = { + KNOT_RRTYPE_A, + KNOT_RRTYPE_AAAA, + 0 +}; + +typedef struct { + knot_time_t event_rollover; + knot_time_t event_parent_ds_q; + pthread_mutex_t event_mutex; + pthread_rwlock_t signing_mutex; + + uint16_t *nsec_force_types; + + bool zone_doomed; +} online_sign_ctx_t; + +static bool want_dnssec(knotd_qdata_t *qdata) +{ + return knot_pkt_has_dnssec(qdata->query); +} + +static uint32_t dnskey_ttl(knotd_qdata_t *qdata) +{ + knot_rrset_t soa = knotd_qdata_zone_apex_rrset(qdata, KNOT_RRTYPE_SOA); + return soa.ttl; +} + +static uint32_t nsec_ttl(knotd_qdata_t *qdata) +{ + knot_rrset_t soa = knotd_qdata_zone_apex_rrset(qdata, KNOT_RRTYPE_SOA); + return knot_soa_minimum(soa.rrs.rdata); +} + +/*! + * \brief Add bitmap records synthesized by online-signing. + */ +static void bitmap_add_synth(dnssec_nsec_bitmap_t *map, bool is_apex) +{ + dnssec_nsec_bitmap_add(map, KNOT_RRTYPE_NSEC); + dnssec_nsec_bitmap_add(map, KNOT_RRTYPE_RRSIG); + if (is_apex) { + dnssec_nsec_bitmap_add(map, KNOT_RRTYPE_DNSKEY); + //dnssec_nsec_bitmap_add(map, KNOT_RRTYPE_CDS); + } +} + +/*! + * \brief Add bitmap records present in the zone. + */ +static void bitmap_add_zone(dnssec_nsec_bitmap_t *map, const zone_node_t *node) +{ + if (!node) { + return; + } + + for (int i = 0; i < node->rrset_count; i++) { + dnssec_nsec_bitmap_add(map, node->rrs[i].type); + } +} + +/*! + * \brief Add bitmap records which can be synthesized by other modules. + * + * \param qtype Current QTYPE, will never be added into the map. + */ +static void bitmap_add_forced(dnssec_nsec_bitmap_t *map, uint16_t qtype, + const uint16_t *force_types) +{ + for (int i = 0; force_types[i] > 0; i++) { + if (force_types[i] != qtype) { + dnssec_nsec_bitmap_add(map, force_types[i]); + } + } +} + +/*! + * \brief Add bitmap records from the actual response. + */ +static void bitmap_add_section(dnssec_nsec_bitmap_t *map, const knot_pktsection_t *answer) +{ + for (int i = 0; i < answer->count; i++) { + const knot_rrset_t *rr = knot_pkt_rr(answer, i); + dnssec_nsec_bitmap_add(map, rr->type); + } +} + +/*! + * \brief Synthesize NSEC type bitmap. + * + * - The bitmap will contain types synthesized by this module. + * - For ANY query, the bitmap will contain types from the actual response. + * - For non-ANY query, the bitmap will contain types from zone and forced + * types which can be potentionally synthesized by other query modules. + */ +static dnssec_nsec_bitmap_t *synth_bitmap(knot_pkt_t *pkt, const knotd_qdata_t *qdata, + const uint16_t *force_types) +{ + dnssec_nsec_bitmap_t *map = dnssec_nsec_bitmap_new(); + if (!map) { + return NULL; + } + + uint16_t qtype = knot_pkt_qtype(qdata->query); + bool is_apex = qdata->extra->zone + && qdata->extra->zone->contents + && qdata->extra->node == qdata->extra->zone->contents->apex; + + bitmap_add_synth(map, is_apex); + + if (qtype == KNOT_RRTYPE_ANY) { + const knot_pktsection_t *answer = knot_pkt_section(pkt, KNOT_ANSWER); + bitmap_add_section(map, answer); + } else { + bitmap_add_zone(map, qdata->extra->node); + if (force_types != NULL && !node_rrtype_exists(qdata->extra->node, KNOT_RRTYPE_CNAME)) { + bitmap_add_forced(map, qtype, force_types); + } + } + + return map; +} + +static bool is_deleg(const knot_pkt_t *pkt) +{ + return !knot_wire_get_aa(pkt->wire); +} + +static knot_rrset_t *synth_nsec(knot_pkt_t *pkt, knotd_qdata_t *qdata, knotd_mod_t *mod, + knot_mm_t *mm) +{ + const knot_dname_t *nsec_owner = is_deleg(pkt) ? qdata->extra->encloser->owner : qdata->name; + knot_rrset_t *nsec = knot_rrset_new(nsec_owner, KNOT_RRTYPE_NSEC, + KNOT_CLASS_IN, nsec_ttl(qdata), mm); + if (!nsec) { + return NULL; + } + + knot_dname_t *next = online_nsec_next(nsec_owner, knotd_qdata_zone_name(qdata)); + if (!next) { + knot_rrset_free(nsec, mm); + return NULL; + } + + // If necessary, prepare types to force into NSEC bitmap. + uint16_t *force_types = NULL; + if (!is_deleg(pkt)) { + online_sign_ctx_t *ctx = knotd_mod_ctx(mod); + force_types = ctx->nsec_force_types; + } + + dnssec_nsec_bitmap_t *bitmap = synth_bitmap(pkt, qdata, force_types); + if (!bitmap) { + free(next); + knot_rrset_free(nsec, mm); + return NULL; + } + + size_t size = knot_dname_size(next) + dnssec_nsec_bitmap_size(bitmap); + uint8_t rdata[size]; + + int written = knot_dname_to_wire(rdata, next, size); + dnssec_nsec_bitmap_write(bitmap, rdata + written); + + knot_dname_free(next, NULL); + dnssec_nsec_bitmap_free(bitmap); + + if (knot_rrset_add_rdata(nsec, rdata, size, mm) != KNOT_EOK) { + knot_rrset_free(nsec, mm); + return NULL; + } + + return nsec; +} + +static knot_rrset_t *sign_rrset(const knot_dname_t *owner, + const knot_rrset_t *cover, + knotd_mod_t *mod, + zone_sign_ctx_t *sign_ctx, + knot_mm_t *mm) +{ + // copy of RR set with replaced owner name + + knot_rrset_t *copy = knot_rrset_new(owner, cover->type, cover->rclass, + cover->ttl, NULL); + if (!copy) { + return NULL; + } + + if (knot_rdataset_copy(©->rrs, &cover->rrs, NULL) != KNOT_EOK) { + knot_rrset_free(copy, NULL); + return NULL; + } + + // resulting RRSIG + + knot_rrset_t *rrsig = knot_rrset_new(owner, KNOT_RRTYPE_RRSIG, copy->rclass, + copy->ttl, mm); + if (!rrsig) { + knot_rrset_free(copy, NULL); + return NULL; + } + + online_sign_ctx_t *ctx = knotd_mod_ctx(mod); + pthread_rwlock_rdlock(&ctx->signing_mutex); + int ret = knot_sign_rrset2(rrsig, copy, sign_ctx, mm); + pthread_rwlock_unlock(&ctx->signing_mutex); + if (ret != KNOT_EOK) { + knot_rrset_free(copy, NULL); + knot_rrset_free(rrsig, mm); + return NULL; + } + + knot_rrset_free(copy, NULL); + + return rrsig; +} + +static glue_t *find_glue_for(const knot_rrset_t *rr, const knot_pkt_t *pkt) +{ + for (int i = KNOT_ANSWER; i <= KNOT_AUTHORITY; i++) { + const knot_pktsection_t *section = knot_pkt_section(pkt, i); + for (int j = 0; j < section->count; j++) { + const knot_rrset_t *attempt = knot_pkt_rr(section, j); + const additional_t *a = attempt->additional; + for (int k = 0; a != NULL && k < a->count; k++) { + // no need for knot_dname_cmp because the pointers are assigned + if (a->glues[k].node->owner == rr->owner) { + return &a->glues[k]; + } + } + } + } + return NULL; +} + +static bool shall_sign_rr(const knot_rrset_t *rr, const knot_pkt_t *pkt) +{ + if (pkt->current == KNOT_ADDITIONAL) { + glue_t *g = find_glue_for(rr, pkt); + assert(g); // finds actually the node which is rr in + return !(g->node->flags & NODE_FLAGS_NONAUTH); + } else { + return !is_deleg(pkt) || rr->type == KNOT_RRTYPE_NSEC; + } +} + +static knotd_in_state_t sign_section(knotd_in_state_t state, knot_pkt_t *pkt, + knotd_qdata_t *qdata, knotd_mod_t *mod) +{ + if (!want_dnssec(qdata)) { + return state; + } + + const knot_pktsection_t *section = knot_pkt_section(pkt, pkt->current); + assert(section); + + zone_sign_ctx_t *sign_ctx = zone_sign_ctx(mod->keyset, mod->dnssec); + if (sign_ctx == NULL) { + return KNOTD_IN_STATE_ERROR; + } + + uint16_t count_unsigned = section->count; + for (int i = 0; i < count_unsigned; i++) { + const knot_rrset_t *rr = knot_pkt_rr(section, i); + if (!shall_sign_rr(rr, pkt)) { + continue; + } + + uint16_t rr_pos = knot_pkt_rr_offset(section, i); + + uint8_t owner[KNOT_DNAME_MAXLEN] = { 0 }; + knot_dname_unpack(owner, pkt->wire + rr_pos, sizeof(owner), pkt->wire); + knot_dname_to_lower(owner); + + knot_rrset_t *rrsig = sign_rrset(owner, rr, mod, sign_ctx, &pkt->mm); + if (!rrsig) { + state = KNOTD_IN_STATE_ERROR; + break; + } + + int r = knot_pkt_put(pkt, KNOT_COMPR_HINT_NONE, rrsig, KNOT_PF_FREE); + if (r != KNOT_EOK) { + knot_rrset_free(rrsig, &pkt->mm); + state = KNOTD_IN_STATE_ERROR; + break; + } + } + + zone_sign_ctx_free(sign_ctx); + + return state; +} + +static knotd_in_state_t synth_authority(knotd_in_state_t state, knot_pkt_t *pkt, + knotd_qdata_t *qdata, knotd_mod_t *mod) +{ + if (state == KNOTD_IN_STATE_HIT) { + return state; + } + + // synthesise NSEC + + if (want_dnssec(qdata)) { + knot_rrset_t *nsec = synth_nsec(pkt, qdata, mod, &pkt->mm); + int r = knot_pkt_put(pkt, KNOT_COMPR_HINT_NONE, nsec, KNOT_PF_FREE); + if (r != DNSSEC_EOK) { + knot_rrset_free(nsec, &pkt->mm); + return KNOTD_IN_STATE_ERROR; + } + } + + // promote NXDOMAIN to NODATA + + if (state == KNOTD_IN_STATE_MISS) { + //! \todo Override RCODE set in solver_authority. Review. + qdata->rcode = KNOT_RCODE_NOERROR; + return KNOTD_IN_STATE_NODATA; + } + + return state; +} + +static knot_rrset_t *synth_dnskey(knotd_qdata_t *qdata, knotd_mod_t *mod, + knot_mm_t *mm) +{ + knot_rrset_t *dnskey = knot_rrset_new(knotd_qdata_zone_name(qdata), + KNOT_RRTYPE_DNSKEY, KNOT_CLASS_IN, + dnskey_ttl(qdata), mm); + if (!dnskey) { + return 0; + } + + dnssec_binary_t rdata = { 0 }; + online_sign_ctx_t *ctx = knotd_mod_ctx(mod); + pthread_rwlock_rdlock(&ctx->signing_mutex); + for (size_t i = 0; i < mod->keyset->count; i++) { + if (!mod->keyset->keys[i].is_public) { + continue; + } + + dnssec_key_get_rdata(mod->keyset->keys[i].key, &rdata); + assert(rdata.size > 0 && rdata.data); + + int r = knot_rrset_add_rdata(dnskey, rdata.data, rdata.size, mm); + if (r != KNOT_EOK) { + knot_rrset_free(dnskey, mm); + pthread_rwlock_unlock(&ctx->signing_mutex); + return NULL; + } + } + + pthread_rwlock_unlock(&ctx->signing_mutex); + return dnskey; +} + +/* copied from the middle of zone-sign.c */ +static zone_key_t *ksk_for_cds(knotd_mod_t *mod) +{ + zone_key_t *res = NULL; + unsigned crp = mod->dnssec->policy->child_records_publish; + int kfc_prio = (crp == CHILD_RECORDS_ALWAYS ? + 0 : (crp == CHILD_RECORDS_ROLLOVER ? 1 : 2)); + for (int i = 0; i < mod->keyset->count; i++) { + zone_key_t *key = &mod->keyset->keys[i]; + if (key->is_ksk && key->cds_priority > kfc_prio) { + res = key; + kfc_prio = key->cds_priority; + } + } + return res; +} + +static knot_rrset_t *synth_cdnskey(knotd_qdata_t *qdata, knotd_mod_t *mod, + knot_mm_t *mm) +{ + knot_rrset_t *dnskey = knot_rrset_new(knotd_qdata_zone_name(qdata), + KNOT_RRTYPE_CDNSKEY, KNOT_CLASS_IN, + 0, mm); + if (dnskey == NULL) { + return 0; + } + + dnssec_binary_t rdata = { 0 }; + online_sign_ctx_t *ctx = knotd_mod_ctx(mod); + pthread_rwlock_rdlock(&ctx->signing_mutex); + zone_key_t *key = ksk_for_cds(mod); + if (key == NULL) { + pthread_rwlock_unlock(&ctx->signing_mutex); + knot_rrset_free(dnskey, mm); + return NULL; + } + dnssec_key_get_rdata(key->key, &rdata); + assert(rdata.size > 0 && rdata.data); + + int ret = knot_rrset_add_rdata(dnskey, rdata.data, rdata.size, mm); + pthread_rwlock_unlock(&ctx->signing_mutex); + if (ret != KNOT_EOK) { + knot_rrset_free(dnskey, mm); + return NULL; + } + + return dnskey; +} + +static knot_rrset_t *synth_cds(knotd_qdata_t *qdata, knotd_mod_t *mod, + knot_mm_t *mm) +{ + knot_rrset_t *ds = knot_rrset_new(knotd_qdata_zone_name(qdata), + KNOT_RRTYPE_CDS, KNOT_CLASS_IN, + 0, mm); + if (ds == NULL) { + return 0; + } + + dnssec_binary_t rdata = { 0 }; + online_sign_ctx_t *ctx = knotd_mod_ctx(mod); + pthread_rwlock_rdlock(&ctx->signing_mutex); + zone_key_t *key = ksk_for_cds(mod); + if (key == NULL) { + pthread_rwlock_unlock(&ctx->signing_mutex); + knot_rrset_free(ds, mm); + return NULL; + } + zone_key_calculate_ds(key, &rdata); + assert(rdata.size > 0 && rdata.data); + + int ret = knot_rrset_add_rdata(ds, rdata.data, rdata.size, mm); + pthread_rwlock_unlock(&ctx->signing_mutex); + if (ret != KNOT_EOK) { + knot_rrset_free(ds, mm); + return NULL; + } + + return ds; +} + +static bool qtype_match(knotd_qdata_t *qdata, uint16_t type) +{ + uint16_t qtype = knot_pkt_qtype(qdata->query); + return (qtype == KNOT_RRTYPE_ANY || qtype == type); +} + +static bool is_apex_query(knotd_qdata_t *qdata) +{ + return knot_dname_is_equal(qdata->name, knotd_qdata_zone_name(qdata)); +} + +static knotd_in_state_t pre_routine(knotd_in_state_t state, knot_pkt_t *pkt, + knotd_qdata_t *qdata, knotd_mod_t *mod) +{ + online_sign_ctx_t *ctx = knotd_mod_ctx(mod); + zone_sign_reschedule_t resch = { .allow_rollover = true }; + + (void)pkt, (void)qdata; + + pthread_mutex_lock(&ctx->event_mutex); + if (ctx->zone_doomed) { + pthread_mutex_unlock(&ctx->event_mutex); + return KNOTD_IN_STATE_ERROR; + } + mod->dnssec->now = time(NULL); + int ret = KNOT_ESEMCHECK; + if (knot_time_cmp(ctx->event_parent_ds_q, mod->dnssec->now) <= 0) { + pthread_rwlock_rdlock(&ctx->signing_mutex); + ret = knot_parent_ds_query(mod->dnssec, mod->keyset, 1000); + pthread_rwlock_unlock(&ctx->signing_mutex); + if (ret != KNOT_EOK && mod->dnssec->policy->ksk_sbm_check_interval > 0) { + ctx->event_parent_ds_q = mod->dnssec->now + mod->dnssec->policy->ksk_sbm_check_interval; + } else { + ctx->event_parent_ds_q = 0; + } + } + if (ret == KNOT_EOK || knot_time_cmp(ctx->event_rollover, mod->dnssec->now) <= 0) { + update_policy_from_zone(mod->dnssec->policy, qdata->extra->zone->contents); + ret = knot_dnssec_key_rollover(mod->dnssec, &resch); + } + if (ret == KNOT_EOK) { + if (resch.plan_ds_query && mod->dnssec->policy->ksk_sbm_check_interval > 0) { + ctx->event_parent_ds_q = mod->dnssec->now + mod->dnssec->policy->ksk_sbm_check_interval; + } else { + ctx->event_parent_ds_q = 0; + } + + ctx->event_rollover = resch.next_rollover; + + pthread_rwlock_wrlock(&ctx->signing_mutex); + knotd_mod_dnssec_unload_keyset(mod); + ret = knotd_mod_dnssec_load_keyset(mod, true); + if (ret != KNOT_EOK) { + ctx->zone_doomed = true; + state = KNOTD_IN_STATE_ERROR; + } else { + ctx->event_rollover = knot_time_min(ctx->event_rollover, knot_get_next_zone_key_event(mod->keyset)); + } + pthread_rwlock_unlock(&ctx->signing_mutex); + } + pthread_mutex_unlock(&ctx->event_mutex); + + return state; +} + +static knotd_in_state_t synth_answer(knotd_in_state_t state, knot_pkt_t *pkt, + knotd_qdata_t *qdata, knotd_mod_t *mod) +{ + // disallowed queries + + if (knot_pkt_qtype(pkt) == KNOT_RRTYPE_RRSIG) { + qdata->rcode = KNOT_RCODE_REFUSED; + return KNOTD_IN_STATE_ERROR; + } + + // synthesized DNSSEC answers + + if (qtype_match(qdata, KNOT_RRTYPE_DNSKEY) && is_apex_query(qdata)) { + knot_rrset_t *dnskey = synth_dnskey(qdata, mod, &pkt->mm); + if (!dnskey) { + return KNOTD_IN_STATE_ERROR; + } + + int r = knot_pkt_put(pkt, KNOT_COMPR_HINT_QNAME, dnskey, KNOT_PF_FREE); + if (r != DNSSEC_EOK) { + knot_rrset_free(dnskey, &pkt->mm); + return KNOTD_IN_STATE_ERROR; + } + state = KNOTD_IN_STATE_HIT; + } + + if (qtype_match(qdata, KNOT_RRTYPE_CDNSKEY) && is_apex_query(qdata)) { + knot_rrset_t *dnskey = synth_cdnskey(qdata, mod, &pkt->mm); + if (!dnskey) { + return KNOTD_IN_STATE_ERROR; + } + + int r = knot_pkt_put(pkt, KNOT_COMPR_HINT_QNAME, dnskey, KNOT_PF_FREE); + if (r != DNSSEC_EOK) { + knot_rrset_free(dnskey, &pkt->mm); + return KNOTD_IN_STATE_ERROR; + } + state = KNOTD_IN_STATE_HIT; + } + + if (qtype_match(qdata, KNOT_RRTYPE_CDS) && is_apex_query(qdata)) { + knot_rrset_t *ds = synth_cds(qdata, mod, &pkt->mm); + if (!ds) { + return KNOTD_IN_STATE_ERROR; + } + + int r = knot_pkt_put(pkt, KNOT_COMPR_HINT_QNAME, ds, KNOT_PF_FREE); + if (r != DNSSEC_EOK) { + knot_rrset_free(ds, &pkt->mm); + return KNOTD_IN_STATE_ERROR; + } + state = KNOTD_IN_STATE_HIT; + } + + if (qtype_match(qdata, KNOT_RRTYPE_NSEC)) { + knot_rrset_t *nsec = synth_nsec(pkt, qdata, mod, &pkt->mm); + if (!nsec) { + return KNOTD_IN_STATE_ERROR; + } + + int r = knot_pkt_put(pkt, KNOT_COMPR_HINT_QNAME, nsec, KNOT_PF_FREE); + if (r != DNSSEC_EOK) { + knot_rrset_free(nsec, &pkt->mm); + return KNOTD_IN_STATE_ERROR; + } + + state = KNOTD_IN_STATE_HIT; + } + + return state; +} + +static void online_sign_ctx_free(online_sign_ctx_t *ctx) +{ + pthread_mutex_destroy(&ctx->event_mutex); + pthread_rwlock_destroy(&ctx->signing_mutex); + + free(ctx->nsec_force_types); + free(ctx); +} + +static int online_sign_ctx_new(online_sign_ctx_t **ctx_ptr, knotd_mod_t *mod) +{ + online_sign_ctx_t *ctx = calloc(1, sizeof(*ctx)); + if (!ctx) { + return KNOT_ENOMEM; + } + + int ret = knotd_mod_dnssec_init(mod); + if (ret != KNOT_EOK) { + free(ctx); + return ret; + } + + // Force Singe-Type signing scheme. This is only important for compatibility with older versions. + mod->dnssec->policy->singe_type_signing = true; + + zone_sign_reschedule_t resch = { + .allow_rollover = true + }; + ret = knot_dnssec_key_rollover(mod->dnssec, &resch); + if (ret != KNOT_EOK) { + free(ctx); + return ret; + } + + if (resch.plan_ds_query) { + ctx->event_parent_ds_q = time(NULL); + } + ctx->event_rollover = resch.next_rollover; + + ret = knotd_mod_dnssec_load_keyset(mod, true); + if (ret != KNOT_EOK) { + free(ctx); + return ret; + } + + ctx->event_rollover = knot_time_min(ctx->event_rollover, knot_get_next_zone_key_event(mod->keyset)); + + pthread_mutex_init(&ctx->event_mutex, NULL); + pthread_rwlock_init(&ctx->signing_mutex, NULL); + + *ctx_ptr = ctx; + + return KNOT_EOK; +} + +int load_nsec_bitmap(online_sign_ctx_t *ctx, knotd_conf_t *conf) +{ + int count = (conf->count > 0) ? conf->count : sizeof(NSEC_FORCE_TYPES) / sizeof(uint16_t); + ctx->nsec_force_types = calloc(count + 1, sizeof(uint16_t)); + if (ctx->nsec_force_types == NULL) { + return KNOT_ENOMEM; + } + + if (conf->count == 0) { + // Use the default list. + for (int i = 0; NSEC_FORCE_TYPES[i] > 0; i++) { + ctx->nsec_force_types[i] = NSEC_FORCE_TYPES[i]; + } + } else { + for (int i = 0; i < conf->count; i++) { + int ret = knot_rrtype_from_string(conf->multi[i].string, + &ctx->nsec_force_types[i]); + if (ret != 0) { + return KNOT_EINVAL; + } + } + } + + return KNOT_EOK; +} + +int online_sign_load(knotd_mod_t *mod) +{ + knotd_conf_t conf = knotd_conf_zone(mod, C_DNSSEC_SIGNING, + knotd_mod_zone(mod)); + if (conf.single.boolean) { + knotd_mod_log(mod, LOG_ERR, "incompatible with automatic signing"); + return KNOT_ENOTSUP; + } + + online_sign_ctx_t *ctx = NULL; + int ret = online_sign_ctx_new(&ctx, mod); + if (ret != KNOT_EOK) { + knotd_mod_log(mod, LOG_ERR, "failed to initialize signing key (%s)", + knot_strerror(ret)); + return KNOT_ERROR; + } + + conf = knotd_conf_mod(mod, MOD_NSEC_BITMAP); + ret = load_nsec_bitmap(ctx, &conf); + knotd_conf_free(&conf); + if (ret != KNOT_EOK) { + online_sign_ctx_free(ctx); + return ret; + } + + knotd_mod_ctx_set(mod, ctx); + + knotd_mod_in_hook(mod, KNOTD_STAGE_ANSWER, pre_routine); + + knotd_mod_in_hook(mod, KNOTD_STAGE_ANSWER, synth_answer); + knotd_mod_in_hook(mod, KNOTD_STAGE_ANSWER, sign_section); + + knotd_mod_in_hook(mod, KNOTD_STAGE_AUTHORITY, synth_authority); + knotd_mod_in_hook(mod, KNOTD_STAGE_AUTHORITY, sign_section); + + knotd_mod_in_hook(mod, KNOTD_STAGE_ADDITIONAL, sign_section); + + return KNOT_EOK; +} + +void online_sign_unload(knotd_mod_t *mod) +{ + online_sign_ctx_free(knotd_mod_ctx(mod)); +} + +KNOTD_MOD_API(onlinesign, KNOTD_MOD_FLAG_SCOPE_ZONE | KNOTD_MOD_FLAG_OPT_CONF, + online_sign_load, online_sign_unload, online_sign_conf, NULL); diff --git a/src/knot/modules/onlinesign/onlinesign.rst b/src/knot/modules/onlinesign/onlinesign.rst new file mode 100644 index 0000000..5b34186 --- /dev/null +++ b/src/knot/modules/onlinesign/onlinesign.rst @@ -0,0 +1,152 @@ +.. _mod-onlinesign: + +``onlinesign`` — Online DNSSEC signing +====================================== + +The module provides online DNSSEC signing. Instead of pre-computing the zone +signatures when the zone is loaded into the server or instead of loading an +externally signed zone, the signatures are computed on-the-fly during +answering. + +The main purpose of the module is to enable authenticated responses with +zones which use other dynamic module (e.g., automatic reverse record +synthesis) because these zones cannot be pre-signed. However, it can be also +used as a simple signing solution for zones with low traffic and also as +a protection against zone content enumeration (zone walking). + +In order to minimize the number of computed signatures per query, the module +produces a bit different responses from the responses that would be sent if +the zone was pre-signed. Still, the responses should be perfectly valid for +a DNSSEC validating resolver. + +.. rubric:: Differences from statically signed zones: + +* The NSEC records are constructed as Minimally Covering NSEC Records + (:rfc:`7129#appendix-A`). Therefore the generated domain names cover + the complete domain name space in the zone's authority. + +* NXDOMAIN responses are promoted to NODATA responses. The module proves + that the query type does not exist rather than that the domain name does not + exist. + +* Domain names matching a wildcard are expanded. The module pretends and proves + that the domain name exists rather than proving a presence of the wildcard. + +.. rubric:: Records synthesized by the module: + +* DNSKEY record is synthesized in the zone apex and includes public key + material for the active signing key. + +* NSEC records are synthesized as needed. + +* RRSIG records are synthesized for authoritative content of the zone. + +* CDNSKEY and CDS records are generated as usual to publish valid Secure Entry Point. + +.. rubric:: Known issues: + +* The `knotc zone-ksk-submitted` command does not work well and is discouraged. + The module must be reloaded afterwards. + +.. rubric:: Limitations: + +* Online-sign module always enforces Single-Type Signing scheme. + +* After any change to KASP DB, the module must be reloaded to apply the changed keys. + +* The NSEC records may differ for one domain name if queried for different + types. This is an implementation shortcoming as the dynamic modules + cooperate loosely. Possible synthesis of a type by other module cannot + be predicted. This dissimilarity should not affect response validation, + even with validators performing `aggressive negative caching + <https://datatracker.ietf.org/doc/draft-fujiwara-dnsop-nsec-aggressiveuse/>`_. + +.. rubric:: Recommendations: + +* Configure the module with an explicit signing policy which has the + :ref:`policy_rrsig-lifetime` value in the order of hours. + +Example +------- + +* Enable the module in the zone configuration with the default signing policy:: + + zone: + - domain: example.com + module: mod-onlinesign + + Or with an explicit signing policy:: + + policy: + - id: rsa + algorithm: RSASHA256 + zsk-size: 2048 + rrsig-lifetime: 25h + + mod-onlinesign: + - id: explicit + policy: rsa + + zone: + - domain: example.com + module: mod-onlinesign/explicit + + Or use manual policy in an analogous manner, see + :ref:`Manual key management<dnssec-manual-key-management>`. + +* Make sure the zone is not signed and also that the automatic signing is + disabled. All is set, you are good to go. Reload (or start) the server: + + .. code-block:: console + + $ knotc reload + +The following example stacks the online signing with reverse record synthesis +module:: + + mod-synthrecord: + - id: lan-forward + type: forward + prefix: ip- + ttl: 1200 + network: 192.168.100.0/24 + + zone: + - domain: corp.example.net + module: [mod-synthrecord/lan-forward, mod-onlinesign] + +Module reference +---------------- + +:: + + mod-onlinesign: + - id: STR + policy: STR + nsec-bitmap: STR ... + +.. _mod-onlinesign_id: + +id +.. + +A module identifier. + +.. _mod-onlinesign_policy: + +policy +...... + +A :ref:`reference<policy_id>` to DNSSEC signing policy. A special *default* +value can be used for the default policy setting. + +.. _mod-onlinesign_nsec-bitmap: + +nsec-bitmap +........... + +A list of Resource Record types included in an NSEC bitmap generated by the module. +This option should reflect zone contents or synthesized responses by modules, +such as :ref:`synthrecord<mod-synthrecord>` and :ref:`GeoIP<mod-geoip>`. + +*Default:* [A, AAAA] diff --git a/src/knot/modules/queryacl/Makefile.inc b/src/knot/modules/queryacl/Makefile.inc new file mode 100644 index 0000000..25dcc38 --- /dev/null +++ b/src/knot/modules/queryacl/Makefile.inc @@ -0,0 +1,12 @@ +knot_modules_queryacl_la_SOURCES = knot/modules/queryacl/queryacl.c +EXTRA_DIST += knot/modules/queryacl/queryacl.rst + +if STATIC_MODULE_queryacl +libknotd_la_SOURCES += $(knot_modules_queryacl_la_SOURCES) +endif + +if SHARED_MODULE_queryacl +knot_modules_queryacl_la_LDFLAGS = $(KNOTD_MOD_LDFLAGS) +knot_modules_queryacl_la_CPPFLAGS = $(KNOTD_MOD_CPPFLAGS) +pkglib_LTLIBRARIES += knot/modules/queryacl.la +endif diff --git a/src/knot/modules/queryacl/queryacl.c b/src/knot/modules/queryacl/queryacl.c new file mode 100644 index 0000000..38a4f3c --- /dev/null +++ b/src/knot/modules/queryacl/queryacl.c @@ -0,0 +1,98 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include "knot/include/module.h" +#include "contrib/sockaddr.h" + +#define MOD_ADDRESS "\x07""address" +#define MOD_INTERFACE "\x09""interface" + +const yp_item_t queryacl_conf[] = { + { MOD_ADDRESS, YP_TNET, YP_VNONE, YP_FMULTI }, + { MOD_INTERFACE, YP_TNET, YP_VNONE, YP_FMULTI }, + { NULL } +}; + +typedef struct { + knotd_conf_t allow_addr; + knotd_conf_t allow_iface; +} queryacl_ctx_t; + +static knotd_state_t queryacl_process(knotd_state_t state, knot_pkt_t *pkt, + knotd_qdata_t *qdata, knotd_mod_t *mod) +{ + assert(pkt && qdata && mod); + + queryacl_ctx_t *ctx = knotd_mod_ctx(mod); + + // Continue only for regular queries. + if (qdata->type != KNOTD_QUERY_TYPE_NORMAL) { + return state; + } + + // Get interface address. + struct sockaddr_storage iface; + socklen_t iface_len = sizeof(iface); + if (getsockname(qdata->params->socket, (struct sockaddr *)&iface, &iface_len) != 0) { + knotd_mod_log(mod, LOG_ERR, "failed to get interface address"); + return KNOTD_STATE_FAIL; + } + + if (ctx->allow_addr.count > 0) { + if (!knotd_conf_addr_range_match(&ctx->allow_addr, qdata->params->remote)) { + qdata->rcode = KNOT_RCODE_NOTAUTH; + return KNOTD_STATE_FAIL; + } + } + + if (ctx->allow_iface.count > 0) { + if (!knotd_conf_addr_range_match(&ctx->allow_iface, &iface)) { + qdata->rcode = KNOT_RCODE_NOTAUTH; + return KNOTD_STATE_FAIL; + } + } + + return state; +} + +int queryacl_load(knotd_mod_t *mod) +{ + // Create module context. + queryacl_ctx_t *ctx = calloc(1, sizeof(queryacl_ctx_t)); + if (ctx == NULL) { + return KNOT_ENOMEM; + } + + ctx->allow_addr = knotd_conf_mod(mod, MOD_ADDRESS); + ctx->allow_iface = knotd_conf_mod(mod, MOD_INTERFACE); + + knotd_mod_ctx_set(mod, ctx); + + return knotd_mod_hook(mod, KNOTD_STAGE_BEGIN, queryacl_process); +} + +void queryacl_unload(knotd_mod_t *mod) +{ + queryacl_ctx_t *ctx = knotd_mod_ctx(mod); + if (ctx != NULL) { + knotd_conf_free(&ctx->allow_addr); + knotd_conf_free(&ctx->allow_iface); + } + free(ctx); +} + +KNOTD_MOD_API(queryacl, KNOTD_MOD_FLAG_SCOPE_ANY, + queryacl_load, queryacl_unload, queryacl_conf, NULL); diff --git a/src/knot/modules/queryacl/queryacl.rst b/src/knot/modules/queryacl/queryacl.rst new file mode 100644 index 0000000..14a5dfb --- /dev/null +++ b/src/knot/modules/queryacl/queryacl.rst @@ -0,0 +1,60 @@ +.. _mod-queryacl: + +``queryacl`` — Limit queries by remote address or target interface +================================================================== + +This module provides a simple way to whitelist incoming queries +according to the query's source address or target interface. +It can be used e.g. to create a restricted-access subzone with delegations from the corresponding public zone. +The module may be enabled both globally and per-zone. + +.. NOTE:: + The module limits only regular queries. Notify, transfer and update are handled by :ref:`ACL<ACL>`. + +Example +------- + +:: + + mod-queryacl: + - id: default + address: [192.0.2.73-192.0.2.90, 203.0.113.0/24] + interface: 198.51.100 + + zone: + - domain: example.com + module: mod-queryacl/default + +Module reference +---------------- + +:: + + mod-queryacl: + - id: STR + address: ADDR[/INT] | ADDR-ADDR ... + interface: ADDR[/INT] | ADDR-ADDR ... + +.. _mod-queryacl_id: + +id +.. + +A module identifier. + +.. _mod-queryacl_address: + +address +....... + +A list of allowed ranges and/or subnets for query's source address. If the query's address does not fall into any +of the configured ranges, NOTAUTH rcode is returned. + +.. _mod-queryacl_interface: + +interface +......... + +A list of allowed ranges and/or subnets for query's target interface. If the interface does not fall into any +of the configured ranges, NOTAUTH rcode is returned. Note that every interface used has to be configured in :ref:`listen<server_listen>`. + diff --git a/src/knot/modules/rrl/Makefile.inc b/src/knot/modules/rrl/Makefile.inc new file mode 100644 index 0000000..f7c7131 --- /dev/null +++ b/src/knot/modules/rrl/Makefile.inc @@ -0,0 +1,15 @@ +knot_modules_rrl_la_SOURCES = knot/modules/rrl/rrl.c \ + knot/modules/rrl/functions.c \ + knot/modules/rrl/functions.h +EXTRA_DIST += knot/modules/rrl/rrl.rst + +if STATIC_MODULE_rrl +libknotd_la_SOURCES += $(knot_modules_rrl_la_SOURCES) +endif + +if SHARED_MODULE_rrl +knot_modules_rrl_la_LDFLAGS = $(KNOTD_MOD_LDFLAGS) +knot_modules_rrl_la_CPPFLAGS = $(KNOTD_MOD_CPPFLAGS) +knot_modules_rrl_la_LIBADD = libcontrib.la +pkglib_LTLIBRARIES += knot/modules/rrl.la +endif diff --git a/src/knot/modules/rrl/functions.c b/src/knot/modules/rrl/functions.c new file mode 100644 index 0000000..0b2bc26 --- /dev/null +++ b/src/knot/modules/rrl/functions.c @@ -0,0 +1,525 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> +#include <time.h> + +#include "knot/modules/rrl/functions.h" +#include "contrib/sockaddr.h" +#include "contrib/time.h" +#include "libdnssec/error.h" +#include "libdnssec/random.h" + +/* Hopscotch defines. */ +#define HOP_LEN (sizeof(unsigned)*8) +/* Limits */ +#define RRL_CLSBLK_MAXLEN (4 + 8 + 1 + 256) +/* CIDR block prefix lengths for v4/v6 */ +#define RRL_V4_PREFIX ((uint32_t)0x00ffffff) /* /24 */ +#define RRL_V6_PREFIX ((uint64_t)0x00ffffffffffffff) /* /56 */ +/* Defaults */ +#define RRL_SSTART 2 /* 1/Nth of the rate for slow start */ +#define RRL_PSIZE_LARGE 1024 +#define RRL_CAPACITY 4 /* Window size in seconds */ +#define RRL_LOCK_GRANULARITY 32 /* Last digit granularity */ + +/* Classification */ +enum { + CLS_NULL = 0 << 0, /* Empty bucket. */ + CLS_NORMAL = 1 << 0, /* Normal response. */ + CLS_ERROR = 1 << 1, /* Error response. */ + CLS_NXDOMAIN = 1 << 2, /* NXDOMAIN (special case of error). */ + CLS_EMPTY = 1 << 3, /* Empty response. */ + CLS_LARGE = 1 << 4, /* Response size over threshold (1024k). */ + CLS_WILDCARD = 1 << 5, /* Wildcard query. */ + CLS_ANY = 1 << 6, /* ANY query (spec. class). */ + CLS_DNSSEC = 1 << 7 /* DNSSEC related RR query (spec. class) */ +}; + +/* Classification string. */ +struct cls_name { + int code; + const char *name; +}; + +static const struct cls_name rrl_cls_names[] = { + { CLS_NORMAL, "POSITIVE" }, + { CLS_ERROR, "ERROR" }, + { CLS_NXDOMAIN, "NXDOMAIN"}, + { CLS_EMPTY, "EMPTY"}, + { CLS_LARGE, "LARGE"}, + { CLS_WILDCARD, "WILDCARD"}, + { CLS_ANY, "ANY"}, + { CLS_DNSSEC, "DNSSEC"}, + { CLS_NULL, "NULL"}, + { CLS_NULL, NULL} +}; + +static inline const char *rrl_clsstr(int code) +{ + for (const struct cls_name *c = rrl_cls_names; c->name; c++) { + if (c->code == code) { + return c->name; + } + } + + return "unknown class"; +} + +/* Bucket flags. */ +enum { + RRL_BF_NULL = 0 << 0, /* No flags. */ + RRL_BF_SSTART = 1 << 0, /* Bucket in slow-start after collision. */ + RRL_BF_ELIMIT = 1 << 1 /* Bucket is rate-limited. */ +}; + +static uint8_t rrl_clsid(rrl_req_t *p) +{ + /* Check error code */ + int ret = CLS_NULL; + switch (knot_wire_get_rcode(p->w)) { + case KNOT_RCODE_NOERROR: ret = CLS_NORMAL; break; + case KNOT_RCODE_NXDOMAIN: return CLS_NXDOMAIN; break; + default: return CLS_ERROR; break; + } + + /* Check if answered from a qname */ + if (ret == CLS_NORMAL && p->flags & RRL_REQ_WILDCARD) { + return CLS_WILDCARD; + } + + /* Check query type for spec. classes. */ + if (p->query) { + switch(knot_pkt_qtype(p->query)) { + case KNOT_RRTYPE_ANY: /* ANY spec. class */ + return CLS_ANY; + break; + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_RRSIG: + case KNOT_RRTYPE_DS: /* DNSSEC-related RR class. */ + return CLS_DNSSEC; + break; + default: + break; + } + } + + /* Check packet size for threshold. */ + if (p->len >= RRL_PSIZE_LARGE) { + return CLS_LARGE; + } + + /* Check ancount */ + if (knot_wire_get_ancount(p->w) == 0) { + return CLS_EMPTY; + } + + return ret; +} + +static int rrl_clsname(uint8_t *dst, size_t maxlen, uint8_t cls, rrl_req_t *req, + const knot_dname_t *name) +{ + if (name == NULL) { + /* Fallback for errors etc. */ + name = (const knot_dname_t *)"\x00"; + } + + switch (cls) { + case CLS_ERROR: /* Could be a non-existent zone or garbage. */ + case CLS_NXDOMAIN: /* Queries to non-existent names in zone. */ + case CLS_WILDCARD: /* Queries to names covered by a wildcard. */ + break; + default: + /* Use QNAME */ + if (req->query) { + name = knot_pkt_qname(req->query); + } + break; + } + + /* Write to wire */ + return knot_dname_to_wire(dst, name, maxlen); +} + +static int rrl_classify(uint8_t *dst, size_t maxlen, const struct sockaddr_storage *a, + rrl_req_t *req, const knot_dname_t *name) +{ + /* Class */ + uint8_t cls = rrl_clsid(req); + *dst = cls; + int blklen = sizeof(cls); + + /* Address (in network byteorder, adjust masks). */ + uint64_t nb = 0; + if (a->ss_family == AF_INET6) { + struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)a; + nb = *((uint64_t *)(&ipv6->sin6_addr)) & RRL_V6_PREFIX; + } else { + struct sockaddr_in *ipv4 = (struct sockaddr_in *)a; + nb = ((uint32_t)ipv4->sin_addr.s_addr) & RRL_V4_PREFIX; + } + if (blklen + sizeof(nb) > maxlen) { + return KNOT_ESPACE; + } + memcpy(dst + blklen, (void *)&nb, sizeof(nb)); + blklen += sizeof(nb); + + /* Name */ + uint16_t *len_pos = (uint16_t *)(dst + blklen); + blklen += sizeof(uint16_t); + int ret = rrl_clsname(dst + blklen, maxlen - blklen, cls, req, name); + if (ret < 0) { + return ret; + } + uint16_t len = ret; + memcpy(len_pos, &len, sizeof(len)); + blklen += len; + + return blklen; +} + +static int bucket_free(rrl_item_t *b, uint32_t now) +{ + return b->cls == CLS_NULL || (b->time + 1 < now); +} + +static int bucket_match(rrl_item_t *b, rrl_item_t *m) +{ + return b->cls == m->cls && + b->netblk == m->netblk && + b->qname == m->qname; +} + +static int find_free(rrl_table_t *t, unsigned i, uint32_t now) +{ + rrl_item_t *np = t->arr + t->size; + rrl_item_t *b = NULL; + for (b = t->arr + i; b != np; ++b) { + if (bucket_free(b, now)) { + return b - (t->arr + i); + } + } + np = t->arr + i; + for (b = t->arr; b != np; ++b) { + if (bucket_free(b, now)) { + return (b - t->arr) + (t->size - i); + } + } + + /* this happens if table is full... force vacate current elm */ + return i; +} + +static inline unsigned find_match(rrl_table_t *t, uint32_t id, rrl_item_t *m) +{ + unsigned f = 0; + unsigned d = 0; + unsigned match = t->arr[id].hop; + while (match != 0) { + d = __builtin_ctz(match); + f = (id + d) % t->size; + if (bucket_match(t->arr + f, m)) { + return d; + } else { + match &= ~(1<<d); /* clear potential match */ + } + } + + return HOP_LEN + 1; +} + +static inline unsigned reduce_dist(rrl_table_t *t, unsigned id, unsigned d, unsigned *f) +{ + unsigned rd = HOP_LEN - 1; + while (rd > 0) { + unsigned s = (t->size + *f - rd) % t->size; /* bucket to be vacated */ + if (t->arr[s].hop != 0) { + unsigned o = __builtin_ctz(t->arr[s].hop); /* offset of first valid bucket */ + if (o < rd) { /* only offsets in <s, f> are interesting */ + unsigned e = (s + o) % t->size; /* this item will be displaced to [f] */ + unsigned keep_hop = t->arr[*f].hop; /* unpredictable padding */ + memcpy(t->arr + *f, t->arr + e, sizeof(rrl_item_t)); + t->arr[*f].hop = keep_hop; + t->arr[e].cls = CLS_NULL; + t->arr[s].hop &= ~(1<<o); + t->arr[s].hop |= 1<<rd; + *f = e; + return d - (rd - o); + } + } + --rd; + } + + assert(rd == 0); /* this happens with p=1/fact(HOP_LEN) */ + *f = id; + d = 0; /* force vacate initial element */ + return d; +} + +static void rrl_log_state(knotd_mod_t *mod, const struct sockaddr_storage *ss, + uint16_t flags, uint8_t cls) +{ + if (mod == NULL) { + return; + } + + char addr_str[SOCKADDR_STRLEN] = {0}; + sockaddr_tostr(addr_str, sizeof(addr_str), (struct sockaddr *)ss); + + const char *what = "leaves"; + if (flags & RRL_BF_ELIMIT) { + what = "enters"; + } + + knotd_mod_log(mod, LOG_NOTICE, "address %s, class %s, %s limiting", + addr_str, rrl_clsstr(cls), what); +} + +static void rrl_lock(rrl_table_t *t, int lk_id) +{ + assert(lk_id > -1); + pthread_mutex_lock(t->lk + lk_id); +} + +static void rrl_unlock(rrl_table_t *t, int lk_id) +{ + assert(lk_id > -1); + pthread_mutex_unlock(t->lk + lk_id); +} + +static int rrl_setlocks(rrl_table_t *rrl, uint32_t granularity) +{ + assert(!rrl->lk); /* Cannot change while locks are used. */ + assert(granularity <= rrl->size / 10); /* Due to int. division err. */ + + if (pthread_mutex_init(&rrl->ll, NULL) < 0) { + return KNOT_ENOMEM; + } + + /* Alloc new locks. */ + rrl->lk = malloc(granularity * sizeof(pthread_mutex_t)); + if (!rrl->lk) { + return KNOT_ENOMEM; + } + memset(rrl->lk, 0, granularity * sizeof(pthread_mutex_t)); + + /* Initialize. */ + for (size_t i = 0; i < granularity; ++i) { + if (pthread_mutex_init(rrl->lk + i, NULL) < 0) { + break; + } + ++rrl->lk_count; + } + + /* Incomplete initialization */ + if (rrl->lk_count != granularity) { + for (size_t i = 0; i < rrl->lk_count; ++i) { + pthread_mutex_destroy(rrl->lk + i); + } + free(rrl->lk); + rrl->lk_count = 0; + return KNOT_ERROR; + } + + return KNOT_EOK; +} + +rrl_table_t *rrl_create(size_t size, uint32_t rate) +{ + if (size == 0) { + return NULL; + } + + const size_t tbl_len = sizeof(rrl_table_t) + size * sizeof(rrl_item_t); + rrl_table_t *t = calloc(1, tbl_len); + if (!t) { + return NULL; + } + t->size = size; + t->rate = rate; + + if (dnssec_random_buffer((uint8_t *)&t->key, sizeof(t->key)) != DNSSEC_EOK) { + free(t); + return NULL; + } + + if (rrl_setlocks(t, RRL_LOCK_GRANULARITY) != KNOT_EOK) { + free(t); + return NULL; + } + + return t; +} + +/*! \brief Get bucket for current combination of parameters. */ +static rrl_item_t *rrl_hash(rrl_table_t *t, const struct sockaddr_storage *a, + rrl_req_t *req, const knot_dname_t *zone, uint32_t stamp, + int *lock) +{ + uint8_t buf[RRL_CLSBLK_MAXLEN]; + int len = rrl_classify(buf, sizeof(buf), a, req, zone); + if (len < 0) { + return NULL; + } + + uint32_t id = SipHash24(&t->key, buf, len) % t->size; + + /* Lock for lookup. */ + pthread_mutex_lock(&t->ll); + + /* Find an exact match in <id, id + HOP_LEN). */ + uint8_t *qname = buf + sizeof(uint8_t) + sizeof(uint64_t); + uint64_t netblk; + memcpy(&netblk, buf + sizeof(uint8_t), sizeof(netblk)); + rrl_item_t match = { + .hop = 0, + .netblk = netblk, + .ntok = t->rate * RRL_CAPACITY, + .cls = buf[0], + .flags = RRL_BF_NULL, + .qname = SipHash24(&t->key, qname + 1, qname[0]), + .time = stamp + }; + + unsigned d = find_match(t, id, &match); + if (d > HOP_LEN) { /* not an exact match, find free element [f] */ + d = find_free(t, id, stamp); + } + + /* Reduce distance to fit <id, id + HOP_LEN) */ + unsigned f = (id + d) % t->size; + while (d >= HOP_LEN) { + d = reduce_dist(t, id, d, &f); + } + + /* Assign granular lock and unlock lookup. */ + *lock = f % t->lk_count; + rrl_lock(t, *lock); + pthread_mutex_unlock(&t->ll); + + /* found free elm 'k' which is in <id, id + HOP_LEN) */ + t->arr[id].hop |= (1 << d); + rrl_item_t *b = t->arr + f; + assert(f == (id+d) % t->size); + + /* Inspect bucket state. */ + unsigned hop = b->hop; + if (b->cls == CLS_NULL) { + memcpy(b, &match, sizeof(rrl_item_t)); + b->hop = hop; + } + /* Check for collisions. */ + if (!bucket_match(b, &match)) { + if (!(b->flags & RRL_BF_SSTART)) { + memcpy(b, &match, sizeof(rrl_item_t)); + b->hop = hop; + b->ntok = t->rate + t->rate / RRL_SSTART; + b->flags |= RRL_BF_SSTART; + } + } + + return b; +} + +int rrl_query(rrl_table_t *rrl, const struct sockaddr_storage *a, rrl_req_t *req, + const knot_dname_t *zone, knotd_mod_t *mod) +{ + if (!rrl || !req || !a) { + return KNOT_EINVAL; + } + + /* Calculate hash and fetch */ + int ret = KNOT_EOK; + int lock = -1; + uint32_t now = time_now().tv_sec; + rrl_item_t *b = rrl_hash(rrl, a, req, zone, now, &lock); + if (!b) { + if (lock > -1) { + rrl_unlock(rrl, lock); + } + return KNOT_ERROR; + } + + /* Calculate rate for dT */ + uint32_t dt = now - b->time; + if (dt > RRL_CAPACITY) { + dt = RRL_CAPACITY; + } + /* Visit bucket. */ + b->time = now; + if (dt > 0) { /* Window moved. */ + + /* Check state change. */ + if ((b->ntok > 0 || dt > 1) && (b->flags & RRL_BF_ELIMIT)) { + b->flags &= ~RRL_BF_ELIMIT; + rrl_log_state(mod, a, b->flags, b->cls); + } + + /* Add new tokens. */ + uint32_t dn = rrl->rate * dt; + if (b->flags & RRL_BF_SSTART) { /* Bucket in slow-start. */ + b->flags &= ~RRL_BF_SSTART; + } + b->ntok += dn; + if (b->ntok > RRL_CAPACITY * rrl->rate) { + b->ntok = RRL_CAPACITY * rrl->rate; + } + } + + /* Last item taken. */ + if (b->ntok == 1 && !(b->flags & RRL_BF_ELIMIT)) { + b->flags |= RRL_BF_ELIMIT; + rrl_log_state(mod, a, b->flags, b->cls); + } + + /* Decay current bucket. */ + if (b->ntok > 0) { + --b->ntok; + } else if (b->ntok == 0) { + ret = KNOT_ELIMIT; + } + + if (lock > -1) { + rrl_unlock(rrl, lock); + } + return ret; +} + +bool rrl_slip_roll(int n_slip) +{ + /* Now n_slip means every Nth answer slips. + * That represents a chance of 1/N that answer slips. + * Therefore, on average, from 100 answers 100/N will slip. */ + int threshold = RRL_SLIP_MAX / n_slip; + int roll = dnssec_random_uint16_t() % RRL_SLIP_MAX; + return (roll < threshold); +} + +void rrl_destroy(rrl_table_t *rrl) +{ + if (rrl) { + if (rrl->lk_count > 0) { + pthread_mutex_destroy(&rrl->ll); + } + for (size_t i = 0; i < rrl->lk_count; ++i) { + pthread_mutex_destroy(rrl->lk + i); + } + free(rrl->lk); + } + + free(rrl); +} diff --git a/src/knot/modules/rrl/functions.h b/src/knot/modules/rrl/functions.h new file mode 100644 index 0000000..21c88a7 --- /dev/null +++ b/src/knot/modules/rrl/functions.h @@ -0,0 +1,114 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <stdint.h> +#include <pthread.h> +#include <sys/socket.h> + +#include "libknot/libknot.h" +#include "knot/include/module.h" +#include "contrib/openbsd/siphash.h" + +/* Defaults */ +#define RRL_SLIP_MAX 100 + +/*! + * \brief RRL hash bucket. + */ +typedef struct { + unsigned hop; /* Hop bitmap. */ + uint64_t netblk; /* Prefix associated. */ + uint16_t ntok; /* Tokens available. */ + uint8_t cls; /* Bucket class. */ + uint8_t flags; /* Flags. */ + uint32_t qname; /* imputed(QNAME) hash. */ + uint32_t time; /* Timestamp. */ +} rrl_item_t; + +/*! + * \brief RRL hash bucket table. + * + * Table is fixed size, so collisions may occur and are dealt with + * in a way, that hashbucket rate is reset and enters slow-start for 1 dt. + * When a bucket is in a slow-start mode, it cannot reset again for the time + * period. + * + * To avoid lock contention, N locks are created and distributed amongst buckets. + * As of now lock K for bucket N is calculated as K = N % (num_buckets). + */ + +typedef struct { + SIPHASH_KEY key; /* Siphash key. */ + uint32_t rate; /* Configured RRL limit. */ + pthread_mutex_t ll; + pthread_mutex_t *lk; /* Table locks. */ + unsigned lk_count; /* Table lock count (granularity). */ + size_t size; /* Number of buckets. */ + rrl_item_t arr[]; /* Buckets. */ +} rrl_table_t; + +/*! \brief RRL request flags. */ +typedef enum { + RRL_REQ_NOFLAG = 0 << 0, /*!< No flags. */ + RRL_REQ_WILDCARD = 1 << 1 /*!< Query to wildcard name. */ +} rrl_req_flag_t; + +/*! + * \brief RRL request descriptor. + */ +typedef struct { + const uint8_t *w; + uint16_t len; + rrl_req_flag_t flags; + knot_pkt_t *query; +} rrl_req_t; + +/*! + * \brief Create a RRL table. + * \param size Fixed hashtable size (reasonable large prime is recommended). + * \param rate Rate (in pkts/sec). + * \return created table or NULL. + */ +rrl_table_t *rrl_create(size_t size, uint32_t rate); + +/*! + * \brief Query the RRL table for accept or deny, when the rate limit is reached. + * + * \param rrl RRL table. + * \param a Source address. + * \param req RRL request (containing resp., flags and question). + * \param zone Zone name related to the response (or NULL). + * \param mod Query module (needed for logging). + * \retval KNOT_EOK if passed. + * \retval KNOT_ELIMIT when the limit is reached. + */ +int rrl_query(rrl_table_t *rrl, const struct sockaddr_storage *a, rrl_req_t *req, + const knot_dname_t *zone, knotd_mod_t *mod); + +/*! + * \brief Roll a dice whether answer slips or not. + * \param n_slip Number represents every Nth answer that is slipped. + * \return true or false + */ +bool rrl_slip_roll(int n_slip); + +/*! + * \brief Destroy RRL table. + * \param rrl RRL table. + */ +void rrl_destroy(rrl_table_t *rrl); diff --git a/src/knot/modules/rrl/rrl.c b/src/knot/modules/rrl/rrl.c new file mode 100644 index 0000000..e3cea0d --- /dev/null +++ b/src/knot/modules/rrl/rrl.c @@ -0,0 +1,209 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "knot/include/module.h" +#include "knot/nameserver/process_query.h" // Dependency on qdata->extra! +#include "knot/modules/rrl/functions.h" + +#define MOD_RATE_LIMIT "\x0A""rate-limit" +#define MOD_SLIP "\x04""slip" +#define MOD_TBL_SIZE "\x0A""table-size" +#define MOD_WHITELIST "\x09""whitelist" + +const yp_item_t rrl_conf[] = { + { MOD_RATE_LIMIT, YP_TINT, YP_VINT = { 1, INT32_MAX } }, + { MOD_SLIP, YP_TINT, YP_VINT = { 0, RRL_SLIP_MAX, 1 } }, + { MOD_TBL_SIZE, YP_TINT, YP_VINT = { 1, INT32_MAX, 393241 } }, + { MOD_WHITELIST, YP_TNET, YP_VNONE, YP_FMULTI }, + { NULL } +}; + +int rrl_conf_check(knotd_conf_check_args_t *args) +{ + knotd_conf_t limit = knotd_conf_check_item(args, MOD_RATE_LIMIT); + if (limit.count == 0) { + args->err_str = "no rate limit specified"; + return KNOT_EINVAL; + } + + return KNOT_EOK; +} + +typedef struct { + rrl_table_t *rrl; + int slip; + knotd_conf_t whitelist; +} rrl_ctx_t; + +static const knot_dname_t *name_from_rrsig(const knot_rrset_t *rr) +{ + if (rr == NULL) { + return NULL; + } + if (rr->type != KNOT_RRTYPE_RRSIG) { + return NULL; + } + + // This is a signature. + return knot_rrsig_signer_name(rr->rrs.rdata); +} + +static const knot_dname_t *name_from_authrr(const knot_rrset_t *rr) +{ + if (rr == NULL) { + return NULL; + } + if (rr->type != KNOT_RRTYPE_NS && rr->type != KNOT_RRTYPE_SOA) { + return NULL; + } + + // This is a valid authority RR. + return rr->owner; +} + +static knotd_state_t ratelimit_apply(knotd_state_t state, knot_pkt_t *pkt, + knotd_qdata_t *qdata, knotd_mod_t *mod) +{ + assert(pkt && qdata && mod); + + rrl_ctx_t *ctx = knotd_mod_ctx(mod); + + // Rate limit is not applied to TCP connections. + if (!(qdata->params->flags & KNOTD_QUERY_FLAG_LIMIT_SIZE)) { + return state; + } + + // Rate limit is not applied to responses with a valid cookie. + if (qdata->params->flags & KNOTD_QUERY_FLAG_COOKIE) { + return state; + } + + // Exempt clients. + if (knotd_conf_addr_range_match(&ctx->whitelist, qdata->params->remote)) { + return state; + } + + rrl_req_t req = { + .w = pkt->wire, + .query = qdata->query + }; + + if (!EMPTY_LIST(qdata->extra->wildcards)) { + req.flags = RRL_REQ_WILDCARD; + } + + // Take the zone name if known. + const knot_dname_t *zone_name = knotd_qdata_zone_name(qdata); + + // Take the signer name as zone name if there is an RRSIG. + if (zone_name == NULL) { + const knot_pktsection_t *ans = knot_pkt_section(pkt, KNOT_ANSWER); + for (int i = 0; i < ans->count; i++) { + zone_name = name_from_rrsig(knot_pkt_rr(ans, i)); + if (zone_name != NULL) { + break; + } + } + } + + // Take the NS or SOA owner name if there is no RRSIG. + if (zone_name == NULL) { + const knot_pktsection_t *auth = knot_pkt_section(pkt, KNOT_AUTHORITY); + for (int i = 0; i < auth->count; i++) { + zone_name = name_from_authrr(knot_pkt_rr(auth, i)); + if (zone_name != NULL) { + break; + } + } + } + + if (rrl_query(ctx->rrl, qdata->params->remote, &req, zone_name, mod) == KNOT_EOK) { + // Rate limiting not applied. + return state; + } + + if (ctx->slip > 0 && rrl_slip_roll(ctx->slip)) { + // Slip the answer. + knotd_mod_stats_incr(mod, 0, 0, 1); + qdata->err_truncated = true; + return KNOTD_STATE_FAIL; + } else { + // Drop the answer. + knotd_mod_stats_incr(mod, 1, 0, 1); + return KNOTD_STATE_NOOP; + } +} + +static void ctx_free(rrl_ctx_t *ctx) +{ + assert(ctx); + + rrl_destroy(ctx->rrl); + free(ctx); +} + +int rrl_load(knotd_mod_t *mod) +{ + // Create RRL context. + rrl_ctx_t *ctx = calloc(1, sizeof(rrl_ctx_t)); + if (ctx == NULL) { + return KNOT_ENOMEM; + } + + // Create table. + knotd_conf_t rate = knotd_conf_mod(mod, MOD_RATE_LIMIT); + knotd_conf_t size = knotd_conf_mod(mod, MOD_TBL_SIZE); + ctx->rrl = rrl_create(size.single.integer, rate.single.integer); + if (ctx->rrl == NULL) { + ctx_free(ctx); + return KNOT_ENOMEM; + } + + // Get slip. + knotd_conf_t conf = knotd_conf_mod(mod, MOD_SLIP); + ctx->slip = conf.single.integer; + + // Get whitelist. + ctx->whitelist = knotd_conf_mod(mod, MOD_WHITELIST); + + // Set up statistics counters. + int ret = knotd_mod_stats_add(mod, "slipped", 1, NULL); + if (ret != KNOT_EOK) { + ctx_free(ctx); + return ret; + } + + ret = knotd_mod_stats_add(mod, "dropped", 1, NULL); + if (ret != KNOT_EOK) { + ctx_free(ctx); + return ret; + } + + knotd_mod_ctx_set(mod, ctx); + + return knotd_mod_hook(mod, KNOTD_STAGE_END, ratelimit_apply); +} + +void rrl_unload(knotd_mod_t *mod) +{ + rrl_ctx_t *ctx = knotd_mod_ctx(mod); + + knotd_conf_free(&ctx->whitelist); + ctx_free(ctx); +} + +KNOTD_MOD_API(rrl, KNOTD_MOD_FLAG_SCOPE_ANY, + rrl_load, rrl_unload, rrl_conf, rrl_conf_check); diff --git a/src/knot/modules/rrl/rrl.rst b/src/knot/modules/rrl/rrl.rst new file mode 100644 index 0000000..7eac173 --- /dev/null +++ b/src/knot/modules/rrl/rrl.rst @@ -0,0 +1,133 @@ +.. _mod-rrl: + +``rrl`` — Response rate limiting +================================ + +Response rate limiting (RRL) is a method to combat DNS reflection amplification +attacks. These attacks rely on the fact that source address of a UDP query +can be forged, and without a worldwide deployment of `BCP38 +<https://tools.ietf.org/html/bcp38>`_, such a forgery cannot be prevented. +An attacker can use a DNS server (or multiple servers) as an amplification +source and can flood a victim with a large number of unsolicited DNS responses. +The RRL lowers the amplification factor of these attacks by sending some of +the responses as truncated or by dropping them altogether. + +.. NOTE:: + The module introduces two statistics counters. The number of slipped and + dropped responses. + +.. NOTE:: + If the :ref:`Cookies<mod-cookies>` module is active, RRL is not applied + for responses with a valid DNS cookie. + +Example +------- + +You can enable RRL by setting the module globally or per zone. + +:: + + mod-rrl: + - id: default + rate-limit: 200 # Allow 200 resp/s for each flow + slip: 2 # Every other response slips + + template: + - id: default + global-module: mod-rrl/default # Enable RRL globally + +Module reference +---------------- + +:: + + mod-rrl: + - id: STR + rate-limit: INT + slip: INT + table-size: INT + whitelist: ADDR[/INT] | ADDR-ADDR ... + +.. _mod-rrl_id: + +id +.. + +A module identifier. + +.. _mod-rrl_rate-limit: + +rate-limit +.......... + +Rate limiting is based on the token bucket scheme. A rate basically +represents a number of tokens available each second. Each response is +processed and classified (based on several discriminators, e.g. +source netblock, query type, zone name, rcode, etc.). Classified responses are +then hashed and assigned to a bucket containing number of available +tokens, timestamp and metadata. When available tokens are exhausted, +response is dropped or sent as truncated (see :ref:`mod-rrl_slip`). +Number of available tokens is recalculated each second. + +*Required* + +.. _mod-rrl_table-size: + +table-size +.......... + +Size of the hash table in a number of buckets. The larger the hash table, the lesser +the probability of a hash collision, but at the expense of additional memory costs. +Each bucket is estimated roughly to 32 bytes. The size should be selected as +a reasonably large prime due to better hash function distribution properties. +Hash table is internally chained and works well up to a fill rate of 90 %, general +rule of thumb is to select a prime near 1.2 * maximum_qps. + +*Default:* 393241 + +.. _mod-rrl_slip: + +slip +.... + +As attacks using DNS/UDP are usually based on a forged source address, +an attacker could deny services to the victim's netblock if all +responses would be completely blocked. The idea behind SLIP mechanism +is to send each N\ :sup:`th` response as truncated, thus allowing client to +reconnect via TCP for at least some degree of service. It is worth +noting, that some responses can't be truncated (e.g. SERVFAIL). + +- Setting the value to **0** will cause that all rate-limited responses will + be dropped. The outbound bandwidth and packet rate will be strictly capped + by the :ref:`mod-rrl_rate-limit` option. All legitimate requestors affected + by the limit will face denial of service and will observe excessive timeouts. + Therefore this setting is not recommended. + +- Setting the value to **1** will cause that all rate-limited responses will + be sent as truncated. The amplification factor of the attack will be reduced, + but the outbound data bandwidth won't be lower than the incoming bandwidth. + Also the outbound packet rate will be the same as without RRL. + +- Setting the value to **2** will cause that half of the rate-limited responses + will be dropped, the other half will be sent as truncated. With this + configuration, both outbound bandwidth and packet rate will be lower than the + inbound. On the other hand, the dropped responses enlarge the time window + for possible cache poisoning attack on the resolver. + +- Setting the value to anything **larger than 2** will keep on decreasing + the outgoing rate-limited bandwidth, packet rate, and chances to notify + legitimate requestors to reconnect using TCP. These attributes are inversely + proportional to the configured value. Setting the value high is not advisable. + +*Default:* 1 + +.. _mod-rrl_whitelist: + +whitelist +......... + +A list of IP addresses, network subnets, or network ranges to exempt from +rate limiting. Empty list means that no incoming connection will be +white-listed. + +*Default:* not set diff --git a/src/knot/modules/static_modules.h.in b/src/knot/modules/static_modules.h.in new file mode 100644 index 0000000..ef40dac --- /dev/null +++ b/src/knot/modules/static_modules.h.in @@ -0,0 +1,25 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "knot/include/module.h" + +// Forward declarations of static modules (generated by configure). +@STATIC_MODULES_DECLARS@ + +// STATIC_MODULES initializator (generated by configure). +#define STATIC_MODULES_INIT @STATIC_MODULES_INIT@ diff --git a/src/knot/modules/stats/Makefile.inc b/src/knot/modules/stats/Makefile.inc new file mode 100644 index 0000000..09fe6d8 --- /dev/null +++ b/src/knot/modules/stats/Makefile.inc @@ -0,0 +1,12 @@ +knot_modules_stats_la_SOURCES = knot/modules/stats/stats.c +EXTRA_DIST += knot/modules/stats/stats.rst + +if STATIC_MODULE_stats +libknotd_la_SOURCES += $(knot_modules_stats_la_SOURCES) +endif + +if SHARED_MODULE_stats +knot_modules_stats_la_LDFLAGS = $(KNOTD_MOD_LDFLAGS) +knot_modules_stats_la_CPPFLAGS = $(KNOTD_MOD_CPPFLAGS) +pkglib_LTLIBRARIES += knot/modules/stats.la +endif diff --git a/src/knot/modules/stats/stats.c b/src/knot/modules/stats/stats.c new file mode 100644 index 0000000..9f055a8 --- /dev/null +++ b/src/knot/modules/stats/stats.c @@ -0,0 +1,622 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "contrib/macros.h" +#include "contrib/wire_ctx.h" +#include "knot/include/module.h" +#include "knot/nameserver/xfr.h" // Dependency on qdata->extra! + +#define MOD_PROTOCOL "\x10""request-protocol" +#define MOD_OPERATION "\x10""server-operation" +#define MOD_REQ_BYTES "\x0D""request-bytes" +#define MOD_RESP_BYTES "\x0E""response-bytes" +#define MOD_EDNS "\x0D""edns-presence" +#define MOD_FLAG "\x0D""flag-presence" +#define MOD_RCODE "\x0D""response-code" +#define MOD_REQ_EOPT "\x13""request-edns-option" +#define MOD_RESP_EOPT "\x14""response-edns-option" +#define MOD_NODATA "\x0C""reply-nodata" +#define MOD_QTYPE "\x0A""query-type" +#define MOD_QSIZE "\x0A""query-size" +#define MOD_RSIZE "\x0A""reply-size" + +#define OTHER "other" + +const yp_item_t stats_conf[] = { + { MOD_PROTOCOL, YP_TBOOL, YP_VBOOL = { true } }, + { MOD_OPERATION, YP_TBOOL, YP_VBOOL = { true } }, + { MOD_REQ_BYTES, YP_TBOOL, YP_VBOOL = { true } }, + { MOD_RESP_BYTES, YP_TBOOL, YP_VBOOL = { true } }, + { MOD_EDNS, YP_TBOOL, YP_VNONE }, + { MOD_FLAG, YP_TBOOL, YP_VNONE }, + { MOD_RCODE, YP_TBOOL, YP_VBOOL = { true } }, + { MOD_REQ_EOPT, YP_TBOOL, YP_VNONE }, + { MOD_RESP_EOPT, YP_TBOOL, YP_VNONE }, + { MOD_NODATA, YP_TBOOL, YP_VNONE }, + { MOD_QTYPE, YP_TBOOL, YP_VNONE }, + { MOD_QSIZE, YP_TBOOL, YP_VNONE }, + { MOD_RSIZE, YP_TBOOL, YP_VNONE }, + { NULL } +}; + +enum { + CTR_PROTOCOL, + CTR_OPERATION, + CTR_REQ_BYTES, + CTR_RESP_BYTES, + CTR_EDNS, + CTR_FLAG, + CTR_RCODE, + CTR_REQ_EOPT, + CTR_RESP_EOPT, + CTR_NODATA, + CTR_QTYPE, + CTR_QSIZE, + CTR_RSIZE, +}; + +typedef struct { + bool protocol; + bool operation; + bool req_bytes; + bool resp_bytes; + bool edns; + bool flag; + bool rcode; + bool req_eopt; + bool resp_eopt; + bool nodata; + bool qtype; + bool qsize; + bool rsize; +} stats_t; + +typedef struct { + yp_name_t *conf_name; + size_t conf_offset; + uint32_t count; + knotd_mod_idx_to_str_f fcn; +} ctr_desc_t; + +enum { + OPERATION_QUERY = 0, + OPERATION_UPDATE, + OPERATION_NOTIFY, + OPERATION_AXFR, + OPERATION_IXFR, + OPERATION_INVALID, + OPERATION__COUNT +}; + +static char *operation_to_str(uint32_t idx, uint32_t count) +{ + switch (idx) { + case OPERATION_QUERY: return strdup("query"); + case OPERATION_UPDATE: return strdup("update"); + case OPERATION_NOTIFY: return strdup("notify"); + case OPERATION_AXFR: return strdup("axfr"); + case OPERATION_IXFR: return strdup("ixfr"); + case OPERATION_INVALID: return strdup("invalid"); + default: assert(0); return NULL; + } +} + +enum { + PROTOCOL_UDP4 = 0, + PROTOCOL_TCP4, + PROTOCOL_UDP6, + PROTOCOL_TCP6, + PROTOCOL__COUNT +}; + +static char *protocol_to_str(uint32_t idx, uint32_t count) +{ + switch (idx) { + case PROTOCOL_UDP4: return strdup("udp4"); + case PROTOCOL_TCP4: return strdup("tcp4"); + case PROTOCOL_UDP6: return strdup("udp6"); + case PROTOCOL_TCP6: return strdup("tcp6"); + default: assert(0); return NULL; + } +} + +enum { + REQ_BYTES_QUERY = 0, + REQ_BYTES_UPDATE, + REQ_BYTES_OTHER, + REQ_BYTES__COUNT +}; + +static char *req_bytes_to_str(uint32_t idx, uint32_t count) +{ + switch (idx) { + case REQ_BYTES_QUERY: return strdup("query"); + case REQ_BYTES_UPDATE: return strdup("update"); + case REQ_BYTES_OTHER: return strdup(OTHER); + default: assert(0); return NULL; + } +} + +enum { + RESP_BYTES_REPLY = 0, + RESP_BYTES_TRANSFER, + RESP_BYTES_OTHER, + RESP_BYTES__COUNT +}; + +static char *resp_bytes_to_str(uint32_t idx, uint32_t count) +{ + switch (idx) { + case RESP_BYTES_REPLY: return strdup("reply"); + case RESP_BYTES_TRANSFER: return strdup("transfer"); + case RESP_BYTES_OTHER: return strdup(OTHER); + default: assert(0); return NULL; + } +} + +enum { + EDNS_REQ = 0, + EDNS_RESP, + EDNS__COUNT +}; + +static char *edns_to_str(uint32_t idx, uint32_t count) +{ + switch (idx) { + case EDNS_REQ: return strdup("request"); + case EDNS_RESP: return strdup("response"); + default: assert(0); return NULL; + } +} + +enum { + FLAG_DO = 0, + FLAG_TC, + FLAG__COUNT +}; + +static char *flag_to_str(uint32_t idx, uint32_t count) +{ + switch (idx) { + case FLAG_TC: return strdup("TC"); + case FLAG_DO: return strdup("DO"); + default: assert(0); return NULL; + } +} + +enum { + NODATA_A = 0, + NODATA_AAAA, + NODATA_OTHER, + NODATA__COUNT +}; + +static char *nodata_to_str(uint32_t idx, uint32_t count) +{ + switch (idx) { + case NODATA_A: return strdup("A"); + case NODATA_AAAA: return strdup("AAAA"); + case NODATA_OTHER: return strdup(OTHER); + default: assert(0); return NULL; + } +} + +#define RCODE_BADSIG 15 // Unassigned code internally used for BADSIG. +#define RCODE_OTHER (KNOT_RCODE_BADCOOKIE + 1) // Other RCODES. + +static char *rcode_to_str(uint32_t idx, uint32_t count) +{ + const knot_lookup_t *rcode = NULL; + + switch (idx) { + case RCODE_BADSIG: + rcode = knot_lookup_by_id(knot_tsig_rcode_names, KNOT_RCODE_BADSIG); + break; + case RCODE_OTHER: + return strdup(OTHER); + default: + rcode = knot_lookup_by_id(knot_rcode_names, idx); + break; + } + + if (rcode != NULL) { + return strdup(rcode->name); + } else { + return NULL; + } +} + +#define EOPT_OTHER (KNOT_EDNS_MAX_OPTION_CODE + 1) +#define req_eopt_to_str eopt_to_str +#define resp_eopt_to_str eopt_to_str + +static char *eopt_to_str(uint32_t idx, uint32_t count) +{ + if (idx >= EOPT_OTHER) { + return strdup(OTHER); + } + + char str[32]; + if (knot_opt_code_to_string(idx, str, sizeof(str)) < 0) { + return NULL; + } else { + return strdup(str); + } +} + +enum { + QTYPE_OTHER = 0, + QTYPE_MIN1 = 1, + QTYPE_MAX1 = 65, + QTYPE_MIN2 = 99, + QTYPE_MAX2 = 110, + QTYPE_MIN3 = 255, + QTYPE_MAX3 = 260, + QTYPE_SHIFT2 = QTYPE_MIN2 - QTYPE_MAX1 - 1, + QTYPE_SHIFT3 = QTYPE_SHIFT2 + QTYPE_MIN3 - QTYPE_MAX2 - 1, + QTYPE__COUNT = QTYPE_MAX3 - QTYPE_SHIFT3 + 1 +}; + +static char *qtype_to_str(uint32_t idx, uint32_t count) +{ + if (idx == QTYPE_OTHER) { + return strdup(OTHER); + } + + uint16_t qtype; + + if (idx <= QTYPE_MAX1) { + qtype = idx; + assert(qtype >= QTYPE_MIN1 && qtype <= QTYPE_MAX1); + } else if (idx <= QTYPE_MAX2 - QTYPE_SHIFT2) { + qtype = idx + QTYPE_SHIFT2; + assert(qtype >= QTYPE_MIN2 && qtype <= QTYPE_MAX2); + } else { + qtype = idx + QTYPE_SHIFT3; + assert(qtype >= QTYPE_MIN3 && qtype <= QTYPE_MAX3); + } + + char str[32]; + if (knot_rrtype_to_string(qtype, str, sizeof(str)) < 0) { + return NULL; + } else { + return strdup(str); + } +} + +#define BUCKET_SIZE 16 +#define QSIZE_MAX_IDX (288 / BUCKET_SIZE) +#define RSIZE_MAX_IDX (4096 / BUCKET_SIZE) + +static char *size_to_str(uint32_t idx, uint32_t count) +{ + char str[16]; + + int ret; + if (idx < count - 1) { + ret = snprintf(str, sizeof(str), "%u-%u", idx * BUCKET_SIZE, + (idx + 1) * BUCKET_SIZE - 1); + } else { + ret = snprintf(str, sizeof(str), "%u-65535", idx * BUCKET_SIZE); + } + + if (ret <= 0 || (size_t)ret >= sizeof(str)) { + return NULL; + } else { + return strdup(str); + } +} + +static char *qsize_to_str(uint32_t idx, uint32_t count) +{ + return size_to_str(idx, count); +} + +static char *rsize_to_str(uint32_t idx, uint32_t count) +{ + return size_to_str(idx, count); +} + +static const ctr_desc_t ctr_descs[] = { + #define item(macro, name, count) \ + [CTR_##macro] = { MOD_##macro, offsetof(stats_t, name), (count), name##_to_str } + item(PROTOCOL, protocol, PROTOCOL__COUNT), + item(OPERATION, operation, OPERATION__COUNT), + item(REQ_BYTES, req_bytes, REQ_BYTES__COUNT), + item(RESP_BYTES, resp_bytes, RESP_BYTES__COUNT), + item(EDNS, edns, EDNS__COUNT), + item(FLAG, flag, FLAG__COUNT), + item(RCODE, rcode, RCODE_OTHER + 1), + item(REQ_EOPT, req_eopt, EOPT_OTHER + 1), + item(RESP_EOPT, resp_eopt, EOPT_OTHER + 1), + item(NODATA, nodata, NODATA__COUNT), + item(QTYPE, qtype, QTYPE__COUNT), + item(QSIZE, qsize, QSIZE_MAX_IDX + 1), + item(RSIZE, rsize, RSIZE_MAX_IDX + 1), + { NULL } +}; + +static void incr_edns_option(knotd_mod_t *mod, const knot_pkt_t *pkt, unsigned ctr_name) +{ + if (!knot_pkt_has_edns(pkt)) { + return; + } + + knot_rdata_t *rdata = pkt->opt_rr->rrs.rdata; + if (rdata == NULL || rdata->len == 0) { + return; + } + + wire_ctx_t wire = wire_ctx_init_const(rdata->data, rdata->len); + while (wire_ctx_available(&wire) > 0) { + uint16_t opt_code = wire_ctx_read_u16(&wire); + uint16_t opt_len = wire_ctx_read_u16(&wire); + wire_ctx_skip(&wire, opt_len); + if (wire.error != KNOT_EOK) { + break; + } + knotd_mod_stats_incr(mod, ctr_name, MIN(opt_code, EOPT_OTHER), 1); + } +} + +static knotd_state_t update_counters(knotd_state_t state, knot_pkt_t *pkt, + knotd_qdata_t *qdata, knotd_mod_t *mod) +{ + assert(pkt && qdata); + + stats_t *stats = knotd_mod_ctx(mod); + + uint16_t operation; + unsigned xfr_packets = 0; + + // Get the server operation. + switch (qdata->type) { + case KNOTD_QUERY_TYPE_NORMAL: + operation = OPERATION_QUERY; + break; + case KNOTD_QUERY_TYPE_UPDATE: + operation = OPERATION_UPDATE; + break; + case KNOTD_QUERY_TYPE_NOTIFY: + operation = OPERATION_NOTIFY; + break; + case KNOTD_QUERY_TYPE_AXFR: + operation = OPERATION_AXFR; + if (qdata->extra->ext != NULL) { + xfr_packets = ((struct xfr_proc *)qdata->extra->ext)->stats.messages; + } + break; + case KNOTD_QUERY_TYPE_IXFR: + operation = OPERATION_IXFR; + if (qdata->extra->ext != NULL) { + xfr_packets = ((struct xfr_proc *)qdata->extra->ext)->stats.messages; + } + break; + default: + operation = OPERATION_INVALID; + break; + } + + // Count request bytes. + if (stats->req_bytes) { + switch (operation) { + case OPERATION_QUERY: + knotd_mod_stats_incr(mod, CTR_REQ_BYTES, REQ_BYTES_QUERY, + knot_pkt_size(qdata->query)); + break; + case OPERATION_UPDATE: + knotd_mod_stats_incr(mod, CTR_REQ_BYTES, REQ_BYTES_UPDATE, + knot_pkt_size(qdata->query)); + break; + default: + if (xfr_packets <= 1) { + knotd_mod_stats_incr(mod, CTR_REQ_BYTES, REQ_BYTES_OTHER, + knot_pkt_size(qdata->query)); + } + break; + } + } + + // Count response bytes. + if (stats->resp_bytes && state != KNOTD_STATE_NOOP) { + switch (operation) { + case OPERATION_QUERY: + knotd_mod_stats_incr(mod, CTR_RESP_BYTES, RESP_BYTES_REPLY, + knot_pkt_size(pkt)); + break; + case OPERATION_AXFR: + case OPERATION_IXFR: + knotd_mod_stats_incr(mod, CTR_RESP_BYTES, RESP_BYTES_TRANSFER, + knot_pkt_size(pkt)); + break; + default: + knotd_mod_stats_incr(mod, CTR_RESP_BYTES, RESP_BYTES_OTHER, + knot_pkt_size(pkt)); + break; + } + } + + // Get the extended response code. + uint16_t rcode = qdata->rcode; + if (qdata->rcode_tsig != KNOT_RCODE_NOERROR) { + rcode = qdata->rcode_tsig; + } + + // Count the response code. + if (stats->rcode && state != KNOTD_STATE_NOOP) { + if (xfr_packets <= 1 || rcode != KNOT_RCODE_NOERROR) { + if (xfr_packets > 1) { + assert(rcode != KNOT_RCODE_NOERROR); + // Ignore the leading XFR message NOERROR. + knotd_mod_stats_decr(mod, CTR_RCODE, + KNOT_RCODE_NOERROR, 1); + } + + if (qdata->rcode_tsig == KNOT_RCODE_BADSIG) { + knotd_mod_stats_incr(mod, CTR_RCODE, RCODE_BADSIG, 1); + } else { + knotd_mod_stats_incr(mod, CTR_RCODE, + MIN(rcode, RCODE_OTHER), 1); + } + } + } + + // Return if non-first transfer message. + if (xfr_packets > 1) { + return state; + } + + // Count the server opearation. + if (stats->operation) { + knotd_mod_stats_incr(mod, CTR_OPERATION, operation, 1); + } + + // Count the request protocol. + if (stats->protocol) { + if (qdata->params->remote->ss_family == AF_INET) { + if (qdata->params->flags & KNOTD_QUERY_FLAG_LIMIT_SIZE) { + knotd_mod_stats_incr(mod, CTR_PROTOCOL, + PROTOCOL_UDP4, 1); + } else { + knotd_mod_stats_incr(mod, CTR_PROTOCOL, + PROTOCOL_TCP4, 1); + } + } else { + if (qdata->params->flags & KNOTD_QUERY_FLAG_LIMIT_SIZE) { + knotd_mod_stats_incr(mod, CTR_PROTOCOL, + PROTOCOL_UDP6, 1); + } else { + knotd_mod_stats_incr(mod, CTR_PROTOCOL, + PROTOCOL_TCP6, 1); + } + } + } + + // Count EDNS occurrences. + if (stats->edns) { + if (knot_pkt_has_edns(qdata->query)) { + knotd_mod_stats_incr(mod, CTR_EDNS, EDNS_REQ, 1); + } + if (knot_pkt_has_edns(pkt) && state != KNOTD_STATE_NOOP) { + knotd_mod_stats_incr(mod, CTR_EDNS, EDNS_RESP, 1); + } + } + + // Count interesting message header flags. + if (stats->flag) { + if (state != KNOTD_STATE_NOOP && knot_wire_get_tc(pkt->wire)) { + knotd_mod_stats_incr(mod, CTR_FLAG, FLAG_TC, 1); + } + if (knot_pkt_has_dnssec(pkt)) { + knotd_mod_stats_incr(mod, CTR_FLAG, FLAG_DO, 1); + } + } + + // Count EDNS options. + if (stats->req_eopt) { + incr_edns_option(mod, qdata->query, CTR_REQ_EOPT); + } + if (stats->resp_eopt) { + incr_edns_option(mod, pkt, CTR_RESP_EOPT); + } + + // Return if not query operation. + if (operation != OPERATION_QUERY) { + return state; + } + + // Count NODATA reply (RFC 2308, Section 2.2). + if (stats->nodata && rcode == KNOT_RCODE_NOERROR && state != KNOTD_STATE_NOOP && + knot_wire_get_ancount(pkt->wire) == 0 && !knot_wire_get_tc(pkt->wire) && + (knot_wire_get_nscount(pkt->wire) == 0 || + knot_pkt_rr(knot_pkt_section(pkt, KNOT_AUTHORITY), 0)->type == KNOT_RRTYPE_SOA)) { + switch (knot_pkt_qtype(qdata->query)) { + case KNOT_RRTYPE_A: + knotd_mod_stats_incr(mod, CTR_NODATA, NODATA_A, 1); + break; + case KNOT_RRTYPE_AAAA: + knotd_mod_stats_incr(mod, CTR_NODATA, NODATA_AAAA, 1); + break; + default: + knotd_mod_stats_incr(mod, CTR_NODATA, NODATA_OTHER, 1); + break; + } + } + + // Count the query type. + if (stats->qtype) { + uint16_t qtype = knot_pkt_qtype(qdata->query); + + uint16_t idx; + switch (qtype) { + case QTYPE_MIN1 ... QTYPE_MAX1: idx = qtype; break; + case QTYPE_MIN2 ... QTYPE_MAX2: idx = qtype - QTYPE_SHIFT2; break; + case QTYPE_MIN3 ... QTYPE_MAX3: idx = qtype - QTYPE_SHIFT3; break; + default: idx = QTYPE_OTHER; break; + } + + knotd_mod_stats_incr(mod, CTR_QTYPE, idx, 1); + } + + // Count the query size. + if (stats->qsize) { + uint64_t idx = knot_pkt_size(qdata->query) / BUCKET_SIZE; + knotd_mod_stats_incr(mod, CTR_QSIZE, MIN(idx, QSIZE_MAX_IDX), 1); + } + + // Count the reply size. + if (stats->rsize && state != KNOTD_STATE_NOOP) { + uint64_t idx = knot_pkt_size(pkt) / BUCKET_SIZE; + knotd_mod_stats_incr(mod, CTR_RSIZE, MIN(idx, RSIZE_MAX_IDX), 1); + } + + return state; +} + +int stats_load(knotd_mod_t *mod) +{ + stats_t *stats = calloc(1, sizeof(*stats)); + if (stats == NULL) { + return KNOT_ENOMEM; + } + + for (const ctr_desc_t *desc = ctr_descs; desc->conf_name != NULL; desc++) { + knotd_conf_t conf = knotd_conf_mod(mod, desc->conf_name); + bool enabled = conf.single.boolean; + + // Initialize corresponding configuration item. + *(bool *)((uint8_t *)stats + desc->conf_offset) = enabled; + + int ret = knotd_mod_stats_add(mod, enabled ? desc->conf_name + 1 : NULL, + enabled ? desc->count : 1, desc->fcn); + if (ret != KNOT_EOK) { + free(stats); + return ret; + } + } + + knotd_mod_ctx_set(mod, stats); + + return knotd_mod_hook(mod, KNOTD_STAGE_END, update_counters); +} + +void stats_unload(knotd_mod_t *mod) +{ + free(knotd_mod_ctx(mod)); +} + +KNOTD_MOD_API(stats, KNOTD_MOD_FLAG_SCOPE_ANY | KNOTD_MOD_FLAG_OPT_CONF, + stats_load, stats_unload, stats_conf, NULL); diff --git a/src/knot/modules/stats/stats.rst b/src/knot/modules/stats/stats.rst new file mode 100644 index 0000000..45dbc86 --- /dev/null +++ b/src/knot/modules/stats/stats.rst @@ -0,0 +1,266 @@ +.. _mod-stats: + +``stats`` — Query statistics +============================ + +The module extends server statistics with incoming DNS request and corresponding +response counters, such as used network protocol, total number of responded bytes, +etc (see module reference for full list of supported counters). +This module should be configured as the last module. + +.. NOTE:: + Server initiated communication (outgoing NOTIFY, incoming \*XFR,...) is not + counted by this module. + +.. NOTE:: + Leading 16-bit message size over TCP is not considered. + +Example +------- + +Common statistics with default module configuration:: + + template: + - id: default + global-module: mod-stats + +Per zone statistics with explicit module configuration:: + + mod-stats: + - id: custom + edns-presence: on + query-type: on + + template: + - id: default + module: mod-stats/custom + +Module reference +---------------- + +:: + + mod-stats: + - id: STR + request-protocol: BOOL + server-operation: BOOL + request-bytes: BOOL + response-bytes: BOOL + edns-presence: BOOL + flag-presence: BOOL + response-code: BOOL + request-edns-option: BOOL + response-edns-option: BOOL + reply-nodata: BOOL + query-type: BOOL + query-size: BOOL + reply-size: BOOL + +.. _mod-stats_id: + +id +.. + +A module identifier. + +.. _mod-stats_request-protocol: + +request-protocol +................ + +If enabled, all incoming requests are counted by the network protocol: + +* udp4 - UDP over IPv4 +* tcp4 - TCP over IPv4 +* udp6 - UDP over IPv6 +* tcp6 - TCP over IPv6 + +*Default:* on + +.. _mod-stats_server-operation: + +server-operation +................ + +If enabled, all incoming requests are counted by the server operation. The +server operation is based on message header OpCode and message query (meta) type: + +* query - Normal query operation +* update - Dynamic update operation +* notify - NOTIFY request operation +* axfr - Full zone transfer operation +* ixfr - Incremental zone transfer operation +* invalid - Invalid server operation + +*Default:* on + +.. _mod-stats_request-bytes: + +request-bytes +............. + +If enabled, all incoming request bytes are counted by the server operation: + +* query - Normal query bytes +* update - Dynamic update bytes +* other - Other request bytes + +*Default:* on + +.. _mod-stats_response-bytes: + +response-bytes +.............. + +If enabled, outgoing response bytes are counted by the server operation: + +* reply - Normal response bytes +* transfer - Zone transfer bytes +* other - Other response bytes + +.. WARNING:: + Dynamic update response bytes are not counted by this module. + +*Default:* on + +.. _mod-stats_edns-presence: + +edns-presence +............. + +If enabled, EDNS pseudo section presence is counted by the message direction: + +* request - EDNS present in request +* response - EDNS present in response + +*Default:* off + +.. _mod-stats_flag-presence: + +flag-presence +............. + +If enabled, some message header flags are counted: + +* TC - Truncated Answer in response +* DO - DNSSEC OK in request + +*Default:* off + +.. _mod-stats_response-code: + +response-code +............. + +If enabled, outgoing response code is counted: + +* NOERROR +* ... +* NOTZONE +* BADVERS +* ... +* BADCOOKIE +* other - All other codes + +.. NOTE:: + In the case of multi-message zone transfer response, just one counter is + incremented. + +.. WARNING:: + Dynamic update response code is not counted by this module. + +*Default:* on + +.. _mod-stats_request-edns-option: + +request-edns-option +................... + +If enabled, EDNS options in requests are counted by their code: + +* CODE0 +* ... +* EDNS-KEY-TAG (CODE14) +* other - All other codes + +*Default:* off + +.. _mod-stats_response-edns-option: + +response-edns-option +.................... + +If enabled, EDNS options in responses are counted by their code. See +:ref:`mod-stats_request-edns-option`. + +*Default:* off + +.. _mod-stats_reply-nodata: + +reply-nodata +............ + +If enabled, NODATA pseudo RCODE (:rfc:`2308#section-2.2`) is counted by the +query type: + +* A +* AAAA +* other - All other types + +*Default:* off + +.. _mod-stats_query-type: + +query-type +.......... + +If enabled, normal query type is counted: + +* A (TYPE1) +* ... +* TYPE65 +* SPF (TYPE99) +* ... +* TYPE110 +* ANY (TYPE255) +* ... +* TYPE260 +* other - All other types + +.. NOTE:: + Not all assigned meta types (IXFR, AXFR,...) have their own counters, + because such types are not processed as normal query. + +*Default:* off + +.. _mod-stats_query-size: + +query-size +.......... + +If enabled, normal query message size distribution is counted by the size range +in bytes: + +* 0-15 +* 16-31 +* ... +* 272-287 +* 288-65535 + +*Default:* off + +.. _mod-stats_reply-size: + +reply-size +.......... + +If enabled, normal reply message size distribution is counted by the size range +in bytes: + +* 0-15 +* 16-31 +* ... +* 4080-4095 +* 4096-65535 + +*Default:* off diff --git a/src/knot/modules/synthrecord/Makefile.inc b/src/knot/modules/synthrecord/Makefile.inc new file mode 100644 index 0000000..d7aca3b --- /dev/null +++ b/src/knot/modules/synthrecord/Makefile.inc @@ -0,0 +1,13 @@ +knot_modules_synthrecord_la_SOURCES = knot/modules/synthrecord/synthrecord.c +EXTRA_DIST += knot/modules/synthrecord/synthrecord.rst + +if STATIC_MODULE_synthrecord +libknotd_la_SOURCES += $(knot_modules_synthrecord_la_SOURCES) +endif + +if SHARED_MODULE_synthrecord +knot_modules_synthrecord_la_LDFLAGS = $(KNOTD_MOD_LDFLAGS) +knot_modules_synthrecord_la_CPPFLAGS = $(KNOTD_MOD_CPPFLAGS) +knot_modules_synthrecord_la_LIBADD = libcontrib.la +pkglib_LTLIBRARIES += knot/modules/synthrecord.la +endif diff --git a/src/knot/modules/synthrecord/synthrecord.c b/src/knot/modules/synthrecord/synthrecord.c new file mode 100644 index 0000000..b54e783 --- /dev/null +++ b/src/knot/modules/synthrecord/synthrecord.c @@ -0,0 +1,498 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "contrib/ctype.h" +#include "contrib/net.h" +#include "contrib/sockaddr.h" +#include "contrib/wire_ctx.h" +#include "knot/include/module.h" + +#define MOD_NET "\x07""network" +#define MOD_ORIGIN "\x06""origin" +#define MOD_PREFIX "\x06""prefix" +#define MOD_TTL "\x03""ttl" +#define MOD_TYPE "\x04""type" + +/*! \brief Supported answer synthesis template types. */ +enum synth_template_type { + SYNTH_NULL = 0, + SYNTH_FORWARD = 1, + SYNTH_REVERSE = 2 +}; + +static const knot_lookup_t synthetic_types[] = { + { SYNTH_FORWARD, "forward" }, + { SYNTH_REVERSE, "reverse" }, + { 0, NULL } +}; + +int check_prefix(knotd_conf_check_args_t *args) +{ + if (strchr((const char *)args->data, '.') != NULL) { + args->err_str = "dot '.' is not allowed"; + return KNOT_EINVAL; + } + + return KNOT_EOK; +} + +const yp_item_t synth_record_conf[] = { + { MOD_TYPE, YP_TOPT, YP_VOPT = { synthetic_types, SYNTH_NULL } }, + { MOD_PREFIX, YP_TSTR, YP_VSTR = { "" }, YP_FNONE, { check_prefix } }, + { MOD_ORIGIN, YP_TDNAME, YP_VNONE }, + { MOD_TTL, YP_TINT, YP_VINT = { 0, UINT32_MAX, 3600, YP_STIME } }, + { MOD_NET, YP_TNET, YP_VNONE, YP_FMULTI }, + { NULL } +}; + +int synth_record_conf_check(knotd_conf_check_args_t *args) +{ + // Check type. + knotd_conf_t type = knotd_conf_check_item(args, MOD_TYPE); + if (type.count == 0) { + args->err_str = "no synthesis type specified"; + return KNOT_EINVAL; + } + + // Check origin. + knotd_conf_t origin = knotd_conf_check_item(args, MOD_ORIGIN); + if (origin.count == 0 && type.single.option == SYNTH_REVERSE) { + args->err_str = "no origin specified"; + return KNOT_EINVAL; + } + if (origin.count != 0 && type.single.option == SYNTH_FORWARD) { + args->err_str = "origin not allowed with forward type"; + return KNOT_EINVAL; + } + + // Check network subnet. + knotd_conf_t net = knotd_conf_check_item(args, MOD_NET); + if (net.count == 0) { + args->err_str = "no network subnet specified"; + return KNOT_EINVAL; + } + knotd_conf_free(&net); + + return KNOT_EOK; +} + +#define ARPA_ZONE_LABELS 2 +#define IPV4_ADDR_LABELS 4 +#define IPV6_ADDR_LABELS 32 +#define IPV4_ARPA_DNAME (uint8_t *)"\x07""in-addr""\x04""arpa" +#define IPV6_ARPA_DNAME (uint8_t *)"\x03""ip6""\x04""arpa" + +/*! + * \brief Synthetic response template. + */ +typedef struct { + struct sockaddr_storage addr; + struct sockaddr_storage addr_max; + int addr_mask; +} synth_templ_addr_t; + +typedef struct { + enum synth_template_type type; + char *prefix; + size_t prefix_len; + char *zone; + size_t zone_len; + uint32_t ttl; + size_t addr_count; + synth_templ_addr_t *addr; +} synth_template_t; + +/*! \brief Substitute all occurrences of given character. */ +static void str_subst(char *str, size_t len, char from, char to) +{ + for (int i = 0; i < len; ++i) { + if (str[i] == from) { + str[i] = to; + } + } +} + +/*! \brief Separator character for address family. */ +static char str_separator(int addr_family) +{ + if (addr_family == AF_INET6) { + return ':'; + } + return '.'; +} + +/*! \brief Return true if query type is satisfied with provided address family. */ +static bool query_satisfied_by_family(uint16_t qtype, int family) +{ + switch (qtype) { + case KNOT_RRTYPE_A: return family == AF_INET; + case KNOT_RRTYPE_AAAA: return family == AF_INET6; + case KNOT_RRTYPE_ANY: return true; + default: return false; + } +} + +/*! \brief Parse address from reverse query QNAME and return address family. */ +static int reverse_addr_parse(knotd_qdata_t *qdata, char *addr_str, int *addr_family) +{ + /* QNAME required format is [address].[subnet/zone] + * f.e. [1.0...0].[h.g.f.e.0.0.0.0.d.c.b.a.ip6.arpa] represents + * [abcd:0:efgh::1] */ + const knot_dname_t *label = qdata->name; + const uint8_t *wire = qdata->query->wire; + + switch (knot_dname_labels(label, wire)) { + case IPV4_ADDR_LABELS + ARPA_ZONE_LABELS: + *addr_family = AF_INET; + + const knot_dname_t *label1 = label; + const knot_dname_t *label2 = knot_wire_next_label(label1, wire); + const knot_dname_t *label3 = knot_wire_next_label(label2, wire); + const knot_dname_t *label4 = knot_wire_next_label(label3, wire); + assert(label1 && label2 && label3 && label4); + + label = knot_wire_next_label(label4, wire); + if (!knot_dname_is_equal(label, IPV4_ARPA_DNAME)) { + return KNOT_EINVAL; + } + + // 255.255.255.255 + wire_ctx_t ctx = wire_ctx_init((uint8_t *)addr_str, + IPV4_ADDR_LABELS * 3 + 3 + 1); + wire_ctx_write(&ctx, label4 + 1, label4[0]); + wire_ctx_write_u8(&ctx, '.'); + wire_ctx_write(&ctx, label3 + 1, label3[0]); + wire_ctx_write_u8(&ctx, '.'); + wire_ctx_write(&ctx, label2 + 1, label2[0]); + wire_ctx_write_u8(&ctx, '.'); + wire_ctx_write(&ctx, label1 + 1, label1[0]); + wire_ctx_write_u8(&ctx, '\0'); + if (ctx.error != KNOT_EOK) { + return ctx.error; + } + + return KNOT_EOK; + case IPV6_ADDR_LABELS + ARPA_ZONE_LABELS: + *addr_family = AF_INET6; + + // 1-char labels + separators. + const int addr_len = IPV6_ADDR_LABELS + 7; + for (int i = 1; i <= addr_len; i++) { + if (i % 5 == 0) { + addr_str[addr_len - i] = ':'; + } else if (label[0] == 1) { + addr_str[addr_len - i] = label[1]; + label = knot_wire_next_label(label, wire); + assert(label); + } else { + return KNOT_EINVAL; + } + } + addr_str[addr_len] = '\0'; + + if (!knot_dname_is_equal(label, IPV6_ARPA_DNAME)) { + return KNOT_EINVAL; + } + + return KNOT_EOK; + default: + return KNOT_EINVAL; + } +} + +static int forward_addr_parse(knotd_qdata_t *qdata, const synth_template_t *tpl, + char *addr_str, int *addr_family) +{ + const knot_dname_t *label = qdata->name; + + // Check for prefix mismatch. + if (label[0] <= tpl->prefix_len || + memcmp(label + 1, tpl->prefix, tpl->prefix_len) != 0) { + return KNOT_EINVAL; + } + + // Copy address part. + int addr_len = label[0] - tpl->prefix_len; + memcpy(addr_str, label + 1 + tpl->prefix_len, addr_len); + addr_str[addr_len] = '\0'; + + // Determine query family: v6 if *-ABCD.zone. + const char *last_octet = addr_str + addr_len; + while (last_octet > addr_str && is_xdigit(*--last_octet)); + *addr_family = (last_octet + 5 == addr_str + addr_len ? AF_INET6 : AF_INET); + + // Restore correct address format. + const char sep = str_separator(*addr_family); + str_subst(addr_str, addr_len, '-', sep); + + return KNOT_EOK; +} + +static int addr_parse(knotd_qdata_t *qdata, const synth_template_t *tpl, char *addr_str, + int *addr_family) +{ + switch (tpl->type) { + case SYNTH_REVERSE: return reverse_addr_parse(qdata, addr_str, addr_family); + case SYNTH_FORWARD: return forward_addr_parse(qdata, tpl, addr_str, addr_family); + default: return KNOT_EINVAL; + } +} + +static knot_dname_t *synth_ptrname(uint8_t *out, const char *addr_str, + const synth_template_t *tpl, int addr_family) +{ + char ptrname[KNOT_DNAME_TXT_MAXLEN]; + int addr_len = strlen(addr_str); + const char sep = str_separator(addr_family); + + // PTR right-hand value is [prefix][address][zone] + wire_ctx_t ctx = wire_ctx_init((uint8_t *)ptrname, sizeof(ptrname)); + wire_ctx_write(&ctx, tpl->prefix, tpl->prefix_len); + wire_ctx_write(&ctx, addr_str, addr_len); + wire_ctx_write_u8(&ctx, '.'); + wire_ctx_write(&ctx, tpl->zone, tpl->zone_len); + wire_ctx_write_u8(&ctx, '\0'); + if (ctx.error != KNOT_EOK) { + return NULL; + } + + // Substitute address separator by '-'. + str_subst(ptrname + tpl->prefix_len, addr_len, sep, '-'); + + // Convert to domain name. + return knot_dname_from_str(out, ptrname, KNOT_DNAME_MAXLEN); +} + +static int reverse_rr(char *addr_str, const synth_template_t *tpl, knot_pkt_t *pkt, + knot_rrset_t *rr, int addr_family) +{ + // Synthetize PTR record data. + uint8_t ptrname[KNOT_DNAME_MAXLEN]; + if (synth_ptrname(ptrname, addr_str, tpl, addr_family) == NULL) { + return KNOT_EINVAL; + } + + rr->type = KNOT_RRTYPE_PTR; + knot_rrset_add_rdata(rr, ptrname, knot_dname_size(ptrname), &pkt->mm); + + return KNOT_EOK; +} + +static int forward_rr(char *addr_str, const synth_template_t *tpl, knot_pkt_t *pkt, + knot_rrset_t *rr, int addr_family) +{ + struct sockaddr_storage query_addr; + sockaddr_set(&query_addr, addr_family, addr_str, 0); + + // Specify address type and data. + if (addr_family == AF_INET6) { + rr->type = KNOT_RRTYPE_AAAA; + const struct sockaddr_in6* ip = (const struct sockaddr_in6*)&query_addr; + knot_rrset_add_rdata(rr, (const uint8_t *)&ip->sin6_addr, + sizeof(struct in6_addr), &pkt->mm); + } else if (addr_family == AF_INET) { + rr->type = KNOT_RRTYPE_A; + const struct sockaddr_in* ip = (const struct sockaddr_in*)&query_addr; + knot_rrset_add_rdata(rr, (const uint8_t *)&ip->sin_addr, + sizeof(struct in_addr), &pkt->mm); + } else { + return KNOT_EINVAL; + } + + return KNOT_EOK; +} + +static knot_rrset_t *synth_rr(char *addr_str, const synth_template_t *tpl, knot_pkt_t *pkt, + knotd_qdata_t *qdata, int addr_family) +{ + knot_rrset_t *rr = knot_rrset_new(qdata->name, 0, KNOT_CLASS_IN, tpl->ttl, + &pkt->mm); + if (rr == NULL) { + return NULL; + } + + // Fill in the specific data. + int ret = KNOT_ERROR; + switch (tpl->type) { + case SYNTH_REVERSE: ret = reverse_rr(addr_str, tpl, pkt, rr, addr_family); break; + case SYNTH_FORWARD: ret = forward_rr(addr_str, tpl, pkt, rr, addr_family); break; + default: break; + } + + if (ret != KNOT_EOK) { + knot_rrset_free(rr, &pkt->mm); + return NULL; + } + + return rr; +} + +/*! \brief Check if query fits the template requirements. */ +static knotd_in_state_t template_match(knotd_in_state_t state, const synth_template_t *tpl, + knot_pkt_t *pkt, knotd_qdata_t *qdata) +{ + int provided_af = AF_UNSPEC; + struct sockaddr_storage query_addr; + char addr_str[SOCKADDR_STRLEN]; + assert(SOCKADDR_STRLEN > KNOT_DNAME_MAXLABELLEN); + + // Parse address from query name. + if (addr_parse(qdata, tpl, addr_str, &provided_af) != KNOT_EOK || + sockaddr_set(&query_addr, provided_af, addr_str, 0) != KNOT_EOK) { + return state; + } + + // Try all available addresses. + int i; + for (i = 0; i < tpl->addr_count; i++) { + if (tpl->addr[i].addr_max.ss_family == AF_UNSPEC) { + if (sockaddr_net_match((struct sockaddr *)&query_addr, + (struct sockaddr *)&tpl->addr[i].addr, + tpl->addr[i].addr_mask)) { + break; + } + } else { + if (sockaddr_range_match((struct sockaddr *)&query_addr, + (struct sockaddr *)&tpl->addr[i].addr, + (struct sockaddr *)&tpl->addr[i].addr_max)) { + break; + } + } + } + if (i >= tpl->addr_count) { + return state; + } + + // Check if the request is for an available query type. + uint16_t qtype = knot_pkt_qtype(qdata->query); + switch (tpl->type) { + case SYNTH_FORWARD: + if (!query_satisfied_by_family(qtype, provided_af)) { + qdata->rcode = KNOT_RCODE_NOERROR; + return KNOTD_IN_STATE_NODATA; + } + break; + case SYNTH_REVERSE: + if (qtype != KNOT_RRTYPE_PTR && qtype != KNOT_RRTYPE_ANY) { + qdata->rcode = KNOT_RCODE_NOERROR; + return KNOTD_IN_STATE_NODATA; + } + break; + default: + return state; + } + + // Synthetise record from template. + knot_rrset_t *rr = synth_rr(addr_str, tpl, pkt, qdata, provided_af); + if (rr == NULL) { + qdata->rcode = KNOT_RCODE_SERVFAIL; + return KNOTD_IN_STATE_ERROR; + } + + // Insert synthetic response into packet. + if (knot_pkt_put(pkt, 0, rr, KNOT_PF_FREE) != KNOT_EOK) { + return KNOTD_IN_STATE_ERROR; + } + + // Authoritative response. + knot_wire_set_aa(pkt->wire); + + return KNOTD_IN_STATE_HIT; +} + +static knotd_in_state_t solve_synth_record(knotd_in_state_t state, knot_pkt_t *pkt, + knotd_qdata_t *qdata, knotd_mod_t *mod) +{ + assert(pkt && qdata && mod); + + // Applicable when search in zone fails. + if (state != KNOTD_IN_STATE_MISS) { + return state; + } + + // Check if template fits. + return template_match(state, knotd_mod_ctx(mod), pkt, qdata); +} + +int synth_record_load(knotd_mod_t *mod) +{ + // Create synthesis template. + synth_template_t *tpl = calloc(1, sizeof(*tpl)); + if (tpl == NULL) { + return KNOT_ENOMEM; + } + + // Set type. + knotd_conf_t conf = knotd_conf_mod(mod, MOD_TYPE); + tpl->type = conf.single.option; + + /* Set prefix. */ + conf = knotd_conf_mod(mod, MOD_PREFIX); + tpl->prefix = strdup(conf.single.string); + tpl->prefix_len = strlen(tpl->prefix); + + // Set origin if generating reverse record. + if (tpl->type == SYNTH_REVERSE) { + conf = knotd_conf_mod(mod, MOD_ORIGIN); + tpl->zone = knot_dname_to_str_alloc(conf.single.dname); + if (tpl->zone == NULL) { + free(tpl->prefix); + free(tpl); + return KNOT_ENOMEM; + } + tpl->zone_len = strlen(tpl->zone); + } + + // Set ttl. + conf = knotd_conf_mod(mod, MOD_TTL); + tpl->ttl = conf.single.integer; + + // Set address. + conf = knotd_conf_mod(mod, MOD_NET); + tpl->addr_count = conf.count; + tpl->addr = calloc(conf.count, sizeof(*tpl->addr)); + if (tpl->addr == NULL) { + knotd_conf_free(&conf); + free(tpl->zone); + free(tpl->prefix); + free(tpl); + return KNOT_ENOMEM; + } + for (size_t i = 0; i < conf.count; i++) { + tpl->addr[i].addr = conf.multi[i].addr; + tpl->addr[i].addr_max = conf.multi[i].addr_max; + tpl->addr[i].addr_mask = conf.multi[i].addr_mask; + } + knotd_conf_free(&conf); + + knotd_mod_ctx_set(mod, tpl); + + return knotd_mod_in_hook(mod, KNOTD_STAGE_ANSWER, solve_synth_record); +} + +void synth_record_unload(knotd_mod_t *mod) +{ + synth_template_t *tpl = knotd_mod_ctx(mod); + + free(tpl->addr); + free(tpl->zone); + free(tpl->prefix); + free(tpl); +} + +KNOTD_MOD_API(synthrecord, KNOTD_MOD_FLAG_SCOPE_ZONE, + synth_record_load, synth_record_unload, synth_record_conf, + synth_record_conf_check); diff --git a/src/knot/modules/synthrecord/synthrecord.rst b/src/knot/modules/synthrecord/synthrecord.rst new file mode 100644 index 0000000..eddac2f --- /dev/null +++ b/src/knot/modules/synthrecord/synthrecord.rst @@ -0,0 +1,161 @@ +.. _mod-synthrecord: + +``synthrecord`` – Automatic forward/reverse records +=================================================== + +This module is able to synthesize either forward or reverse records for +a given prefix and subnet. + +Records are synthesized only if the query can't be satisfied from the zone. +Both IPv4 and IPv6 are supported. + +Example +------- + +Automatic forward records +......................... + +:: + + mod-synthrecord: + - id: test1 + type: forward + prefix: dynamic- + ttl: 400 + network: 2620:0:b61::/52 + + zone: + - domain: test. + file: test.zone # Must exist + module: mod-synthrecord/test1 + +Result: + +.. code-block:: console + + $ kdig AAAA dynamic-2620-0000-0b61-0100-0000-0000-0000-0001.test. + ... + ;; QUESTION SECTION: + ;; dynamic-2620-0000-0b61-0100-0000-0000-0000-0001.test. IN AAAA + + ;; ANSWER SECTION: + dynamic-2620-0000-0b61-0100-0000-0000-0000-0001.test. 400 IN AAAA 2620:0:b61:100::1 + +You can also have CNAME aliases to the dynamic records, which are going to be +further resolved: + +.. code-block:: console + + $ kdig AAAA alias.test. + ... + ;; QUESTION SECTION: + ;; alias.test. IN AAAA + + ;; ANSWER SECTION: + alias.test. 3600 IN CNAME dynamic-2620-0000-0b61-0100-0000-0000-0000-0002.test. + dynamic-2620-0000-0b61-0100-0000-0000-0000-0002.test. 400 IN AAAA 2620:0:b61:100::2 + +Automatic reverse records +......................... + +:: + + mod-synthrecord: + - id: test2 + type: reverse + prefix: dynamic- + origin: test + ttl: 400 + network: 2620:0:b61::/52 + + zone: + - domain: 1.6.b.0.0.0.0.0.0.2.6.2.ip6.arpa. + file: 1.6.b.0.0.0.0.0.0.2.6.2.ip6.arpa.zone # Must exist + module: mod-synthrecord/test2 + +Result: + +.. code-block:: console + + $ kdig -x 2620:0:b61::1 + ... + ;; QUESTION SECTION: + ;; 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.1.6.b.0.0.0.0.0.0.2.6.2.ip6.arpa. IN PTR + + ;; ANSWER SECTION: + 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.1.6.b.0.0.0.0.0.0.2.6.2.ip6.arpa. 400 IN PTR + dynamic-2620-0000-0b61-0000-0000-0000-0000-0001.test. + +Module reference +---------------- + +:: + + mod-synthrecord: + - id: STR + type: forward | reverse + prefix: STR + origin: DNAME + ttl: INT + network: ADDR[/INT] | ADDR-ADDR ... + +.. _mod-synthrecord_id: + +id +.. + +A module identifier. + +.. _mod-synthrecord_type: + +type +.... + +The type of generated records. + +Possible values: + +- ``forward`` – Forward records +- ``reverse`` – Reverse records + +*Required* + +.. _mod-synthrecord_prefix: + +prefix +...... + +A record owner prefix. + +.. NOTE:: + The value doesn’t allow dots, address parts in the synthetic names are + separated with a dash. + +*Default:* empty + +.. _mod-synthrecord_origin: + +origin +...... + +A zone origin (only valid for the :ref:`reverse type<mod-synthrecord_type>`). + +*Required* + +.. _mod-synthrecord_ttl: + +ttl +... + +Time to live of the generated records. + +*Default:* 3600 + +.. _mod-synthrecord_network: + +network +....... + +An IP address, a network subnet, or a network range the query must match. + +*Required* diff --git a/src/knot/modules/whoami/Makefile.inc b/src/knot/modules/whoami/Makefile.inc new file mode 100644 index 0000000..4d20fcb --- /dev/null +++ b/src/knot/modules/whoami/Makefile.inc @@ -0,0 +1,12 @@ +knot_modules_whoami_la_SOURCES = knot/modules/whoami/whoami.c +EXTRA_DIST += knot/modules/whoami/whoami.rst + +if STATIC_MODULE_whoami +libknotd_la_SOURCES += $(knot_modules_whoami_la_SOURCES) +endif + +if SHARED_MODULE_whoami +knot_modules_whoami_la_LDFLAGS = $(KNOTD_MOD_LDFLAGS) +knot_modules_whoami_la_CPPFLAGS = $(KNOTD_MOD_CPPFLAGS) +pkglib_LTLIBRARIES += knot/modules/whoami.la +endif diff --git a/src/knot/modules/whoami/whoami.c b/src/knot/modules/whoami/whoami.c new file mode 100644 index 0000000..ea80fc9 --- /dev/null +++ b/src/knot/modules/whoami/whoami.c @@ -0,0 +1,114 @@ +/* Copyright (C) 2017 Fastly, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <netinet/in.h> + +#include "knot/include/module.h" + +static knotd_in_state_t whoami_query(knotd_in_state_t state, knot_pkt_t *pkt, + knotd_qdata_t *qdata, knotd_mod_t *mod) +{ + assert(pkt && qdata); + + const knot_dname_t *zone_name = knotd_qdata_zone_name(qdata); + if (zone_name == NULL) { + return KNOTD_IN_STATE_ERROR; + } + + /* Retrieve the query tuple. */ + const knot_dname_t *qname = knot_pkt_qname(qdata->query); + const uint16_t qtype = knot_pkt_qtype(qdata->query); + const uint16_t qclass = knot_pkt_qclass(qdata->query); + + /* We only generate A and AAAA records, which are Internet class. */ + if (qclass != KNOT_CLASS_IN) { + return state; + } + + /* Only handle queries with qname set to the zone name. */ + if (!knot_dname_is_equal(qname, zone_name)) { + return state; + } + + /* Only handle A and AAAA queries. */ + if (qtype != KNOT_RRTYPE_A && qtype != KNOT_RRTYPE_AAAA) { + return state; + } + + /* Retrieve the IP address that sent the query. */ + const struct sockaddr_storage *query_source = qdata->params->remote; + if (query_source == NULL) { + return KNOTD_IN_STATE_ERROR; + } + + /* If the socket address family corresponds to the query type (i.e., + * AF_INET <-> A and AF_INET6 <-> AAAA), put the socket address and + * length into 'rdata' and 'len_rdata'. + */ + const void *rdata = NULL; + uint16_t len_rdata = 0; + if (query_source->ss_family == AF_INET && qtype == KNOT_RRTYPE_A) { + const struct sockaddr_in *sai = (struct sockaddr_in *)query_source; + rdata = &sai->sin_addr.s_addr; + len_rdata = sizeof(sai->sin_addr.s_addr); + } else if (query_source->ss_family == AF_INET6 && qtype == KNOT_RRTYPE_AAAA) { + const struct sockaddr_in6 *sai6 = (struct sockaddr_in6 *)query_source; + rdata = &sai6->sin6_addr; + len_rdata = sizeof(sai6->sin6_addr); + } else { + /* Query type didn't match address family. */ + return state; + } + + /* Synthesize the response RRset. */ + + /* TTL is taken from the TTL of the SOA record. */ + knot_rrset_t soa = knotd_qdata_zone_apex_rrset(qdata, KNOT_RRTYPE_SOA); + + /* Owner name, type, and class are taken from the question. */ + knot_rrset_t *rrset = knot_rrset_new(qname, qtype, qclass, soa.ttl, &pkt->mm); + if (rrset == NULL) { + return KNOTD_IN_STATE_ERROR; + } + + /* Record data is the query source address. */ + int ret = knot_rrset_add_rdata(rrset, rdata, len_rdata, &pkt->mm); + if (ret != KNOT_EOK) { + knot_rrset_free(rrset, &pkt->mm); + return KNOTD_IN_STATE_ERROR; + } + + /* Add the new RRset to the response packet. */ + ret = knot_pkt_put(pkt, KNOT_COMPR_HINT_QNAME, rrset, KNOT_PF_FREE); + if (ret != KNOT_EOK) { + knot_rrset_free(rrset, &pkt->mm); + return KNOTD_IN_STATE_ERROR; + } + + /* Success. */ + return KNOTD_IN_STATE_HIT; +} + +int whoami_load(knotd_mod_t *mod) +{ + /* Hook to the query plan. */ + knotd_mod_in_hook(mod, KNOTD_STAGE_ANSWER, whoami_query); + + return KNOT_EOK; +} + +KNOTD_MOD_API(whoami, KNOTD_MOD_FLAG_SCOPE_ZONE | KNOTD_MOD_FLAG_OPT_CONF, + whoami_load, NULL, NULL, NULL); diff --git a/src/knot/modules/whoami/whoami.rst b/src/knot/modules/whoami/whoami.rst new file mode 100644 index 0000000..25d0174 --- /dev/null +++ b/src/knot/modules/whoami/whoami.rst @@ -0,0 +1,97 @@ +.. _mod-whoami: + +``whoami`` — Whoami response +============================ + +The module synthesizes an A or AAAA record containing the query source IP address, +at the apex of the zone being served. It makes sure to allow Knot DNS to generate +cacheable negative responses, and to allow fallback to extra records defined in the +underlying zone file. The TTL of the synthesized record is copied from +the TTL of the SOA record in the zone file. + +Because a DNS query for type A or AAAA has nothing to do with whether +the query occurs over IPv4 or IPv6, this module requires a special +zone configuration to support both address families. For A queries, the +underlying zone must have a set of nameservers that only have IPv4 +addresses, and for AAAA queries, the underlying zone must have a set of +nameservers that only have IPv6 addresses. + +Example +------- + +To enable this module, you need to add something like the following to +the Knot DNS configuration file:: + + zone: + - domain: whoami.domain.example + file: "/path/to/whoami.domain.example" + module: mod-whoami + + zone: + - domain: whoami6.domain.example + file: "/path/to/whoami6.domain.example" + module: mod-whoami + +The whoami.domain.example zone file example: + + .. code-block:: none + + $TTL 1 + + @ SOA ( + whoami.domain.example. ; MNAME + hostmaster.domain.example. ; RNAME + 2016051300 ; SERIAL + 86400 ; REFRESH + 86400 ; RETRY + 86400 ; EXPIRE + 1 ; MINIMUM + ) + + $TTL 86400 + + @ NS ns1.whoami.domain.example. + @ NS ns2.whoami.domain.example. + @ NS ns3.whoami.domain.example. + @ NS ns4.whoami.domain.example. + + ns1 A 198.51.100.53 + ns2 A 192.0.2.53 + ns3 A 203.0.113.53 + ns4 A 198.19.123.53 + +The whoami6.domain.example zone file example: + + .. code-block:: none + + $TTL 1 + + @ SOA ( + whoami6.domain.example. ; MNAME + hostmaster.domain.example. ; RNAME + 2016051300 ; SERIAL + 86400 ; REFRESH + 86400 ; RETRY + 86400 ; EXPIRE + 1 ; MINIMUM + ) + + $TTL 86400 + + @ NS ns1.whoami6.domain.example. + @ NS ns2.whoami6.domain.example. + @ NS ns3.whoami6.domain.example. + @ NS ns4.whoami6.domain.example. + + ns1 AAAA 2001:db8:100::53 + ns2 AAAA 2001:db8:200::53 + ns3 AAAA 2001:db8:300::53 + ns4 AAAA 2001:db8:400::53 + +The parent domain would then delegate whoami.domain.example to +ns[1-4].whoami.domain.example and whoami6.domain.example to +ns[1-4].whoami6.domain.example, and include the corresponding A-only or +AAAA-only glue records. + +.. NOTE:: + This module is not configurable. diff --git a/src/knot/nameserver/axfr.c b/src/knot/nameserver/axfr.c new file mode 100644 index 0000000..6a60b19 --- /dev/null +++ b/src/knot/nameserver/axfr.c @@ -0,0 +1,214 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <urcu.h> + +#include "contrib/mempattern.h" +#include "contrib/sockaddr.h" +#include "knot/nameserver/axfr.h" +#include "knot/nameserver/internet.h" +#include "knot/nameserver/log.h" +#include "knot/nameserver/xfr.h" +#include "libknot/libknot.h" + +#define ZONE_NAME(qdata) knot_pkt_qname((qdata)->query) +#define REMOTE(qdata) (struct sockaddr *)(qdata)->params->remote + +#define AXFROUT_LOG(priority, qdata, fmt...) \ + ns_log(priority, ZONE_NAME(qdata), LOG_OPERATION_AXFR, \ + LOG_DIRECTION_OUT, REMOTE(qdata), fmt) + +/* AXFR context. @note aliasing the generic xfr_proc */ +struct axfr_proc { + struct xfr_proc proc; + trie_it_t *i; + unsigned cur_rrset; +}; + +static int axfr_put_rrsets(knot_pkt_t *pkt, zone_node_t *node, + struct axfr_proc *state) +{ + assert(node != NULL); + + /* Append all RRs. */ + for (unsigned i = state->cur_rrset; i < node->rrset_count; ++i) { + knot_rrset_t rrset = node_rrset_at(node, i); + if (rrset.type == KNOT_RRTYPE_SOA) { + continue; + } + + int ret = knot_pkt_put(pkt, 0, &rrset, KNOT_PF_NOTRUNC | KNOT_PF_ORIGTTL); + if (ret != KNOT_EOK) { + /* If something failed, remember the current RR for later. */ + state->cur_rrset = i; + return ret; + } + } + + state->cur_rrset = 0; + + return KNOT_EOK; +} + +static int axfr_process_node_tree(knot_pkt_t *pkt, const void *item, + struct xfr_proc *state) +{ + assert(item != NULL); + + struct axfr_proc *axfr = (struct axfr_proc*)state; + + if (axfr->i == NULL) { + axfr->i = trie_it_begin((trie_t *)item); + } + + /* Put responses. */ + int ret = KNOT_EOK; + while (!trie_it_finished(axfr->i)) { + zone_node_t *node = (zone_node_t *)*trie_it_val(axfr->i); + ret = axfr_put_rrsets(pkt, node, axfr); + if (ret != KNOT_EOK) { + break; + } + trie_it_next(axfr->i); + } + + /* Finished all nodes. */ + if (ret == KNOT_EOK) { + trie_it_free(axfr->i); + axfr->i = NULL; + } + return ret; +} + +static void axfr_query_cleanup(knotd_qdata_t *qdata) +{ + struct axfr_proc *axfr = (struct axfr_proc *)qdata->extra->ext; + + trie_it_free(axfr->i); + ptrlist_free(&axfr->proc.nodes, qdata->mm); + mm_free(qdata->mm, axfr); + + /* Allow zone changes (finished). */ + rcu_read_unlock(); +} + +static int axfr_query_check(knotd_qdata_t *qdata) +{ + NS_NEED_ZONE(qdata, KNOT_RCODE_NOTAUTH); + NS_NEED_AUTH(qdata, qdata->extra->zone->name, ACL_ACTION_TRANSFER); + NS_NEED_ZONE_CONTENTS(qdata, KNOT_RCODE_SERVFAIL); + + return KNOT_STATE_DONE; +} + +static int axfr_query_init(knotd_qdata_t *qdata) +{ + assert(qdata); + + /* Check AXFR query validity. */ + if (axfr_query_check(qdata) == KNOT_STATE_FAIL) { + if (qdata->rcode == KNOT_RCODE_FORMERR) { + return KNOT_EMALF; + } else { + return KNOT_EDENIED; + } + } + + /* Create transfer processing context. */ + knot_mm_t *mm = qdata->mm; + struct axfr_proc *axfr = mm_alloc(mm, sizeof(struct axfr_proc)); + if (axfr == NULL) { + return KNOT_ENOMEM; + } + memset(axfr, 0, sizeof(struct axfr_proc)); + init_list(&axfr->proc.nodes); + + /* Put data to process. */ + xfr_stats_begin(&axfr->proc.stats); + zone_contents_t *contents = qdata->extra->zone->contents; + /* Must be non-NULL for the first message. */ + assert(contents); + ptrlist_add(&axfr->proc.nodes, contents->nodes, mm); + /* Put NSEC3 data if exists. */ + if (!zone_tree_is_empty(contents->nsec3_nodes)) { + ptrlist_add(&axfr->proc.nodes, contents->nsec3_nodes, mm); + } + + /* Set up cleanup callback. */ + qdata->extra->ext = axfr; + qdata->extra->ext_cleanup = &axfr_query_cleanup; + + /* No zone changes during multipacket answer (unlocked in axfr_answer_cleanup) */ + rcu_read_lock(); + + return KNOT_EOK; +} + +int axfr_process_query(knot_pkt_t *pkt, knotd_qdata_t *qdata) +{ + if (pkt == NULL || qdata == NULL) { + return KNOT_STATE_FAIL; + } + + /* If AXFR is disabled, respond with NOTIMPL. */ + if (qdata->params->flags & KNOTD_QUERY_FLAG_NO_AXFR) { + qdata->rcode = KNOT_RCODE_NOTIMPL; + return KNOT_STATE_FAIL; + } + + /* Initialize on first call. */ + struct axfr_proc *axfr = qdata->extra->ext; + if (axfr == NULL) { + int ret = axfr_query_init(qdata); + axfr = qdata->extra->ext; + switch (ret) { + case KNOT_EOK: /* OK */ + AXFROUT_LOG(LOG_INFO, qdata, "started, serial %u", + zone_contents_serial(qdata->extra->zone->contents)); + break; + case KNOT_EDENIED: /* Not authorized, already logged. */ + return KNOT_STATE_FAIL; + case KNOT_EMALF: /* Malformed query. */ + AXFROUT_LOG(LOG_DEBUG, qdata, "malformed query"); + return KNOT_STATE_FAIL; + default: + AXFROUT_LOG(LOG_ERR, qdata, "failed to start (%s)", + knot_strerror(ret)); + return KNOT_STATE_FAIL; + } + } + + /* Reserve space for TSIG. */ + int ret = knot_pkt_reserve(pkt, knot_tsig_wire_size(&qdata->sign.tsig_key)); + if (ret != KNOT_EOK) { + return KNOT_STATE_FAIL; + } + + /* Answer current packet (or continue). */ + ret = xfr_process_list(pkt, &axfr_process_node_tree, qdata); + switch (ret) { + case KNOT_ESPACE: /* Couldn't write more, send packet and continue. */ + return KNOT_STATE_PRODUCE; /* Check for more. */ + case KNOT_EOK: /* Last response. */ + xfr_stats_end(&axfr->proc.stats); + xfr_log_finished(ZONE_NAME(qdata), LOG_OPERATION_AXFR, LOG_DIRECTION_OUT, + REMOTE(qdata), &axfr->proc.stats); + return KNOT_STATE_DONE; + default: /* Generic error. */ + AXFROUT_LOG(LOG_ERR, qdata, "failed (%s)", knot_strerror(ret)); + return KNOT_STATE_FAIL; + } +} diff --git a/src/knot/nameserver/axfr.h b/src/knot/nameserver/axfr.h new file mode 100644 index 0000000..1d1e4ea --- /dev/null +++ b/src/knot/nameserver/axfr.h @@ -0,0 +1,27 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "knot/nameserver/process_query.h" +#include "libknot/packet/pkt.h" + +/*! + * \brief Process an AXFR query message. + * + * \return KNOT_STATE_* processing states + */ +int axfr_process_query(knot_pkt_t *pkt, knotd_qdata_t *qdata); diff --git a/src/knot/nameserver/chaos.c b/src/knot/nameserver/chaos.c new file mode 100644 index 0000000..04b646a --- /dev/null +++ b/src/knot/nameserver/chaos.c @@ -0,0 +1,145 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <strings.h> +#include <stdlib.h> + +#include "knot/nameserver/chaos.h" +#include "knot/conf/conf.h" +#include "libknot/libknot.h" + +#define WISH "Knot DNS developers wish you " +#define HOPE "Knot DNS developers hope you " + +static const char *wishes[] = { + HOPE "have all your important life questions aswered without SERVFAIL.", + WISH "many wonderful people in your domain.", + WISH "non-empty lymph nodes.", + HOPE "resolve the . of your problems.", + WISH "long enough TTL.", + HOPE "become authoritative master in your domain.", + HOPE "always find useful PTR in CHAOS.", + "Canonical name is known to both DNS experts and Ubuntu users.", + HOPE "never forget both your name and address.", + "Don't fix broken CNAME chains with glue!", + WISH "no Additional section in your TODO list.", + HOPE "won't find surprising news in today's journal.", + HOPE "perform rollover often just when playing roulette.", + HOPE "get notified before your domain registration expires.", +}; + +#undef WISH +#undef HOPE + +static const char *get_txt_response_string(knot_pkt_t *response) +{ + char qname[32]; + if (knot_dname_to_str(qname, knot_pkt_qname(response), sizeof(qname)) == NULL) { + return NULL; + } + + const char *response_str = NULL; + + /* Allow hostname.bind. for compatibility. */ + if (strcasecmp("id.server.", qname) == 0 || + strcasecmp("hostname.bind.", qname) == 0) { + conf_val_t val = conf_get(conf(), C_SRV, C_IDENT); + if (val.code == KNOT_EOK) { + response_str = conf_str(&val); // Can be NULL! + } else { + response_str = conf()->hostname; + } + /* Allow version.bind. for compatibility. */ + } else if (strcasecmp("version.server.", qname) == 0 || + strcasecmp("version.bind.", qname) == 0) { + conf_val_t val = conf_get(conf(), C_SRV, C_VERSION); + if (val.code == KNOT_EOK) { + response_str = conf_str(&val); // Can be NULL! + } else { + response_str = "Knot DNS " PACKAGE_VERSION; + } + } else if (strcasecmp("fortune.", qname) == 0) { + conf_val_t val = conf_get(conf(), C_SRV, C_VERSION); + if (val.code != KNOT_EOK) { + uint16_t wishno = knot_wire_get_id(response->wire) % + (sizeof(wishes) / sizeof(wishes[0])); + response_str = wishes[wishno]; + } + } + + return response_str; +} + +static int create_txt_rrset(knot_rrset_t *rrset, const knot_dname_t *owner, + const char *response_str, knot_mm_t *mm) +{ + /* Truncate response to one TXT label. */ + size_t response_len = strlen(response_str); + if (response_len > UINT8_MAX) { + response_len = UINT8_MAX; + } + + knot_dname_t *rowner = knot_dname_copy(owner, mm); + if (rowner == NULL) { + return KNOT_ENOMEM; + } + + knot_rrset_init(rrset, rowner, KNOT_RRTYPE_TXT, KNOT_CLASS_CH, 0); + uint8_t rdata[response_len + 1]; + + rdata[0] = response_len; + memcpy(&rdata[1], response_str, response_len); + + int ret = knot_rrset_add_rdata(rrset, rdata, response_len + 1, mm); + if (ret != KNOT_EOK) { + knot_dname_free(rrset->owner, mm); + return ret; + } + + return KNOT_EOK; +} + +static int answer_txt(knot_pkt_t *response) +{ + const char *response_str = get_txt_response_string(response); + if (response_str == NULL || response_str[0] == '\0') { + return KNOT_RCODE_REFUSED; + } + + knot_rrset_t rrset; + int ret = create_txt_rrset(&rrset, knot_pkt_qname(response), + response_str, &response->mm); + if (ret != KNOT_EOK) { + return KNOT_RCODE_SERVFAIL; + } + + int result = knot_pkt_put(response, 0, &rrset, KNOT_PF_FREE); + if (result != KNOT_EOK) { + knot_rrset_clear(&rrset, &response->mm); + return KNOT_RCODE_SERVFAIL; + } + + return KNOT_RCODE_NOERROR; +} + +int knot_chaos_answer(knot_pkt_t *pkt) +{ + if (knot_pkt_qtype(pkt) != KNOT_RRTYPE_TXT) { + return KNOT_RCODE_REFUSED; + } + + return answer_txt(pkt); +} diff --git a/src/knot/nameserver/chaos.h b/src/knot/nameserver/chaos.h new file mode 100644 index 0000000..14923d1 --- /dev/null +++ b/src/knot/nameserver/chaos.h @@ -0,0 +1,34 @@ +/* Copyright (C) 2015 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \file + * + * \brief Chaos class processing. + * + * \addtogroup query_processing + * @{ + */ + +#pragma once + +#include "libknot/packet/pkt.h" + +/*! + * \brief Create a response for a given query in the CHAOS class. + */ +int knot_chaos_answer(knot_pkt_t *pkt); + +/*! @} */ diff --git a/src/knot/nameserver/internet.c b/src/knot/nameserver/internet.c new file mode 100644 index 0000000..62830e7 --- /dev/null +++ b/src/knot/nameserver/internet.c @@ -0,0 +1,664 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "libknot/libknot.h" +#include "knot/nameserver/internet.h" +#include "knot/nameserver/nsec_proofs.h" +#include "knot/nameserver/query_module.h" +#include "knot/zone/serial.h" +#include "contrib/mempattern.h" + +/*! \brief Check if given node was already visited. */ +static int wildcard_has_visited(knotd_qdata_t *qdata, const zone_node_t *node) +{ + struct wildcard_hit *item = NULL; + WALK_LIST(item, qdata->extra->wildcards) { + if (item->node == node) { + return true; + } + } + return false; +} + +/*! \brief Mark given node as visited. */ +static int wildcard_visit(knotd_qdata_t *qdata, const zone_node_t *node, + const zone_node_t *prev, const knot_dname_t *sname) +{ + assert(qdata); + assert(node); + + /* Already in the list. */ + if (wildcard_has_visited(qdata, node)) { + return KNOT_EOK; + } + + knot_mm_t *mm = qdata->mm; + struct wildcard_hit *item = mm_alloc(mm, sizeof(struct wildcard_hit)); + item->node = node; + item->prev = prev; + item->sname = sname; + add_tail(&qdata->extra->wildcards, (node_t *)item); + return KNOT_EOK; +} + +/*! \brief Synthetizes a CNAME RR from a DNAME. */ +static int dname_cname_synth(const knot_rrset_t *dname_rr, + const knot_dname_t *qname, + knot_rrset_t *cname_rrset, + knot_mm_t *mm) +{ + if (cname_rrset == NULL) { + return KNOT_EINVAL; + } + knot_dname_t *owner_copy = knot_dname_copy(qname, mm); + if (owner_copy == NULL) { + return KNOT_ENOMEM; + } + knot_rrset_init(cname_rrset, owner_copy, KNOT_RRTYPE_CNAME, dname_rr->rclass, + dname_rr->ttl); + + /* Replace last labels of qname with DNAME. */ + const knot_dname_t *dname_wire = dname_rr->owner; + const knot_dname_t *dname_tgt = knot_dname_target(dname_rr->rrs.rdata); + size_t labels = knot_dname_labels(dname_wire, NULL); + knot_dname_t *cname = knot_dname_replace_suffix(qname, labels, dname_tgt, mm); + if (cname == NULL) { + knot_dname_free(owner_copy, mm); + return KNOT_ENOMEM; + } + + /* Store DNAME into RDATA. */ + size_t cname_size = knot_dname_size(cname); + uint8_t cname_rdata[cname_size]; + memcpy(cname_rdata, cname, cname_size); + knot_dname_free(cname, mm); + + int ret = knot_rrset_add_rdata(cname_rrset, cname_rdata, cname_size, mm); + if (ret != KNOT_EOK) { + knot_dname_free(owner_copy, mm); + return ret; + } + + return KNOT_EOK; +} + +/*! + * \brief Checks if the name created by replacing the owner of \a dname_rrset + * in the \a qname by the DNAME's target would be longer than allowed. + */ +static bool dname_cname_cannot_synth(const knot_rrset_t *rrset, const knot_dname_t *qname) +{ + if (knot_dname_labels(qname, NULL) - knot_dname_labels(rrset->owner, NULL) + + knot_dname_labels(knot_dname_target(rrset->rrs.rdata), NULL) > KNOT_DNAME_MAXLABELS) { + return true; + } else if (knot_dname_size(qname) - knot_dname_size(rrset->owner) + + knot_dname_size(knot_dname_target(rrset->rrs.rdata)) > KNOT_DNAME_MAXLEN) { + return true; + } else { + return false; + } +} + +/*! \brief DNSSEC both requested & available. */ +static bool have_dnssec(knotd_qdata_t *qdata) +{ + return knot_pkt_has_dnssec(qdata->query) && + qdata->extra->zone->contents->dnssec; +} + +/*! \brief This is a wildcard-covered or any other terminal node for QNAME. + * e.g. positive answer. + */ +static int put_answer(knot_pkt_t *pkt, uint16_t type, knotd_qdata_t *qdata) +{ + knot_rrset_t rrset; + knot_rrset_init_empty(&rrset); + + /* Wildcard expansion or exact match, either way RRSet owner is + * is QNAME. We can fake name synthesis by setting compression hint to + * QNAME position. Just need to check if we're answering QNAME and not + * a CNAME target. + */ + uint16_t compr_hint = KNOT_COMPR_HINT_NONE; + if (pkt->rrset_count == 0) { /* Guaranteed first answer. */ + compr_hint = KNOT_COMPR_HINT_QNAME; + } + + unsigned put_rr_flags = (qdata->params->flags & KNOTD_QUERY_FLAG_LIMIT_SIZE) ? + KNOT_PF_NULL : KNOT_PF_NOTRUNC; + put_rr_flags |= KNOT_PF_ORIGTTL; + + int ret = KNOT_EOK; + switch (type) { + case KNOT_RRTYPE_ANY: /* Append all RRSets. */ { + conf_val_t val = conf_zone_get(conf(), C_DISABLE_ANY, + qdata->extra->zone->name); + /* If ANY not allowed, set TC bit. */ + if ((qdata->params->flags & KNOTD_QUERY_FLAG_LIMIT_ANY) && + conf_bool(&val)) { + knot_wire_set_tc(pkt->wire); + return KNOT_ESPACE; + } + for (unsigned i = 0; i < qdata->extra->node->rrset_count; ++i) { + rrset = node_rrset_at(qdata->extra->node, i); + ret = process_query_put_rr(pkt, qdata, &rrset, NULL, + compr_hint, put_rr_flags); + if (ret != KNOT_EOK) { + break; + } + } + break; + } + default: /* Single RRSet of given type. */ + rrset = node_rrset(qdata->extra->node, type); + if (!knot_rrset_empty(&rrset)) { + knot_rrset_t rrsigs = node_rrset(qdata->extra->node, KNOT_RRTYPE_RRSIG); + ret = process_query_put_rr(pkt, qdata, &rrset, &rrsigs, + compr_hint, put_rr_flags); + } + break; + } + + return ret; +} + +/*! \brief Puts optional SOA RRSet to the Authority section of the response. */ +static int put_authority_soa(knot_pkt_t *pkt, knotd_qdata_t *qdata, + const zone_contents_t *zone) +{ + knot_rrset_t soa = node_rrset(zone->apex, KNOT_RRTYPE_SOA); + knot_rrset_t rrsigs = node_rrset(zone->apex, KNOT_RRTYPE_RRSIG); + return process_query_put_rr(pkt, qdata, &soa, &rrsigs, + KNOT_COMPR_HINT_NONE, KNOT_PF_NOTRUNC); +} + +/*! \brief Put the delegation NS RRSet to the Authority section. */ +static int put_delegation(knot_pkt_t *pkt, knotd_qdata_t *qdata) +{ + /* Find closest delegation point. */ + while (!(qdata->extra->node->flags & NODE_FLAGS_DELEG)) { + qdata->extra->node = qdata->extra->node->parent; + } + + /* Insert NS record. */ + knot_rrset_t rrset = node_rrset(qdata->extra->node, KNOT_RRTYPE_NS); + knot_rrset_t rrsigs = node_rrset(qdata->extra->node, KNOT_RRTYPE_RRSIG); + return process_query_put_rr(pkt, qdata, &rrset, &rrsigs, + KNOT_COMPR_HINT_NONE, 0); +} + +/*! \brief Put additional records for given RR. */ +static int put_additional(knot_pkt_t *pkt, const knot_rrset_t *rr, + knotd_qdata_t *qdata, knot_rrinfo_t *info, int state) +{ + if (rr->additional == NULL) { + return KNOT_EOK; + } + + /* Valid types for ADDITIONALS insertion. */ + /* \note Not resolving CNAMEs as MX/NS name must not be an alias. (RFC2181/10.3) */ + static const uint16_t ar_type_list[] = { KNOT_RRTYPE_A, KNOT_RRTYPE_AAAA }; + static const int ar_type_count = 2; + + int ret = KNOT_EOK; + + additional_t *additional = (additional_t *)rr->additional; + + /* Iterate over the additionals. */ + for (uint16_t i = 0; i < additional->count; i++) { + glue_t *glue = &additional->glues[i]; + uint32_t flags = KNOT_PF_NULL; + + /* Optional glue doesn't cause truncation. (RFC 1034/4.3.2 step 3b). */ + if (state != KNOTD_IN_STATE_DELEG || glue->optional) { + flags |= KNOT_PF_NOTRUNC; + } + + uint16_t hint = knot_compr_hint(info, KNOT_COMPR_HINT_RDATA + + glue->ns_pos); + knot_rrset_t rrsigs = node_rrset(glue->node, KNOT_RRTYPE_RRSIG); + for (int k = 0; k < ar_type_count; ++k) { + knot_rrset_t rrset = node_rrset(glue->node, ar_type_list[k]); + if (knot_rrset_empty(&rrset)) { + continue; + } + ret = process_query_put_rr(pkt, qdata, &rrset, &rrsigs, + hint, flags); + if (ret != KNOT_EOK) { + break; + } + } + } + + return ret; +} + +static int follow_cname(knot_pkt_t *pkt, uint16_t rrtype, knotd_qdata_t *qdata) +{ + const zone_node_t *cname_node = qdata->extra->node; + knot_rrset_t cname_rr = node_rrset(qdata->extra->node, rrtype); + knot_rrset_t rrsigs = node_rrset(qdata->extra->node, KNOT_RRTYPE_RRSIG); + + assert(!knot_rrset_empty(&cname_rr)); + + /* Check whether RR is already in the packet. */ + uint16_t flags = KNOT_PF_CHECKDUP; + + /* Now, try to put CNAME to answer. */ + uint16_t rr_count_before = pkt->rrset_count; + int ret = process_query_put_rr(pkt, qdata, &cname_rr, &rrsigs, 0, flags); + switch (ret) { + case KNOT_EOK: break; + case KNOT_ESPACE: return KNOTD_IN_STATE_TRUNC; + default: return KNOTD_IN_STATE_ERROR; + } + + /* Check if RR count increased. */ + if (pkt->rrset_count <= rr_count_before) { + qdata->extra->node = NULL; /* Act is if the name leads to nowhere. */ + return KNOTD_IN_STATE_HIT; + } + + /* Synthesize CNAME if followed DNAME. */ + if (rrtype == KNOT_RRTYPE_DNAME) { + if (dname_cname_cannot_synth(&cname_rr, qdata->name)) { + qdata->rcode = KNOT_RCODE_YXDOMAIN; + } else { + knot_rrset_t dname_rr = cname_rr; + int ret = dname_cname_synth(&dname_rr, qdata->name, + &cname_rr, &pkt->mm); + if (ret != KNOT_EOK) { + qdata->rcode = KNOT_RCODE_SERVFAIL; + return KNOTD_IN_STATE_ERROR; + } + ret = process_query_put_rr(pkt, qdata, &cname_rr, NULL, 0, KNOT_PF_FREE); + switch (ret) { + case KNOT_EOK: break; + case KNOT_ESPACE: return KNOTD_IN_STATE_TRUNC; + default: return KNOTD_IN_STATE_ERROR; + } + } + } + + /* If node is a wildcard, follow only if we didn't visit the same node + * earlier, as that would mean a CNAME loop. */ + if (knot_dname_is_wildcard(cname_node->owner)) { + + /* Check if is not in wildcard nodes (loop). */ + if (wildcard_has_visited(qdata, cname_node)) { + qdata->extra->node = NULL; /* Act is if the name leads to nowhere. */ + return KNOTD_IN_STATE_HIT; + } + + /* Put to wildcard node list. */ + if (wildcard_visit(qdata, cname_node, qdata->extra->previous, qdata->name) != KNOT_EOK) { + return KNOTD_IN_STATE_ERROR; + } + } + + /* Now follow the next CNAME TARGET. */ + qdata->name = knot_cname_name(cname_rr.rrs.rdata); + + return KNOTD_IN_STATE_FOLLOW; +} + +static int name_found(knot_pkt_t *pkt, knotd_qdata_t *qdata) +{ + uint16_t qtype = knot_pkt_qtype(pkt); + + if (node_rrtype_exists(qdata->extra->node, KNOT_RRTYPE_CNAME) + && qtype != KNOT_RRTYPE_CNAME + && qtype != KNOT_RRTYPE_RRSIG + && qtype != KNOT_RRTYPE_NSEC + && qtype != KNOT_RRTYPE_ANY) { + return follow_cname(pkt, KNOT_RRTYPE_CNAME, qdata); + } + + /* DS query at DP is answered normally, but everything else at/below DP + * triggers referral response. */ + if (((qdata->extra->node->flags & NODE_FLAGS_DELEG) && qtype != KNOT_RRTYPE_DS) || + (qdata->extra->node->flags & NODE_FLAGS_NONAUTH)) { + return KNOTD_IN_STATE_DELEG; + } + + uint16_t old_rrcount = pkt->rrset_count; + int ret = put_answer(pkt, qtype, qdata); + if (ret != KNOT_EOK) { + if (ret == KNOT_ESPACE && (qdata->params->flags & KNOTD_QUERY_FLAG_LIMIT_SIZE)) { + return KNOTD_IN_STATE_TRUNC; + } else { + return KNOTD_IN_STATE_ERROR; + } + } + + /* Check for NODATA (=0 RRs added). */ + if (old_rrcount == pkt->rrset_count) { + return KNOTD_IN_STATE_NODATA; + } else { + return KNOTD_IN_STATE_HIT; + } +} + +static int name_not_found(knot_pkt_t *pkt, knotd_qdata_t *qdata) +{ + /* Name is covered by wildcard. */ + if (qdata->extra->encloser->flags & NODE_FLAGS_WILDCARD_CHILD) { + /* Find wildcard child in the zone. */ + const zone_node_t *wildcard_node = + zone_contents_find_wildcard_child( + qdata->extra->zone->contents, qdata->extra->encloser); + + qdata->extra->node = wildcard_node; + assert(qdata->extra->node != NULL); + + /* Follow expanded wildcard. */ + int next_state = name_found(pkt, qdata); + + /* Put to wildcard node list. */ + if (wildcard_visit(qdata, wildcard_node, qdata->extra->previous, qdata->name) != KNOT_EOK) { + next_state = KNOTD_IN_STATE_ERROR; + } + + return next_state; + } + + /* Name is under DNAME, use it for substitution. */ + knot_rrset_t dname_rrset = node_rrset(qdata->extra->encloser, KNOT_RRTYPE_DNAME); + if (!knot_rrset_empty(&dname_rrset)) { + qdata->extra->node = qdata->extra->encloser; /* Follow encloser as new node. */ + return follow_cname(pkt, KNOT_RRTYPE_DNAME, qdata); + } + + /* Look up an authoritative encloser or its parent. */ + const zone_node_t *node = qdata->extra->encloser; + while (node->rrset_count == 0 || node->flags & NODE_FLAGS_NONAUTH) { + node = node->parent; + assert(node); + } + + /* Name is below delegation. */ + if ((node->flags & NODE_FLAGS_DELEG)) { + qdata->extra->node = node; + return KNOTD_IN_STATE_DELEG; + } + + return KNOTD_IN_STATE_MISS; +} + +static int solve_name(int state, knot_pkt_t *pkt, knotd_qdata_t *qdata) +{ + int ret = zone_contents_find_dname(qdata->extra->zone->contents, qdata->name, + &qdata->extra->node, &qdata->extra->encloser, + &qdata->extra->previous); + + switch (ret) { + case ZONE_NAME_FOUND: + return name_found(pkt, qdata); + case ZONE_NAME_NOT_FOUND: + return name_not_found(pkt, qdata); + case KNOT_EOUTOFZONE: + assert(state == KNOTD_IN_STATE_FOLLOW); /* CNAME/DNAME chain only. */ + return KNOTD_IN_STATE_HIT; + default: + return KNOTD_IN_STATE_ERROR; + } +} + +static int solve_answer(int state, knot_pkt_t *pkt, knotd_qdata_t *qdata, void *ctx) +{ + /* Do not solve if already solved, e.g. in a module. */ + if (state == KNOTD_IN_STATE_HIT) { + return state; + } + + /* Get answer to QNAME. */ + state = solve_name(state, pkt, qdata); + + /* Is authoritative answer unless referral. + * Must check before we chase the CNAME chain. */ + if (state != KNOTD_IN_STATE_DELEG) { + knot_wire_set_aa(pkt->wire); + } + + /* Additional resolving for CNAME/DNAME chain. */ + while (state == KNOTD_IN_STATE_FOLLOW) { + state = solve_name(state, pkt, qdata); + } + + return state; +} + +static int solve_answer_dnssec(int state, knot_pkt_t *pkt, knotd_qdata_t *qdata, void *ctx) +{ + /* RFC4035, section 3.1 RRSIGs for RRs in ANSWER are mandatory. */ + int ret = nsec_append_rrsigs(pkt, qdata, false); + switch (ret) { + case KNOT_ESPACE: return KNOTD_IN_STATE_TRUNC; + case KNOT_EOK: return state; + default: return KNOTD_IN_STATE_ERROR; + } +} + +static int solve_authority(int state, knot_pkt_t *pkt, knotd_qdata_t *qdata, void *ctx) +{ + int ret = KNOT_ERROR; + const zone_contents_t *zone_contents = qdata->extra->zone->contents; + + switch (state) { + case KNOTD_IN_STATE_HIT: /* Positive response. */ + ret = KNOT_EOK; + break; + case KNOTD_IN_STATE_MISS: /* MISS, set NXDOMAIN RCODE. */ + qdata->rcode = KNOT_RCODE_NXDOMAIN; + ret = put_authority_soa(pkt, qdata, zone_contents); + break; + case KNOTD_IN_STATE_NODATA: /* NODATA append AUTHORITY SOA. */ + ret = put_authority_soa(pkt, qdata, zone_contents); + break; + case KNOTD_IN_STATE_DELEG: /* Referral response. */ + ret = put_delegation(pkt, qdata); + break; + case KNOTD_IN_STATE_TRUNC: /* Truncated ANSWER. */ + ret = KNOT_ESPACE; + break; + case KNOTD_IN_STATE_ERROR: /* Error resolving ANSWER. */ + break; + default: + assert(0); + break; + } + + /* Evaluate final state. */ + switch (ret) { + case KNOT_EOK: return state; /* Keep current state. */ + case KNOT_ESPACE: return KNOTD_IN_STATE_TRUNC; /* Truncated. */ + default: return KNOTD_IN_STATE_ERROR; /* Error. */ + } +} + +static int solve_authority_dnssec(int state, knot_pkt_t *pkt, knotd_qdata_t *qdata, void *ctx) +{ + int ret = KNOT_ERROR; + + /* Authenticated denial of existence. */ + switch (state) { + case KNOTD_IN_STATE_HIT: ret = KNOT_EOK; break; + case KNOTD_IN_STATE_MISS: ret = nsec_prove_nxdomain(pkt, qdata); break; + case KNOTD_IN_STATE_NODATA: ret = nsec_prove_nodata(pkt, qdata); break; + case KNOTD_IN_STATE_DELEG: ret = nsec_prove_dp_security(pkt, qdata); break; + case KNOTD_IN_STATE_TRUNC: ret = KNOT_ESPACE; break; + case KNOTD_IN_STATE_ERROR: ret = KNOT_ERROR; break; + default: + assert(0); + break; + } + + /* RFC4035 3.1.3 Prove visited wildcards. + * Wildcard expansion applies for Name Error, Wildcard Answer and + * No Data proofs if at one point the search expanded a wildcard node. + * \note Do not attempt to prove non-authoritative data. */ + if (ret == KNOT_EOK && state != KNOTD_IN_STATE_DELEG) { + ret = nsec_prove_wildcards(pkt, qdata); + } + + /* RFC4035, section 3.1 RRSIGs for RRs in AUTHORITY are mandatory. */ + if (ret == KNOT_EOK) { + ret = nsec_append_rrsigs(pkt, qdata, false); + } + + /* Evaluate final state. */ + switch (ret) { + case KNOT_EOK: return state; /* Keep current state. */ + case KNOT_ESPACE: return KNOTD_IN_STATE_TRUNC; /* Truncated. */ + default: return KNOTD_IN_STATE_ERROR; /* Error. */ + } +} + +static int solve_additional(int state, knot_pkt_t *pkt, knotd_qdata_t *qdata, + void *ctx) +{ + int ret = KNOT_EOK; + + /* Scan all RRs in ANSWER/AUTHORITY. */ + for (uint16_t i = 0; i < pkt->rrset_count; ++i) { + knot_rrset_t *rr = &pkt->rr[i]; + knot_rrinfo_t *info = &pkt->rr_info[i]; + + /* Skip types for which it doesn't apply. */ + if (!knot_rrtype_additional_needed(rr->type)) { + continue; + } + + /* Put additional records for given type. */ + ret = put_additional(pkt, rr, qdata, info, state); + if (ret != KNOT_EOK) { + break; + } + } + + /* Evaluate final state. */ + switch (ret) { + case KNOT_EOK: return state; /* Keep current state. */ + case KNOT_ESPACE: return KNOTD_IN_STATE_TRUNC; /* Truncated. */ + default: return KNOTD_IN_STATE_ERROR; /* Error. */ + } +} + +static int solve_additional_dnssec(int state, knot_pkt_t *pkt, knotd_qdata_t *qdata, void *ctx) +{ + /* RFC4035, section 3.1 RRSIGs for RRs in ADDITIONAL are optional. */ + int ret = nsec_append_rrsigs(pkt, qdata, true); + switch (ret) { + case KNOT_ESPACE: return KNOTD_IN_STATE_TRUNC; + case KNOT_EOK: return state; + default: return KNOTD_IN_STATE_ERROR; + } +} + +/*! \brief Helper for internet_query repetitive code. */ +#define SOLVE_STEP(solver, state, context) \ + state = (solver)(state, pkt, qdata, context); \ + if (state == KNOTD_IN_STATE_TRUNC) { \ + return KNOT_STATE_DONE; \ + } else if (state == KNOTD_IN_STATE_ERROR) { \ + return KNOT_STATE_FAIL; \ + } + +static int answer_query(knot_pkt_t *pkt, knotd_qdata_t *qdata) +{ + int state = KNOTD_IN_STATE_BEGIN; + struct query_plan *plan = qdata->extra->zone->query_plan; + struct query_step *step = NULL; + + bool with_dnssec = have_dnssec(qdata); + + /* Resolve PREANSWER. */ + if (plan != NULL) { + WALK_LIST(step, plan->stage[KNOTD_STAGE_PREANSWER]) { + SOLVE_STEP(step->process, state, step->ctx); + } + } + + /* Resolve ANSWER. */ + knot_pkt_begin(pkt, KNOT_ANSWER); + SOLVE_STEP(solve_answer, state, NULL); + if (with_dnssec) { + SOLVE_STEP(solve_answer_dnssec, state, NULL); + } + if (plan != NULL) { + WALK_LIST(step, plan->stage[KNOTD_STAGE_ANSWER]) { + SOLVE_STEP(step->process, state, step->ctx); + } + } + + /* Resolve AUTHORITY. */ + knot_pkt_begin(pkt, KNOT_AUTHORITY); + SOLVE_STEP(solve_authority, state, NULL); + if (with_dnssec) { + SOLVE_STEP(solve_authority_dnssec, state, NULL); + } + if (plan != NULL) { + WALK_LIST(step, plan->stage[KNOTD_STAGE_AUTHORITY]) { + SOLVE_STEP(step->process, state, step->ctx); + } + } + + /* Resolve ADDITIONAL. */ + knot_pkt_begin(pkt, KNOT_ADDITIONAL); + SOLVE_STEP(solve_additional, state, NULL); + if (with_dnssec) { + SOLVE_STEP(solve_additional_dnssec, state, NULL); + } + if (plan != NULL) { + WALK_LIST(step, plan->stage[KNOTD_STAGE_ADDITIONAL]) { + SOLVE_STEP(step->process, state, step->ctx); + } + } + + /* Write resulting RCODE. */ + knot_wire_set_rcode(pkt->wire, qdata->rcode); + + return KNOT_STATE_DONE; +} + +int internet_process_query(knot_pkt_t *pkt, knotd_qdata_t *qdata) +{ + if (pkt == NULL || qdata == NULL) { + return KNOT_STATE_FAIL; + } + + /* Check valid zone, transaction security (optional) and contents. */ + NS_NEED_ZONE(qdata, KNOT_RCODE_REFUSED); + + /* No applicable ACL, refuse transaction security. */ + if (knot_pkt_has_tsig(qdata->query)) { + /* We have been challenged... */ + NS_NEED_AUTH(qdata, qdata->extra->zone->name, ACL_ACTION_NONE); + + /* Reserve space for TSIG. */ + int ret = knot_pkt_reserve(pkt, knot_tsig_wire_size(&qdata->sign.tsig_key)); + if (ret != KNOT_EOK) { + return KNOT_STATE_FAIL; + } + } + + NS_NEED_ZONE_CONTENTS(qdata, KNOT_RCODE_SERVFAIL); /* Expired */ + + /* Get answer to QNAME. */ + qdata->name = knot_pkt_qname(qdata->query); + + return answer_query(pkt, qdata); +} diff --git a/src/knot/nameserver/internet.h b/src/knot/nameserver/internet.h new file mode 100644 index 0000000..d70ca0e --- /dev/null +++ b/src/knot/nameserver/internet.h @@ -0,0 +1,76 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "libknot/packet/pkt.h" +#include "knot/include/module.h" + +/*! + * \brief Answer query from an IN class zone. + * + * \retval KNOT_STATE_FAIL if it encountered an error. + * \retval KNOT_STATE_DONE if finished. + */ +int internet_process_query(knot_pkt_t *pkt, knotd_qdata_t *qdata); + +/*! \brief Require given QUERY TYPE or return error code. */ +#define NS_NEED_QTYPE(qdata, qtype_want, error_rcode) \ + if (knot_pkt_qtype((qdata)->query) != (qtype_want)) { \ + qdata->rcode = (error_rcode); \ + return KNOT_STATE_FAIL; \ + } + +/*! \brief Require given QUERY NAME or return error code. */ +#define NS_NEED_QNAME(qdata, qname_want, error_rcode) \ + if (!knot_dname_is_equal(knot_pkt_qname((qdata)->query), (qname_want))) { \ + qdata->rcode = (error_rcode); \ + return KNOT_STATE_FAIL; \ + } + +/*! \brief Require existing zone or return failure. */ +#define NS_NEED_ZONE(qdata, error_rcode) \ + if ((qdata)->extra->zone == NULL) { \ + qdata->rcode = (error_rcode); \ + return KNOT_STATE_FAIL; \ + } + +/*! \brief Require existing zone contents or return failure. */ +#define NS_NEED_ZONE_CONTENTS(qdata, error_rcode) \ + if ((qdata)->extra->zone->contents == NULL) { \ + qdata->rcode = (error_rcode); \ + return KNOT_STATE_FAIL; \ + } + +/*! \brief Require authentication. */ +#define NS_NEED_AUTH(qdata, zone_name, action) \ + if (!process_query_acl_check(conf(), (zone_name), (action), (qdata)) || \ + process_query_verify(qdata) != KNOT_EOK) { \ + return KNOT_STATE_FAIL; \ + } + +/*! \brief Require maximum number of unsigned messages. */ +#define NS_NEED_TSIG_SIGNED(tsig_ctx, max_unsigned) \ + if (tsig_unsigned_count(tsig_ctx) > max_unsigned) { \ + return KNOT_STATE_FAIL; \ + } + +/*! \brief Require the zone not to be frozen. */ +#define NS_NEED_NOT_FROZEN(qdata, error_rcode) \ + if ((qdata)->extra->zone->events.ufrozen) { \ + (qdata)->rcode = (error_rcode); \ + return KNOT_STATE_FAIL; \ + } diff --git a/src/knot/nameserver/ixfr.c b/src/knot/nameserver/ixfr.c new file mode 100644 index 0000000..fa1447e --- /dev/null +++ b/src/knot/nameserver/ixfr.c @@ -0,0 +1,298 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <urcu.h> + +#include "contrib/mempattern.h" +#include "contrib/sockaddr.h" +#include "knot/journal/chgset_ctx.h" +#include "knot/nameserver/axfr.h" +#include "knot/nameserver/internet.h" +#include "knot/nameserver/ixfr.h" +#include "knot/nameserver/log.h" +#include "knot/nameserver/xfr.h" +#include "knot/zone/serial.h" +#include "libknot/libknot.h" + +#define ZONE_NAME(qdata) knot_pkt_qname((qdata)->query) +#define REMOTE(qdata) (struct sockaddr *)(qdata)->params->remote + +#define IXFROUT_LOG(priority, qdata, fmt...) \ + ns_log(priority, ZONE_NAME(qdata), LOG_OPERATION_IXFR, \ + LOG_DIRECTION_OUT, REMOTE(qdata), fmt) + +/*! \brief Helper macro for putting RRs into packet. */ +#define IXFR_SAFE_PUT(pkt, rr) \ + int ret = knot_pkt_put((pkt), 0, (rr), KNOT_PF_NOTRUNC | KNOT_PF_ORIGTTL); \ + if (ret != KNOT_EOK) { \ + return ret; \ + } + +/*! \brief Puts current RR into packet, stores state for retries. */ +static int ixfr_put_chg_part(knot_pkt_t *pkt, struct ixfr_proc *ixfr, + chgset_ctx_t *itt) +{ + assert(pkt); + assert(ixfr); + assert(itt); + + if (!knot_rrset_empty(&ixfr->cur_rr)) { + IXFR_SAFE_PUT(pkt, &ixfr->cur_rr); + knot_rrset_clear(&ixfr->cur_rr, NULL); + } + + while (itt->phase != CHGSET_CTX_DONE) { + int r = chgset_ctx_next(itt, &ixfr->cur_rr); + if (r != KNOT_EOK) { + knot_rrset_clear(&ixfr->cur_rr, NULL); + return r; + } + IXFR_SAFE_PUT(pkt, &ixfr->cur_rr); + knot_rrset_clear(&ixfr->cur_rr, NULL); + } + + return KNOT_EOK; +} + +/*! + * \brief Process single changeset. + * \note Keep in mind that this function must be able to resume processing, + * for example if it fills a packet and returns ESPACE, it is called again + * with next empty answer and it must resume the processing exactly where + * it's left off. + */ +static int ixfr_process_changeset(knot_pkt_t *pkt, const void *item, + struct xfr_proc *xfer) +{ + int ret = KNOT_EOK; + struct ixfr_proc *ixfr = (struct ixfr_proc *)xfer; + chgset_ctx_t *chgset = (chgset_ctx_t *)item; + + ret = ixfr_put_chg_part(pkt, ixfr, chgset); + if (ret != KNOT_EOK) { + return ret; + } + + /* Finished change set. */ + IXFROUT_LOG(LOG_DEBUG, ixfr->qdata, "serial %u -> %u", chgset->serial_from, chgset->serial_to); + + return ret; +} + +#undef IXFR_SAFE_PUT + +static int ixfr_load_chsets(chgset_ctx_list_t *chgsets, zone_t *zone, + const knot_rrset_t *their_soa) +{ + assert(chgsets); + assert(zone); + + /* Compare serials. */ + uint32_t serial_to = zone_contents_serial(zone->contents); + uint32_t serial_from = knot_soa_serial(their_soa->rrs.rdata); + if (serial_compare(serial_to, serial_from) & SERIAL_MASK_LEQ) { /* We have older/same age zone. */ + return KNOT_EUPTODATE; + } + + return zone_chgset_ctx_load(conf(), zone, chgsets, serial_from); +} + +static int ixfr_query_check(knotd_qdata_t *qdata) +{ + NS_NEED_ZONE(qdata, KNOT_RCODE_NOTAUTH); + NS_NEED_AUTH(qdata, qdata->extra->zone->name, ACL_ACTION_TRANSFER); + NS_NEED_ZONE_CONTENTS(qdata, KNOT_RCODE_SERVFAIL); + + /* Need SOA authority record. */ + const knot_pktsection_t *authority = knot_pkt_section(qdata->query, KNOT_AUTHORITY); + const knot_rrset_t *their_soa = knot_pkt_rr(authority, 0); + if (authority->count < 1 || their_soa->type != KNOT_RRTYPE_SOA) { + qdata->rcode = KNOT_RCODE_FORMERR; + return KNOT_STATE_FAIL; + } + /* SOA needs to match QNAME. */ + NS_NEED_QNAME(qdata, their_soa->owner, KNOT_RCODE_FORMERR); + + return KNOT_STATE_DONE; +} + +static void ixfr_answer_cleanup(knotd_qdata_t *qdata) +{ + struct ixfr_proc *ixfr = (struct ixfr_proc *)qdata->extra->ext; + knot_mm_t *mm = qdata->mm; + + ptrlist_free(&ixfr->proc.nodes, mm); + chgset_ctx_list_close(&ixfr->changesets); + mm_free(mm, qdata->extra->ext); + + /* Allow zone changes (finished). */ + rcu_read_unlock(); +} + +static int ixfr_answer_init(knotd_qdata_t *qdata) +{ + assert(qdata); + + /* Check IXFR query validity. */ + if (ixfr_query_check(qdata) == KNOT_STATE_FAIL) { + if (qdata->rcode == KNOT_RCODE_FORMERR) { + return KNOT_EMALF; + } else { + return KNOT_EDENIED; + } + } + + /* Compare serials. */ + const knot_pktsection_t *authority = knot_pkt_section(qdata->query, KNOT_AUTHORITY); + const knot_rrset_t *their_soa = knot_pkt_rr(authority, 0); + + /* Initialize transfer processing. */ + knot_mm_t *mm = qdata->mm; + struct ixfr_proc *xfer = mm_alloc(mm, sizeof(struct ixfr_proc)); + if (xfer == NULL) { + return KNOT_ENOMEM; + } + memset(xfer, 0, sizeof(struct ixfr_proc)); + + int ret = ixfr_load_chsets(&xfer->changesets, (zone_t *)qdata->extra->zone, their_soa); + if (ret != KNOT_EOK) { + mm_free(mm, xfer); + return ret; + } + + xfr_stats_begin(&xfer->proc.stats); + xfer->state = IXFR_SOA_DEL; + init_list(&xfer->proc.nodes); + knot_rrset_init_empty(&xfer->cur_rr); + xfer->qdata = qdata; + + /* Put all changesets to processing queue. */ + chgset_ctx_t *chs = NULL; + WALK_LIST(chs, xfer->changesets.l) { + ptrlist_add(&xfer->proc.nodes, chs, mm); + chgset_ctx_iterate(chs); // init already, so we don't have to decide later, no harm + } + + /* Keep first and last serial. */ + chs = HEAD(xfer->changesets.l); + xfer->soa_from = chs->serial_from; + chs = TAIL(xfer->changesets.l); + xfer->soa_to = chs->serial_to; + + /* Set up cleanup callback. */ + qdata->extra->ext = xfer; + qdata->extra->ext_cleanup = &ixfr_answer_cleanup; + + /* No zone changes during multipacket answer (unlocked in ixfr_answer_cleanup) */ + rcu_read_lock(); + + return KNOT_EOK; +} + +static int ixfr_answer_soa(knot_pkt_t *pkt, knotd_qdata_t *qdata) +{ + assert(pkt); + assert(qdata); + + /* Check query. */ + int state = ixfr_query_check(qdata); + if (state == KNOT_STATE_FAIL) { + return state; /* Malformed query. */ + } + + /* Reserve space for TSIG. */ + int ret = knot_pkt_reserve(pkt, knot_tsig_wire_size(&qdata->sign.tsig_key)); + if (ret != KNOT_EOK) { + return KNOT_STATE_FAIL; + } + + /* Guaranteed to have zone contents. */ + const zone_node_t *apex = qdata->extra->zone->contents->apex; + knot_rrset_t soa_rr = node_rrset(apex, KNOT_RRTYPE_SOA); + if (knot_rrset_empty(&soa_rr)) { + return KNOT_STATE_FAIL; + } + ret = knot_pkt_put(pkt, 0, &soa_rr, 0); + if (ret != KNOT_EOK) { + qdata->rcode = KNOT_RCODE_SERVFAIL; + return KNOT_STATE_FAIL; + } + + return KNOT_STATE_DONE; +} + +int ixfr_process_query(knot_pkt_t *pkt, knotd_qdata_t *qdata) +{ + if (pkt == NULL || qdata == NULL) { + return KNOT_STATE_FAIL; + } + + /* If IXFR is disabled, respond with SOA. */ + if (qdata->params->flags & KNOTD_QUERY_FLAG_NO_IXFR) { + return ixfr_answer_soa(pkt, qdata); + } + + /* Initialize on first call. */ + struct ixfr_proc *ixfr = qdata->extra->ext; + if (ixfr == NULL) { + int ret = ixfr_answer_init(qdata); + ixfr = qdata->extra->ext; + switch (ret) { + case KNOT_EOK: /* OK */ + IXFROUT_LOG(LOG_INFO, qdata, "started, serial %u -> %u", + ixfr->soa_from, ixfr->soa_to); + break; + case KNOT_EUPTODATE: /* Our zone is same age/older, send SOA. */ + IXFROUT_LOG(LOG_INFO, qdata, "zone is up-to-date"); + return ixfr_answer_soa(pkt, qdata); + case KNOT_ERANGE: /* No history -> AXFR. */ + case KNOT_ENOENT: + IXFROUT_LOG(LOG_INFO, qdata, "incomplete history, fallback to AXFR"); + qdata->type = KNOTD_QUERY_TYPE_AXFR; /* Solve as AXFR. */ + return axfr_process_query(pkt, qdata); + case KNOT_EDENIED: /* Not authorized, already logged. */ + return KNOT_STATE_FAIL; + case KNOT_EMALF: /* Malformed query. */ + IXFROUT_LOG(LOG_DEBUG, qdata, "malformed query"); + return KNOT_STATE_FAIL; + default: /* Server errors. */ + IXFROUT_LOG(LOG_ERR, qdata, "failed to start (%s)", + knot_strerror(ret)); + return KNOT_STATE_FAIL; + } + } + + /* Reserve space for TSIG. */ + int ret = knot_pkt_reserve(pkt, knot_tsig_wire_size(&qdata->sign.tsig_key)); + if (ret != KNOT_EOK) { + return KNOT_STATE_FAIL; + } + + /* Answer current packet (or continue). */ + ret = xfr_process_list(pkt, &ixfr_process_changeset, qdata); + switch (ret) { + case KNOT_ESPACE: /* Couldn't write more, send packet and continue. */ + return KNOT_STATE_PRODUCE; /* Check for more. */ + case KNOT_EOK: /* Last response. */ + xfr_stats_end(&ixfr->proc.stats); + xfr_log_finished(ZONE_NAME(qdata), LOG_OPERATION_IXFR, LOG_DIRECTION_OUT, + REMOTE(qdata), &ixfr->proc.stats); + return KNOT_STATE_DONE; + default: /* Generic error. */ + IXFROUT_LOG(LOG_ERR, qdata, "failed (%s)", knot_strerror(ret)); + return KNOT_STATE_FAIL; + } +} diff --git a/src/knot/nameserver/ixfr.h b/src/knot/nameserver/ixfr.h new file mode 100644 index 0000000..6c8ba2e --- /dev/null +++ b/src/knot/nameserver/ixfr.h @@ -0,0 +1,60 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "knot/nameserver/process_query.h" +#include "knot/nameserver/xfr.h" +#include "libknot/packet/pkt.h" + +/*! \brief IXFR-in processing states. */ +enum ixfr_state { + IXFR_INVALID = 0, + IXFR_START, /* IXFR-in starting, expecting final SOA. */ + IXFR_SOA_DEL, /* Expecting starting SOA. */ + IXFR_SOA_ADD, /* Expecting ending SOA. */ + IXFR_DEL, /* Expecting RR to delete. */ + IXFR_ADD, /* Expecting RR to add. */ + IXFR_DONE /* Processing done, IXFR-in complete. */ +}; + +/*! \brief Extended structure for IXFR-in/IXFR-out processing. */ +struct ixfr_proc { + /* Processing state. */ + struct xfr_proc proc; + enum ixfr_state state; + + /* Changes to be sent. */ + chgset_ctx_list_t changesets; + + /* Currenty processed changeset. */ + knot_rrset_t cur_rr; + uint32_t soa_from; + uint32_t soa_to; + + /* Processing context. */ + knotd_qdata_t *qdata; + knot_mm_t *mm; +}; + +/*! + * \brief IXFR query processing module. + * + * \retval PRODUCE if it has an answer, but not yet finished. + * \retval FAIL if it encountered an error. + * \retval DONE if finished. + */ +int ixfr_process_query(knot_pkt_t *pkt, knotd_qdata_t *qdata); diff --git a/src/knot/nameserver/log.h b/src/knot/nameserver/log.h new file mode 100644 index 0000000..c46a908 --- /dev/null +++ b/src/knot/nameserver/log.h @@ -0,0 +1,90 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "contrib/sockaddr.h" +#include "knot/common/log.h" +#include "libknot/dname.h" + +enum log_operation { + LOG_OPERATION_AXFR, + LOG_OPERATION_IXFR, + LOG_OPERATION_NOTIFY, + LOG_OPERATION_REFRESH, + LOG_OPERATION_UPDATE, + LOG_OPERATION_PARENT, +}; + +enum log_direction { + LOG_DIRECTION_IN, + LOG_DIRECTION_OUT, +}; + +static inline const char *log_operation_name(enum log_operation operation) +{ + switch (operation) { + case LOG_OPERATION_AXFR: + return "AXFR"; + case LOG_OPERATION_IXFR: + return "IXFR"; + case LOG_OPERATION_NOTIFY: + return "notify"; + case LOG_OPERATION_REFRESH: + return "refresh"; + case LOG_OPERATION_UPDATE: + return "DDNS"; + case LOG_OPERATION_PARENT: + return "parent DS check"; + default: + return "?"; + } +} + +static inline const char *log_direction_name(enum log_direction direction) +{ + switch (direction) { + case LOG_DIRECTION_IN: + return "incoming"; + case LOG_DIRECTION_OUT: + return "outgoing"; + default: + return "?"; + } +} + +/*! + * \brief Generate log message for server communication. + * + * If this macro was a function: + * + * void ns_log(int priority, const knot_dname_t *zone, enum log_operation op, + * enum log_direction dir, const struct sockaddr *remote, + * const char *fmt, ...); + * + * Example output: + * + * [example.com] NOTIFY, outgoing, 2001:db8::1@53: serial 123 + * + */ +#define ns_log(priority, zone, op, dir, remote, fmt, ...) \ + do { \ + char address[SOCKADDR_STRLEN] = ""; \ + sockaddr_tostr(address, sizeof(address), remote); \ + log_fmt_zone(priority, LOG_SOURCE_ZONE, zone, NULL, "%s, %s, %s: " fmt, \ + log_operation_name(op), log_direction_name(dir), address, \ + ## __VA_ARGS__); \ + } while (0) diff --git a/src/knot/nameserver/notify.c b/src/knot/nameserver/notify.c new file mode 100644 index 0000000..fe9273b --- /dev/null +++ b/src/knot/nameserver/notify.c @@ -0,0 +1,93 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> + +#include "knot/common/log.h" +#include "knot/nameserver/notify.h" +#include "knot/nameserver/internet.h" +#include "knot/nameserver/log.h" +#include "knot/nameserver/tsig_ctx.h" +#include "knot/zone/serial.h" +#include "libdnssec/random.h" +#include "libknot/libknot.h" + +#define NOTIFY_LOG(priority, qdata, fmt...) \ + ns_log(priority, knot_pkt_qname(qdata->query), LOG_OPERATION_NOTIFY, \ + LOG_DIRECTION_IN, (struct sockaddr *)qdata->params->remote, fmt) + +static int notify_check_query(knotd_qdata_t *qdata) +{ + NS_NEED_ZONE(qdata, KNOT_RCODE_NOTAUTH); + NS_NEED_AUTH(qdata, qdata->extra->zone->name, ACL_ACTION_NOTIFY); + /* RFC1996 requires SOA question. */ + NS_NEED_QTYPE(qdata, KNOT_RRTYPE_SOA, KNOT_RCODE_FORMERR); + + return KNOT_STATE_DONE; +} + +int notify_process_query(knot_pkt_t *pkt, knotd_qdata_t *qdata) +{ + if (pkt == NULL || qdata == NULL) { + return KNOT_STATE_FAIL; + } + + /* Validate notification query. */ + int state = notify_check_query(qdata); + if (state == KNOT_STATE_FAIL) { + switch (qdata->rcode) { + case KNOT_RCODE_NOTAUTH: /* Not authorized, already logged. */ + break; + default: /* Other errors. */ + NOTIFY_LOG(LOG_DEBUG, qdata, "invalid query"); + break; + } + return state; + } + + /* Reserve space for TSIG. */ + int ret = knot_pkt_reserve(pkt, knot_tsig_wire_size(&qdata->sign.tsig_key)); + if (ret != KNOT_EOK) { + return KNOT_STATE_FAIL; + } + + /* SOA RR in answer may be included, recover serial. */ + zone_t *zone = (zone_t *)qdata->extra->zone; + const knot_pktsection_t *answer = knot_pkt_section(qdata->query, KNOT_ANSWER); + if (answer->count > 0) { + const knot_rrset_t *soa = knot_pkt_rr(answer, 0); + if (soa->type == KNOT_RRTYPE_SOA) { + uint32_t serial = knot_soa_serial(soa->rrs.rdata); + uint32_t zone_serial = zone_contents_serial(zone->contents); + (void)zone_get_master_serial(zone, &zone_serial); + NOTIFY_LOG(LOG_INFO, qdata, "received, serial %u", serial); + if (serial_equal(serial, zone_serial)) { + // NOTIFY serial == zone serial => ignore, keep timers + return KNOT_STATE_DONE; + } + } else { /* Complain, but accept N/A record. */ + NOTIFY_LOG(LOG_NOTICE, qdata, "received, bad record in answer section"); + } + } else { + NOTIFY_LOG(LOG_INFO, qdata, "received, serial none"); + } + + /* Incoming NOTIFY expires REFRESH timer and renews EXPIRE timer. */ + zone_set_preferred_master(zone, qdata->params->remote); + zone_events_schedule_now(zone, ZONE_EVENT_REFRESH); + + return KNOT_STATE_DONE; +} diff --git a/src/knot/nameserver/notify.h b/src/knot/nameserver/notify.h new file mode 100644 index 0000000..4f282da --- /dev/null +++ b/src/knot/nameserver/notify.h @@ -0,0 +1,30 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "libknot/packet/pkt.h" +#include "knot/nameserver/process_query.h" + +#define NOTIFY_TIMEOUT 3 /*!< Interval between NOTIFY retries. */ + +/*! + * \brief Answer IN class zone NOTIFY message (RFC1996). + * + * \retval FAIL if it encountered an error. + * \retval DONE if finished. + */ +int notify_process_query(knot_pkt_t *pkt, knotd_qdata_t *qdata); diff --git a/src/knot/nameserver/nsec_proofs.c b/src/knot/nameserver/nsec_proofs.c new file mode 100644 index 0000000..cbbdf2d --- /dev/null +++ b/src/knot/nameserver/nsec_proofs.c @@ -0,0 +1,677 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> + +#include "libknot/libknot.h" +#include "knot/nameserver/nsec_proofs.h" +#include "knot/nameserver/internet.h" +#include "knot/dnssec/zone-nsec.h" + +/*! + * \brief Check if node is empty non-terminal. + */ +static bool empty_nonterminal(const zone_node_t *node) +{ + return node && node->rrset_count == 0; +} + +/*! + * \brief Check if wildcard expansion happened for given node and QNAME. + */ +static bool wildcard_expanded(const zone_node_t *node, const knot_dname_t *qname) +{ + return !knot_dname_is_wildcard(qname) && knot_dname_is_wildcard(node->owner); +} + +/*! + * \brief Check if opt-out can take an effect. + */ +static bool ds_optout(const zone_node_t *node) +{ + return node->nsec3_node == NULL && node->flags & NODE_FLAGS_DELEG; +} + +/*! + * \brief Check if node is part of the NSEC chain. + * + * NSEC is created for each node with authoritative data or delegation. + * + * \see https://tools.ietf.org/html/rfc4035#section-2.3 + */ +static bool node_in_nsec(const zone_node_t *node) +{ + return (node->flags & NODE_FLAGS_NONAUTH) == 0 && !empty_nonterminal(node); +} + +/*! + * \brief Check if node is part of the NSEC3 chain. + * + * NSEC3 is created for each node with authoritative data, empty-non terminal, + * and delegation (unless opt-out is in effect). + * + * \see https://tools.ietf.org/html/rfc5155#section-7.1 + */ +static bool node_in_nsec3(const zone_node_t *node) +{ + return (node->flags & NODE_FLAGS_NONAUTH) == 0 && !ds_optout(node); +} + +/*! + * \brief Walk previous names until we reach a node in NSEC chain. + * + */ +static const zone_node_t *nsec_previous(const zone_node_t *previous) +{ + assert(previous); + + while (!node_in_nsec(previous)) { + previous = previous->prev; + assert(previous); + } + + return previous; +} + +/*! + * \brief Get closest provable encloser from closest matching parent node. + */ +static const zone_node_t *nsec3_encloser(const zone_node_t *closest) +{ + assert(closest); + + while (!node_in_nsec3(closest)) { + closest = closest->parent; + assert(closest); + } + + return closest; +} + +/*! + * \brief Create a 'next closer name' to the given domain name. + * + * Next closer is the name one label longer than the closest provable encloser + * of a name. + * + * \see https://tools.ietf.org/html/rfc5155#section-1.3 + * + * \param closest_encloser Closest provable encloser of \a name. + * \param name Domain name to create the 'next closer' name to. + * + * \return Next closer name, NULL on error. + */ +static const knot_dname_t *get_next_closer(const knot_dname_t *closest_encloser, + const knot_dname_t *name) +{ + // make name only one label longer than closest_encloser + size_t ce_labels = knot_dname_labels(closest_encloser, NULL); + size_t qname_labels = knot_dname_labels(name, NULL); + for (int i = 0; i < (qname_labels - ce_labels - 1); ++i) { + name = knot_wire_next_label(name, NULL); + } + + // the common labels should match + assert(knot_dname_is_equal(knot_wire_next_label(name, NULL), closest_encloser)); + + return name; +} + +/*! + * \brief Put NSEC/NSEC3 record with corresponding RRSIG into the response. + */ +static int put_nxt_from_node(const zone_node_t *node, + uint16_t type, + knotd_qdata_t *qdata, + knot_pkt_t *resp) +{ + assert(type == KNOT_RRTYPE_NSEC || type == KNOT_RRTYPE_NSEC3); + + knot_rrset_t rrset = node_rrset(node, type); + if (knot_rrset_empty(&rrset)) { + return KNOT_EOK; + } + + knot_rrset_t rrsigs = node_rrset(node, KNOT_RRTYPE_RRSIG); + + return process_query_put_rr(resp, qdata, &rrset, &rrsigs, + KNOT_COMPR_HINT_NONE, KNOT_PF_CHECKDUP); +} + +/*! + * \brief Put NSEC record with corresponding RRSIG into the response. + */ +static int put_nsec_from_node(const zone_node_t *node, + knotd_qdata_t *qdata, + knot_pkt_t *resp) +{ + return put_nxt_from_node(node, KNOT_RRTYPE_NSEC, qdata, resp); +} + +/*! + * \brief Put NSEC3 record with corresponding RRSIG into the response. + */ +static int put_nsec3_from_node(const zone_node_t *node, + knotd_qdata_t *qdata, + knot_pkt_t *resp) +{ + return put_nxt_from_node(node, KNOT_RRTYPE_NSEC3, qdata, resp); +} + +/*! + * \brief Find NSEC for given name and put it into the response. + * + * Note this function allows the name to match the QNAME. The NODATA proof + * for empty non-terminal is equivalent to NXDOMAIN proof, except that the + * names may exist. This is why. + */ +static int put_covering_nsec(const zone_contents_t *zone, + const knot_dname_t *name, + knotd_qdata_t *qdata, + knot_pkt_t *resp) +{ + const zone_node_t *match = NULL; + const zone_node_t *closest = NULL; + const zone_node_t *prev = NULL; + + const zone_node_t *proof = NULL; + + int ret = zone_contents_find_dname(zone, name, &match, &closest, &prev); + if (ret == ZONE_NAME_FOUND) { + proof = match; + } else if (ret == ZONE_NAME_NOT_FOUND) { + proof = nsec_previous(prev); + } else { + assert(ret < 0); + return ret; + } + + return put_nsec_from_node(proof, qdata, resp); +} + +/*! + * \brief Find NSEC3 covering the given name and put it into the response. + */ +static int put_covering_nsec3(const zone_contents_t *zone, + const knot_dname_t *name, + knotd_qdata_t *qdata, + knot_pkt_t *resp) +{ + const zone_node_t *prev = NULL; + const zone_node_t *node = NULL; + + int match = zone_contents_find_nsec3_for_name(zone, name, &node, &prev); + if (match < 0) { + // ignore if missing + return KNOT_EOK; + } + + if (match == ZONE_NAME_FOUND || prev == NULL){ + return KNOT_ERROR; + } + + return put_nsec3_from_node(prev, qdata, resp); +} + +/*! + * \brief Add NSEC3 covering the next closer name to closest encloser. + * + * \param cpe Closest provable encloser of \a qname. + * \param qname Source QNAME. + * \param zone Source zone. + * \param qdata Query processing data. + * \param resp Response packet. + * + * \return KNOT_E* + */ +static int put_nsec3_next_closer(const zone_node_t *cpe, + const knot_dname_t *qname, + const zone_contents_t *zone, + knotd_qdata_t *qdata, + knot_pkt_t *resp) +{ + const knot_dname_t *next_closer = get_next_closer(cpe->owner, qname); + + return put_covering_nsec3(zone, next_closer, qdata, resp); +} + +/*! + * \brief Add NSEC3s for closest encloser proof. + * + * Adds up to two NSEC3 records. The first one proves that closest encloser + * of the queried name exists, the second one proves that the name bellow the + * encloser doesn't. + * + * \see https://tools.ietf.org/html/rfc5155#section-7.2.1 + * + * \param qname Source QNAME. + * \param zone Source zone. + * \param cpe Closest provable encloser of \a qname. + * \param qdata Query processing data. + * \param resp Response packet. + * + * \return KNOT_E* + */ +static int put_closest_encloser_proof(const knot_dname_t *qname, + const zone_contents_t *zone, + const zone_node_t *cpe, + knotd_qdata_t *qdata, + knot_pkt_t *resp) +{ + // An NSEC3 RR that matches the closest (provable) encloser. + + int ret = put_nsec3_from_node(cpe->nsec3_node, qdata, resp); + if (ret != KNOT_EOK) { + return ret; + } + + // An NSEC3 RR that covers the "next closer" name to the closest encloser. + + return put_nsec3_next_closer(cpe, qname, zone, qdata, resp); +} + +/*! + * \brief Put NSEC for wildcard answer into the response. + * + * Add NSEC record proving that no better match on QNAME exists. + * + * \see https://tools.ietf.org/html/rfc4035#section-3.1.3.3 + * + * \param previous Previous name for QNAME. + * \param qdata Query processing data. + * \param resp Response packet. + * + * \return KNOT_E* + */ +static int put_nsec_wildcard(const zone_node_t *previous, + knotd_qdata_t *qdata, + knot_pkt_t *resp) +{ + return put_nsec_from_node(previous, qdata, resp); +} + +/*! + * \brief Put NSEC3s for wildcard answer into the response. + * + * Add NSEC3 record proving that no better match on QNAME exists. + * + * \see https://tools.ietf.org/html/rfc5155#section-7.2.6 + * + * \param wildcard Wildcard node that was used for expansion. + * \param qname Source QNAME. + * \param zone Source zone. + * \param qdata Query processing data. + * \param resp Response packet. + */ +static int put_nsec3_wildcard(const zone_node_t *wildcard, + const knot_dname_t *qname, + const zone_contents_t *zone, + knotd_qdata_t *qdata, + knot_pkt_t *resp) +{ + const zone_node_t *cpe = nsec3_encloser(wildcard->parent); + + return put_nsec3_next_closer(cpe, qname, zone, qdata, resp); +} + +/*! + * \brief Put NSECs or NSEC3s for wildcard expansion in the response. + * + * \return KNOT_E* + */ +static int put_wildcard_answer(const zone_node_t *wildcard, + const zone_node_t *previous, + const zone_contents_t *zone, + const knot_dname_t *qname, + knotd_qdata_t *qdata, + knot_pkt_t *resp) +{ + if (!wildcard_expanded(wildcard, qname)) { + return KNOT_EOK; + } + + int ret = 0; + + if (knot_is_nsec3_enabled(zone)) { + ret = put_nsec3_wildcard(wildcard, qname, zone, qdata, resp); + } else { + previous = nsec_previous(previous); + ret = put_nsec_wildcard(previous, qdata, resp); + } + + return ret; +} + +/*! + * \brief Put NSECs for NXDOMAIN error into the response. + * + * Adds up to two NSEC records. We have to prove that the queried name doesn't + * exist and that no wildcard expansion is possible for that name. + * + * \see https://tools.ietf.org/html/rfc4035#section-3.1.3.2 + * + * \param zone Source zone. + * \param previous Previous node to QNAME. + * \param closest Closest matching parent of QNAME. + * \param qdata Query data. + * \param resp Response packet. + * + * \return KNOT_E* + */ +static int put_nsec_nxdomain(const zone_contents_t *zone, + const zone_node_t *previous, + const zone_node_t *closest, + knotd_qdata_t *qdata, + knot_pkt_t *resp) +{ + assert(previous); + assert(closest); + + // An NSEC RR proving that there is no exact match for <SNAME, SCLASS>. + + previous = nsec_previous(previous); + int ret = put_nsec_from_node(previous, qdata, resp); + if (ret != KNOT_EOK) { + return ret; + } + + // An NSEC RR proving that the zone contains no RRsets that would match + // <SNAME, SCLASS> via wildcard name expansion. + + // NOTE: closest may be empty non-terminal and thus not authoritative. + + size_t size = knot_dname_size(closest->owner); + if (size > KNOT_DNAME_MAXLEN - 2) { + return KNOT_EINVAL; + } + assert(size > 0); + uint8_t wildcard[2 + size]; + memcpy(wildcard, "\x01""*", 2); + memcpy(wildcard + 2, closest->owner, size); + + return put_covering_nsec(zone, wildcard, qdata, resp); +} + +/*! + * \brief Put NSEC3s for NXDOMAIN error into the response. + * + * Adds up to three NSEC3 records. We have to prove that some parent name + * exists (closest encloser proof) and that no wildcard expansion is possible + * bellow that closest encloser. + * + * \see https://tools.ietf.org/html/rfc5155#section-7.2.2 + * + * \param qname Source QNAME. + * \param zone Source zone. + * \param closest Closest matching parent of \a qname. + * \param qdata Query processing data. + * \param resp Response packet. + * + * \retval KNOT_E* + */ +static int put_nsec3_nxdomain(const knot_dname_t *qname, + const zone_contents_t *zone, + const zone_node_t *closest, + knotd_qdata_t *qdata, + knot_pkt_t *resp) +{ + const zone_node_t *cpe = nsec3_encloser(closest); + + // Closest encloser proof. + + int ret = put_closest_encloser_proof(qname, zone, cpe, qdata, resp); + if (ret != KNOT_EOK) { + return ret; + } + + // NSEC3 covering the (nonexistent) wildcard at the closest encloser. + + if (cpe->nsec3_wildcard_prev == NULL) { + return KNOT_ERROR; + } + + return put_nsec3_from_node(cpe->nsec3_wildcard_prev, qdata, resp); +} + +/*! + * \brief Put NSECs or NSEC3s for the NXDOMAIN error into the response. + * + * \param zone Zone used for answering. + * \param previous Previous node to \a qname. + * \param closest Closest matching parent name for \a qname. + * \param qname Source QNAME. + * \param qdata Query processing data. + * \param resp Response packet. + * + * \return KNOT_E* + */ +static int put_nxdomain(const zone_contents_t *zone, + const zone_node_t *previous, + const zone_node_t *closest, + const knot_dname_t *qname, + knotd_qdata_t *qdata, + knot_pkt_t *resp) +{ + if (knot_is_nsec3_enabled(zone)) { + return put_nsec3_nxdomain(qname, zone, closest, qdata, resp); + } else { + return put_nsec_nxdomain(zone, previous, closest, qdata, resp); + } +} + +/*! + * \brief Put NSEC for NODATA error into the response. + * + * Then NSEC matching the QNAME must be added into the response and the bitmap + * will indicate that the QTYPE doesn't exist. As NSECs for empty non-terminals + * don't exist, the proof for NODATA match on non-terminal is proved as for + * NXDOMAIN. + * + * \see https://tools.ietf.org/html/rfc4035#section-3.1.3.1 + * \see https://tools.ietf.org/html/rfc4035#section-3.1.3.2 (empty non-terminal) + * + * \param zone Source zone. + * \param match Node matching QNAME. + * \param previous Previous node to QNAME in the zone. + * \param qdata Query procssing data. + * \param resp Response packet. + * + * \return KNOT_E* + */ +static int put_nsec_nodata(const zone_contents_t *zone, + const zone_node_t *match, + const zone_node_t *closest, + const zone_node_t *previous, + knotd_qdata_t *qdata, + knot_pkt_t *resp) +{ + if (empty_nonterminal(match)) { + return put_nsec_nxdomain(zone, previous, closest, qdata, resp); + } else { + return put_nsec_from_node(match, qdata, resp); + } +} + +/*! + * \brief Put NSEC3 for NODATA error into the response. + * + * The NSEC3 matching the QNAME is added into the response and the bitmap + * will indicate that the QTYPE doesn't exist. For QTYPE==DS, the server + * may alternatively serve a closest encloser proof with opt-out. For wildcard + * expansion, the closest encloser proof must included as well. + * + * \see https://tools.ietf.org/html/rfc5155#section-7.2.3 + * \see https://tools.ietf.org/html/rfc5155#section-7.2.4 + * \see https://tools.ietf.org/html/rfc5155#section-7.2.5 + */ +static int put_nsec3_nodata(const knot_dname_t *qname, + const zone_contents_t *zone, + const zone_node_t *match, + const zone_node_t *closest, + knotd_qdata_t *qdata, + knot_pkt_t *resp) +{ + int ret = KNOT_EOK; + + // NSEC3 matching QNAME is always included. + + if (match->nsec3_node) { + ret = put_nsec3_from_node(match->nsec3_node, qdata, resp); + if (ret != KNOT_EOK) { + return ret; + } + } + + // Closest encloser proof for wildcard effect or NSEC3 opt-out. + + if (wildcard_expanded(match, qname) || ds_optout(match)) { + const zone_node_t *cpe = nsec3_encloser(closest); + ret = put_closest_encloser_proof(qname, zone, cpe, qdata, resp); + } + + return ret; +} + +/*! + * \brief Put NSECs or NSEC3s for the NODATA error into the response. + * + * \param node Source node. + * \param qdata Query processing data. + * \param resp Response packet. + */ +static int put_nodata(const zone_node_t *node, + const zone_node_t *closest, + const zone_node_t *previous, + const zone_contents_t *zone, + const knot_dname_t *qname, + knotd_qdata_t *qdata, + knot_pkt_t *resp) +{ + if (knot_is_nsec3_enabled(zone)) { + return put_nsec3_nodata(qname, zone, node, closest, qdata, resp); + } else { + return put_nsec_nodata(zone, node, closest, previous, qdata, resp); + } +} + +int nsec_prove_wildcards(knot_pkt_t *pkt, knotd_qdata_t *qdata) +{ + if (qdata->extra->zone->contents == NULL) { + return KNOT_EINVAL; + } + + int ret = KNOT_EOK; + struct wildcard_hit *item = NULL; + + WALK_LIST(item, qdata->extra->wildcards) { + if (item->node == NULL) { + return KNOT_EINVAL; + } + ret = put_wildcard_answer(item->node, item->prev, + qdata->extra->zone->contents, + item->sname, qdata, pkt); + if (ret != KNOT_EOK) { + break; + } + } + + return ret; +} + +int nsec_prove_nodata(knot_pkt_t *pkt, knotd_qdata_t *qdata) +{ + if (qdata->extra->zone->contents == NULL || qdata->extra->node == NULL) { + return KNOT_EINVAL; + } + + return put_nodata(qdata->extra->node, qdata->extra->encloser, qdata->extra->previous, + qdata->extra->zone->contents, qdata->name, qdata, pkt); +} + +int nsec_prove_nxdomain(knot_pkt_t *pkt, knotd_qdata_t *qdata) +{ + if (qdata->extra->zone->contents == NULL) { + return KNOT_EINVAL; + } + + return put_nxdomain(qdata->extra->zone->contents, + qdata->extra->previous, qdata->extra->encloser, + qdata->name, qdata, pkt); +} + +int nsec_prove_dp_security(knot_pkt_t *pkt, knotd_qdata_t *qdata) +{ + if (qdata->extra->node == NULL || qdata->extra->encloser == NULL || + qdata->extra->zone->contents == NULL) { + return KNOT_EINVAL; + } + + // Add DS into the response. + + knot_rrset_t rrset = node_rrset(qdata->extra->node, KNOT_RRTYPE_DS); + if (!knot_rrset_empty(&rrset)) { + knot_rrset_t rrsigs = node_rrset(qdata->extra->node, KNOT_RRTYPE_RRSIG); + return process_query_put_rr(pkt, qdata, &rrset, &rrsigs, + KNOT_COMPR_HINT_NONE, 0); + } + + // Alternatively prove that DS doesn't exist. + + return put_nodata(qdata->extra->node, qdata->extra->encloser, qdata->extra->previous, + qdata->extra->zone->contents, qdata->name, qdata, pkt); +} + +int nsec_append_rrsigs(knot_pkt_t *pkt, knotd_qdata_t *qdata, bool optional) +{ + int ret = KNOT_EOK; + uint32_t flags = optional ? KNOT_PF_NOTRUNC : KNOT_PF_NULL; + flags |= KNOT_PF_FREE; // Free all RRSIGs, they are synthesized + flags |= KNOT_PF_ORIGTTL; + + /* Append RRSIGs for section. */ + struct rrsig_info *info = NULL; + WALK_LIST(info, qdata->extra->rrsigs) { + knot_rrset_t *rrsig = &info->synth_rrsig; + uint16_t compr_hint = info->rrinfo->compress_ptr[KNOT_COMPR_HINT_OWNER]; + ret = knot_pkt_put(pkt, compr_hint, rrsig, flags); + if (ret != KNOT_EOK) { + break; + } + /* RRSIG is owned by packet now. */ + knot_rdataset_init(&info->synth_rrsig.rrs); + }; + + /* Clear the list. */ + nsec_clear_rrsigs(qdata); + + return ret; +} + +void nsec_clear_rrsigs(knotd_qdata_t *qdata) +{ + if (qdata == NULL) { + return; + } + + struct rrsig_info *info = NULL; + WALK_LIST(info, qdata->extra->rrsigs) { + knot_rrset_t *rrsig = &info->synth_rrsig; + knot_rrset_clear(rrsig, qdata->mm); + }; + + ptrlist_free(&qdata->extra->rrsigs, qdata->mm); + init_list(&qdata->extra->rrsigs); +} diff --git a/src/knot/nameserver/nsec_proofs.h b/src/knot/nameserver/nsec_proofs.h new file mode 100644 index 0000000..1a5528f --- /dev/null +++ b/src/knot/nameserver/nsec_proofs.h @@ -0,0 +1,38 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "libknot/packet/pkt.h" +#include "knot/nameserver/process_query.h" + +/*! \brief Prove wildcards visited during answer resolution. */ +int nsec_prove_wildcards(knot_pkt_t *pkt, knotd_qdata_t *qdata); + +/*! \brief Prove answer leading to non-existent name. */ +int nsec_prove_nxdomain(knot_pkt_t *pkt, knotd_qdata_t *qdata); + +/*! \brief Prove empty answer. */ +int nsec_prove_nodata(knot_pkt_t *pkt, knotd_qdata_t *qdata); + +/*! \brief Prove delegation point security. */ +int nsec_prove_dp_security(knot_pkt_t *pkt, knotd_qdata_t *qdata); + +/*! \brief Append missing RRSIGs for current processing section. */ +int nsec_append_rrsigs(knot_pkt_t *pkt, knotd_qdata_t *qdata, bool optional); + +/*! \brief Clear RRSIG list. */ +void nsec_clear_rrsigs(knotd_qdata_t *qdata); diff --git a/src/knot/nameserver/process_query.c b/src/knot/nameserver/process_query.c new file mode 100644 index 0000000..39a905d --- /dev/null +++ b/src/knot/nameserver/process_query.c @@ -0,0 +1,891 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <urcu.h> + +#include "libdnssec/tsig.h" +#include "knot/common/log.h" +#include "knot/dnssec/rrset-sign.h" +#include "knot/nameserver/process_query.h" +#include "knot/nameserver/query_module.h" +#include "knot/nameserver/chaos.h" +#include "knot/nameserver/internet.h" +#include "knot/nameserver/axfr.h" +#include "knot/nameserver/ixfr.h" +#include "knot/nameserver/update.h" +#include "knot/nameserver/nsec_proofs.h" +#include "knot/nameserver/notify.h" +#include "knot/server/server.h" +#include "libknot/libknot.h" +#include "contrib/macros.h" +#include "contrib/mempattern.h" + +/*! \brief Accessor to query-specific data. */ +#define QUERY_DATA(ctx) ((knotd_qdata_t *)(ctx)->data) + +static knotd_query_type_t query_type(const knot_pkt_t *pkt) +{ + switch (knot_wire_get_opcode(pkt->wire)) { + case KNOT_OPCODE_QUERY: + switch (knot_pkt_qtype(pkt)) { + case 0 /* RESERVED */: return KNOTD_QUERY_TYPE_INVALID; + case KNOT_RRTYPE_AXFR: return KNOTD_QUERY_TYPE_AXFR; + case KNOT_RRTYPE_IXFR: return KNOTD_QUERY_TYPE_IXFR; + default: return KNOTD_QUERY_TYPE_NORMAL; + } + case KNOT_OPCODE_NOTIFY: return KNOTD_QUERY_TYPE_NOTIFY; + case KNOT_OPCODE_UPDATE: return KNOTD_QUERY_TYPE_UPDATE; + default: return KNOTD_QUERY_TYPE_INVALID; + } +} + +/*! \brief Reinitialize query data structure. */ +static void query_data_init(knot_layer_t *ctx, knotd_qdata_params_t *params, + knotd_qdata_extra_t *extra) +{ + /* Initialize persistent data. */ + knotd_qdata_t *data = QUERY_DATA(ctx); + memset(data, 0, sizeof(*data)); + data->mm = ctx->mm; + data->params = params; + data->extra = extra; + + /* Initialize lists. */ + memset(extra, 0, sizeof(*extra)); + init_list(&extra->wildcards); + init_list(&extra->rrsigs); +} + +static int process_query_begin(knot_layer_t *ctx, void *params) +{ + /* Initialize context. */ + assert(ctx); + ctx->data = mm_alloc(ctx->mm, sizeof(knotd_qdata_t)); + knotd_qdata_extra_t *extra = mm_alloc(ctx->mm, sizeof(*extra)); + + /* Initialize persistent data. */ + query_data_init(ctx, params, extra); + + /* Await packet. */ + return KNOT_STATE_CONSUME; +} + +static int process_query_reset(knot_layer_t *ctx) +{ + assert(ctx); + knotd_qdata_t *qdata = QUERY_DATA(ctx); + + /* Remember persistent parameters. */ + knotd_qdata_params_t *params = qdata->params; + knotd_qdata_extra_t *extra = qdata->extra; + + /* Free allocated data. */ + knot_rrset_clear(&qdata->opt_rr, qdata->mm); + ptrlist_free(&extra->wildcards, qdata->mm); + nsec_clear_rrsigs(qdata); + if (extra->ext_cleanup != NULL) { + extra->ext_cleanup(qdata); + } + + /* Initialize persistent data. */ + query_data_init(ctx, params, extra); + + /* Await packet. */ + return KNOT_STATE_CONSUME; +} + +static int process_query_finish(knot_layer_t *ctx) +{ + process_query_reset(ctx); + mm_free(ctx->mm, ctx->data); + ctx->data = NULL; + + return KNOT_STATE_NOOP; +} + +static int process_query_in(knot_layer_t *ctx, knot_pkt_t *pkt) +{ + assert(pkt && ctx); + knotd_qdata_t *qdata = QUERY_DATA(ctx); + + /* Check if at least header is parsed. */ + if (pkt->parsed < KNOT_WIRE_HEADER_SIZE) { + return KNOT_STATE_NOOP; /* Ignore. */ + } + + /* Accept only queries. */ + if (knot_wire_get_qr(pkt->wire)) { + return KNOT_STATE_NOOP; /* Ignore. */ + } + + /* Store for processing. */ + qdata->query = pkt; + qdata->type = query_type(pkt); + + /* Declare having response. */ + return KNOT_STATE_PRODUCE; +} + +/*! + * \brief Create a response for a given query in the INTERNET class. + */ +static int query_internet(knot_pkt_t *pkt, knot_layer_t *ctx) +{ + knotd_qdata_t *data = QUERY_DATA(ctx); + + switch (data->type) { + case KNOTD_QUERY_TYPE_NORMAL: return internet_process_query(pkt, data); + case KNOTD_QUERY_TYPE_NOTIFY: return notify_process_query(pkt, data); + case KNOTD_QUERY_TYPE_AXFR: return axfr_process_query(pkt, data); + case KNOTD_QUERY_TYPE_IXFR: return ixfr_process_query(pkt, data); + case KNOTD_QUERY_TYPE_UPDATE: return update_process_query(pkt, data); + default: + /* Nothing else is supported. */ + data->rcode = KNOT_RCODE_NOTIMPL; + return KNOT_STATE_FAIL; + } +} + +/*! + * \brief Create a response for a given query in the CHAOS class. + */ +static int query_chaos(knot_pkt_t *pkt, knot_layer_t *ctx) +{ + knotd_qdata_t *data = QUERY_DATA(ctx); + + /* Nothing except normal queries is supported. */ + if (data->type != KNOTD_QUERY_TYPE_NORMAL) { + data->rcode = KNOT_RCODE_NOTIMPL; + return KNOT_STATE_FAIL; + } + + data->rcode = knot_chaos_answer(pkt); + if (data->rcode != KNOT_RCODE_NOERROR) { + return KNOT_STATE_FAIL; + } + + return KNOT_STATE_DONE; +} + +/*! \brief Find zone for given question. */ +static const zone_t *answer_zone_find(const knot_pkt_t *query, knot_zonedb_t *zonedb) +{ + uint16_t qtype = knot_pkt_qtype(query); + uint16_t qclass = knot_pkt_qclass(query); + const knot_dname_t *qname = knot_pkt_qname(query); + const zone_t *zone = NULL; + + // search for zone only for IN and ANY classes + if (qclass != KNOT_CLASS_IN && qclass != KNOT_CLASS_ANY) { + return NULL; + } + + /* In case of DS query, we strip the leftmost label when searching for + * the zone (but use whole qname in search for the record), as the DS + * records are only present in a parent zone. + */ + if (qtype == KNOT_RRTYPE_DS) { + const knot_dname_t *parent = knot_wire_next_label(qname, NULL); + zone = knot_zonedb_find_suffix(zonedb, parent); + /* If zone does not exist, search for its parent zone, + this will later result to NODATA answer. */ + /*! \note This is not 100% right, it may lead to DS name for example + * when following a CNAME chain, that should also be answered + * from the parent zone (if it exists). + */ + } + + if (zone == NULL) { + if (query_type(query) == KNOTD_QUERY_TYPE_NORMAL) { + zone = knot_zonedb_find_suffix(zonedb, qname); + } else { + // Direct match required. + zone = knot_zonedb_find(zonedb, qname); + } + } + + return zone; +} + +static int answer_edns_reserve(knot_pkt_t *resp, knotd_qdata_t *qdata) +{ + if (knot_rrset_empty(&qdata->opt_rr)) { + return KNOT_EOK; + } + + /* Reserve size in the response. */ + return knot_pkt_reserve(resp, knot_edns_wire_size(&qdata->opt_rr)); +} + +static int answer_edns_init(const knot_pkt_t *query, knot_pkt_t *resp, + knotd_qdata_t *qdata) +{ + if (!knot_pkt_has_edns(query)) { + return KNOT_EOK; + } + + /* Initialize OPT record. */ + int16_t max_payload; + switch (qdata->params->remote->ss_family) { + case AF_INET: + max_payload = conf()->cache.srv_max_ipv4_udp_payload; + break; + case AF_INET6: + max_payload = conf()->cache.srv_max_ipv6_udp_payload; + break; + default: + return KNOT_ERROR; + } + int ret = knot_edns_init(&qdata->opt_rr, max_payload, 0, + KNOT_EDNS_VERSION, qdata->mm); + if (ret != KNOT_EOK) { + return ret; + } + + /* Check supported version. */ + if (knot_edns_get_version(query->opt_rr) != KNOT_EDNS_VERSION) { + qdata->rcode = KNOT_RCODE_BADVERS; + } + + /* Set DO bit if set (DNSSEC requested). */ + if (knot_pkt_has_dnssec(query)) { + knot_edns_set_do(&qdata->opt_rr); + } + + /* Append NSID if requested and available. */ + if (knot_pkt_edns_option(query, KNOT_EDNS_OPTION_NSID) != NULL) { + conf_val_t *nsid = &conf()->cache.srv_nsid; + size_t nsid_len; + const uint8_t *nsid_data = conf_bin(nsid, &nsid_len); + + if (nsid->code != KNOT_EOK) { + ret = knot_edns_add_option(&qdata->opt_rr, + KNOT_EDNS_OPTION_NSID, + strlen(conf()->hostname), + (uint8_t *)conf()->hostname, + qdata->mm); + if (ret != KNOT_EOK) { + return ret; + } + } else if (nsid_len > 0) { + ret = knot_edns_add_option(&qdata->opt_rr, + KNOT_EDNS_OPTION_NSID, + nsid_len, nsid_data, + qdata->mm); + if (ret != KNOT_EOK) { + return ret; + } + } + } + + /* Initialize EDNS Client Subnet if configured and present in query. */ + if (conf()->cache.use_ecs) { + uint8_t *ecs_opt = knot_pkt_edns_option(query, KNOT_EDNS_OPTION_CLIENT_SUBNET); + if (ecs_opt != NULL) { + qdata->ecs = mm_alloc(qdata->mm, sizeof(knot_edns_client_subnet_t)); + if (qdata->ecs == NULL) { + return KNOT_ENOMEM; + } + const uint8_t *ecs_data = knot_edns_opt_get_data(ecs_opt); + uint16_t ecs_len = knot_edns_opt_get_length(ecs_opt); + ret = knot_edns_client_subnet_parse(qdata->ecs, ecs_data, ecs_len); + if (ret != KNOT_EOK) { + qdata->rcode = KNOT_RCODE_FORMERR; + return ret; + } + qdata->ecs->scope_len = 0; + + /* Reserve space for the option in the answer. */ + ret = knot_edns_reserve_option(&qdata->opt_rr, KNOT_EDNS_OPTION_CLIENT_SUBNET, + ecs_len, NULL, qdata->mm); + if (ret != KNOT_EOK) { + return ret; + } + } + } else { + qdata->ecs = NULL; + } + + return answer_edns_reserve(resp, qdata); +} + +static int answer_edns_put(knot_pkt_t *resp, knotd_qdata_t *qdata) +{ + if (knot_rrset_empty(&qdata->opt_rr)) { + return KNOT_EOK; + } + + /* Add ECS if present. */ + int ret = KNOT_EOK; + if (qdata->ecs != NULL) { + uint8_t *ecs_opt = knot_edns_get_option(&qdata->opt_rr, KNOT_EDNS_OPTION_CLIENT_SUBNET); + if (ecs_opt != NULL) { + uint8_t *ecs_data = knot_edns_opt_get_data(ecs_opt); + uint16_t ecs_len = knot_edns_opt_get_length(ecs_opt); + ret = knot_edns_client_subnet_write(ecs_data, ecs_len, qdata->ecs); + if (ret != KNOT_EOK) { + return ret; + } + } + } + + /* Reclaim reserved size. */ + ret = knot_pkt_reclaim(resp, knot_edns_wire_size(&qdata->opt_rr)); + if (ret != KNOT_EOK) { + return ret; + } + + uint8_t *wire_end = resp->wire + resp->size; + + /* Write to packet. */ + assert(resp->current == KNOT_ADDITIONAL); + ret = knot_pkt_put(resp, KNOT_COMPR_HINT_NONE, &qdata->opt_rr, 0); + if (ret == KNOT_EOK) { + /* Save position of the OPT RR. */ + qdata->extra->opt_rr_pos = wire_end; + } + + return ret; +} + +/*! \brief Initialize response, sizes and find zone from which we're going to answer. */ +static int prepare_answer(knot_pkt_t *query, knot_pkt_t *resp, knot_layer_t *ctx) +{ + knotd_qdata_t *qdata = QUERY_DATA(ctx); + server_t *server = qdata->params->server; + + /* Initialize response. */ + int ret = knot_pkt_init_response(resp, query); + if (ret != KNOT_EOK) { + return ret; + } + knot_wire_clear_cd(resp->wire); + + /* Setup EDNS. */ + ret = answer_edns_init(query, resp, qdata); + if (ret != KNOT_EOK || qdata->rcode != 0) { + return KNOT_ERROR; + } + + /* Update maximal answer size. */ + bool has_limit = qdata->params->flags & KNOTD_QUERY_FLAG_LIMIT_SIZE; + if (has_limit) { + resp->max_size = KNOT_WIRE_MIN_PKTSIZE; + if (knot_pkt_has_edns(query)) { + uint16_t server; + switch (qdata->params->remote->ss_family) { + case AF_INET: + server = conf()->cache.srv_max_ipv4_udp_payload; + break; + case AF_INET6: + server = conf()->cache.srv_max_ipv6_udp_payload; + break; + default: + return KNOT_ERROR; + } + uint16_t client = knot_edns_get_payload(query->opt_rr); + uint16_t transfer = MIN(client, server); + resp->max_size = MAX(resp->max_size, transfer); + } + } else { + resp->max_size = KNOT_WIRE_MAX_PKTSIZE; + } + + /* Query MUST carry a question. */ + const knot_dname_t *qname = knot_pkt_qname(query); + if (qname == NULL) { + qdata->rcode = KNOT_RCODE_FORMERR; + return KNOT_EMALF; + } + + /* Convert query QNAME to lowercase, but keep original QNAME case. + * Already checked for absence of compression and length. + */ + memcpy(qdata->extra->orig_qname, qname, query->qname_size); + process_query_qname_case_lower(query); + + /* Find zone for QNAME. */ + qdata->extra->zone = answer_zone_find(query, server->zone_db); + + return KNOT_EOK; +} + +static void set_rcode_to_packet(knot_pkt_t *pkt, knotd_qdata_t *qdata) +{ + uint8_t ext_rcode = KNOT_EDNS_RCODE_HI(qdata->rcode); + + if (ext_rcode != 0) { + /* No OPT RR and Ext RCODE results in SERVFAIL. */ + if (qdata->extra->opt_rr_pos == NULL) { + knot_wire_set_rcode(pkt->wire, KNOT_RCODE_SERVFAIL); + return; + } + + knot_edns_set_ext_rcode_wire(qdata->extra->opt_rr_pos, ext_rcode); + } + + knot_wire_set_rcode(pkt->wire, KNOT_EDNS_RCODE_LO(qdata->rcode)); +} + +static int process_query_err(knot_layer_t *ctx, knot_pkt_t *pkt) +{ + assert(ctx && pkt); + + knotd_qdata_t *qdata = QUERY_DATA(ctx); + + /* Initialize response from query packet. */ + knot_pkt_t *query = qdata->query; + (void)knot_pkt_init_response(pkt, query); + knot_wire_clear_cd(pkt->wire); + + /* Set TC bit if required. */ + if (qdata->err_truncated) { + knot_wire_set_tc(pkt->wire); + } + + /* Restore original QNAME. */ + process_query_qname_case_restore(pkt, qdata); + + /* Move to Additionals to add OPT and TSIG. */ + if (pkt->current != KNOT_ADDITIONAL) { + (void)knot_pkt_begin(pkt, KNOT_ADDITIONAL); + } + + /* Put OPT RR to the additional section. */ + if (answer_edns_reserve(pkt, qdata) != KNOT_EOK || + answer_edns_put(pkt, qdata) != KNOT_EOK) { + qdata->rcode = KNOT_RCODE_FORMERR; + } + + /* Set final RCODE to packet. */ + if (qdata->rcode == KNOT_RCODE_NOERROR) { + /* Default RCODE is SERVFAIL if not otherwise specified. */ + qdata->rcode = KNOT_RCODE_SERVFAIL; + } + set_rcode_to_packet(pkt, qdata); + + /* Transaction security (if applicable). */ + if (process_query_sign_response(pkt, qdata) != KNOT_EOK) { + set_rcode_to_packet(pkt, qdata); + } + + return KNOT_STATE_DONE; +} + +#define PROCESS_BEGIN(plan, step, next_state, qdata) \ + if (plan != NULL) { \ + WALK_LIST(step, plan->stage[KNOTD_STAGE_BEGIN]) { \ + next_state = step->process(next_state, pkt, qdata, step->ctx); \ + if (next_state == KNOT_STATE_FAIL) { \ + goto finish; \ + } \ + } \ + } + +#define PROCESS_END(plan, step, next_state, qdata) \ + if (plan != NULL) { \ + WALK_LIST(step, plan->stage[KNOTD_STAGE_END]) { \ + next_state = step->process(next_state, pkt, qdata, step->ctx); \ + if (next_state == KNOT_STATE_FAIL) { \ + next_state = process_query_err(ctx, pkt); \ + } \ + } \ + } + +static int process_query_out(knot_layer_t *ctx, knot_pkt_t *pkt) +{ + assert(pkt && ctx); + + rcu_read_lock(); + + knotd_qdata_t *qdata = QUERY_DATA(ctx); + struct query_plan *plan = conf()->query_plan; + struct query_plan *zone_plan = NULL; + struct query_step *step = NULL; + + int next_state = KNOT_STATE_PRODUCE; + + /* Check parse state. */ + knot_pkt_t *query = qdata->query; + if (query->parsed < query->size) { + qdata->rcode = KNOT_RCODE_FORMERR; + next_state = KNOT_STATE_FAIL; + goto finish; + } + + /* Preprocessing. */ + if (prepare_answer(query, pkt, ctx) != KNOT_EOK) { + next_state = KNOT_STATE_FAIL; + goto finish; + } + + if (qdata->extra->zone != NULL && qdata->extra->zone->query_plan != NULL) { + zone_plan = qdata->extra->zone->query_plan; + } + + /* Before query processing code. */ + PROCESS_BEGIN(plan, step, next_state, qdata); + PROCESS_BEGIN(zone_plan, step, next_state, qdata); + + /* Answer based on qclass. */ + if (next_state == KNOT_STATE_PRODUCE) { + switch (knot_pkt_qclass(pkt)) { + case KNOT_CLASS_CH: + next_state = query_chaos(pkt, ctx); + break; + case KNOT_CLASS_ANY: + case KNOT_CLASS_IN: + next_state = query_internet(pkt, ctx); + break; + default: + qdata->rcode = KNOT_RCODE_REFUSED; + next_state = KNOT_STATE_FAIL; + break; + } + } + + /* Postprocessing. */ + if (next_state == KNOT_STATE_DONE || next_state == KNOT_STATE_PRODUCE) { + /* Restore original QNAME. */ + process_query_qname_case_restore(pkt, qdata); + + /* Move to Additionals to add OPT and TSIG. */ + if (pkt->current != KNOT_ADDITIONAL) { + (void)knot_pkt_begin(pkt, KNOT_ADDITIONAL); + } + + /* Put OPT RR to the additional section. */ + if (answer_edns_put(pkt, qdata) != KNOT_EOK) { + qdata->rcode = KNOT_RCODE_FORMERR; + next_state = KNOT_STATE_FAIL; + goto finish; + } + + /* Transaction security (if applicable). */ + if (process_query_sign_response(pkt, qdata) != KNOT_EOK) { + next_state = KNOT_STATE_FAIL; + goto finish; + } + } + +finish: + switch (next_state) { + case KNOT_STATE_NOOP: + break; + case KNOT_STATE_FAIL: + /* Error processing. */ + next_state = process_query_err(ctx, pkt); + break; + case KNOT_STATE_FINAL: + /* Just skipped postprocessing. */ + next_state = KNOT_STATE_DONE; + break; + default: + set_rcode_to_packet(pkt, qdata); + } + + /* After query processing code. */ + PROCESS_END(plan, step, next_state, qdata); + PROCESS_END(zone_plan, step, next_state, qdata); + + rcu_read_unlock(); + + return next_state; +} + +bool process_query_acl_check(conf_t *conf, const knot_dname_t *zone_name, + acl_action_t action, knotd_qdata_t *qdata) +{ + knot_pkt_t *query = qdata->query; + const struct sockaddr_storage *query_source = qdata->params->remote; + knot_tsig_key_t tsig = { 0 }; + + /* Skip if already checked and valid. */ + if (qdata->sign.tsig_key.name != NULL) { + return true; + } + + /* Authenticate with NOKEY if the packet isn't signed. */ + if (query->tsig_rr) { + tsig.name = query->tsig_rr->owner; + tsig.algorithm = knot_tsig_rdata_alg(query->tsig_rr); + } + + /* Check if authenticated. */ + conf_val_t acl = conf_zone_get(conf, C_ACL, zone_name); + if (!acl_allowed(conf, &acl, action, query_source, &tsig)) { + char addr_str[SOCKADDR_STRLEN] = { 0 }; + sockaddr_tostr(addr_str, sizeof(addr_str), (struct sockaddr *)query_source); + const knot_lookup_t *act = knot_lookup_by_id((knot_lookup_t *)acl_actions, + action); + char *key_name = knot_dname_to_str_alloc(tsig.name); + + log_zone_debug(zone_name, + "ACL, denied, action %s, remote %s, key %s%s%s", + (act != NULL) ? act->name : "query", + addr_str, + (key_name != NULL) ? "'" : "", + (key_name != NULL) ? key_name : "none", + (key_name != NULL) ? "'" : ""); + free(key_name); + + qdata->rcode = KNOT_RCODE_NOTAUTH; + qdata->rcode_tsig = KNOT_RCODE_BADKEY; + return false; + } + + /* Remember used TSIG key. */ + qdata->sign.tsig_key = tsig; + + return true; +} + +int process_query_verify(knotd_qdata_t *qdata) +{ + knot_pkt_t *query = qdata->query; + knot_sign_context_t *ctx = &qdata->sign; + + /* NOKEY => no verification. */ + if (query->tsig_rr == NULL) { + return KNOT_EOK; + } + + /* Keep digest for signing response. */ + /*! \note This memory will be rewritten for multi-pkt answers. */ + ctx->tsig_digest = (uint8_t *)knot_tsig_rdata_mac(query->tsig_rr); + ctx->tsig_digestlen = knot_tsig_rdata_mac_length(query->tsig_rr); + + /* Checking query. */ + process_query_qname_case_restore(query, qdata); + int ret = knot_tsig_server_check(query->tsig_rr, query->wire, + query->size, &ctx->tsig_key); + process_query_qname_case_lower(query); + + /* Evaluate TSIG check results. */ + switch(ret) { + case KNOT_EOK: + qdata->rcode = KNOT_RCODE_NOERROR; + break; + case KNOT_TSIG_EBADKEY: + qdata->rcode = KNOT_RCODE_NOTAUTH; + qdata->rcode_tsig = KNOT_RCODE_BADKEY; + break; + case KNOT_TSIG_EBADSIG: + qdata->rcode = KNOT_RCODE_NOTAUTH; + qdata->rcode_tsig = KNOT_RCODE_BADSIG; + break; + case KNOT_TSIG_EBADTIME: + qdata->rcode = KNOT_RCODE_NOTAUTH; + qdata->rcode_tsig = KNOT_RCODE_BADTIME; + ctx->tsig_time_signed = knot_tsig_rdata_time_signed(query->tsig_rr); + break; + case KNOT_EMALF: + qdata->rcode = KNOT_RCODE_FORMERR; + break; + default: + qdata->rcode = KNOT_RCODE_SERVFAIL; + break; + } + + /* Log possible error. */ + if (qdata->rcode == KNOT_RCODE_SERVFAIL) { + log_zone_error(qdata->extra->zone->name, + "TSIG, verification failed (%s)", knot_strerror(ret)); + } else if (qdata->rcode != KNOT_RCODE_NOERROR) { + const knot_lookup_t *item = NULL; + if (qdata->rcode_tsig != KNOT_RCODE_NOERROR) { + item = knot_lookup_by_id(knot_tsig_rcode_names, qdata->rcode_tsig); + if (item == NULL) { + item = knot_lookup_by_id(knot_rcode_names, qdata->rcode_tsig); + } + } else { + item = knot_lookup_by_id(knot_rcode_names, qdata->rcode); + } + + char *key_name = knot_dname_to_str_alloc(ctx->tsig_key.name); + log_zone_debug(qdata->extra->zone->name, + "TSIG, key '%s', verification failed '%s'", + (key_name != NULL) ? key_name : "", + (item != NULL) ? item->name : ""); + free(key_name); + } + + return ret; +} + +int process_query_sign_response(knot_pkt_t *pkt, knotd_qdata_t *qdata) +{ + if (pkt->size == 0) { + // Nothing to sign. + return KNOT_EOK; + } + + int ret = KNOT_EOK; + knot_pkt_t *query = qdata->query; + knot_sign_context_t *ctx = &qdata->sign; + + /* KEY provided and verified TSIG or BADTIME allows signing. */ + if (ctx->tsig_key.name != NULL && knot_tsig_can_sign(qdata->rcode_tsig)) { + /* Sign query response. */ + size_t new_digest_len = dnssec_tsig_algorithm_size(ctx->tsig_key.algorithm); + if (ctx->pkt_count == 0) { + ret = knot_tsig_sign(pkt->wire, &pkt->size, pkt->max_size, + ctx->tsig_digest, ctx->tsig_digestlen, + ctx->tsig_digest, &new_digest_len, + &ctx->tsig_key, qdata->rcode_tsig, + ctx->tsig_time_signed); + } else { + ret = knot_tsig_sign_next(pkt->wire, &pkt->size, pkt->max_size, + ctx->tsig_digest, ctx->tsig_digestlen, + ctx->tsig_digest, &new_digest_len, + &ctx->tsig_key, + pkt->wire, pkt->size); + } + if (ret != KNOT_EOK) { + goto fail; /* Failed to sign. */ + } else { + ++ctx->pkt_count; + } + } else { + /* Copy TSIG from query and set RCODE. */ + if (query->tsig_rr && qdata->rcode_tsig != KNOT_RCODE_NOERROR) { + ret = knot_tsig_add(pkt->wire, &pkt->size, pkt->max_size, + qdata->rcode_tsig, query->tsig_rr); + if (ret != KNOT_EOK) { + goto fail; /* Whatever it is, it's server fail. */ + } + } + } + + return KNOT_EOK; + + /* Server failure in signing. */ +fail: + qdata->rcode = KNOT_RCODE_SERVFAIL; + qdata->rcode_tsig = KNOT_RCODE_NOERROR; /* Don't sign again. */ + return ret; +} + +/*! \brief Synthesize RRSIG for given parameters, store in 'qdata' for later use */ +static int put_rrsig(const knot_dname_t *sig_owner, uint16_t type, + const knot_rrset_t *rrsigs, knot_rrinfo_t *rrinfo, + knotd_qdata_t *qdata) +{ + knot_rdataset_t synth_rrs; + knot_rdataset_init(&synth_rrs); + int ret = knot_synth_rrsig(type, &rrsigs->rrs, &synth_rrs, qdata->mm); + if (ret == KNOT_ENOENT) { + // No signature + return KNOT_EOK; + } + if (ret != KNOT_EOK) { + return ret; + } + + /* Create rrsig info structure. */ + struct rrsig_info *info = mm_alloc(qdata->mm, sizeof(struct rrsig_info)); + if (info == NULL) { + knot_rdataset_clear(&synth_rrs, qdata->mm); + return KNOT_ENOMEM; + } + + /* Store RRSIG into info structure. */ + knot_dname_t *owner_copy = knot_dname_copy(sig_owner, qdata->mm); + if (owner_copy == NULL) { + mm_free(qdata->mm, info); + knot_rdataset_clear(&synth_rrs, qdata->mm); + return KNOT_ENOMEM; + } + knot_rrset_init(&info->synth_rrsig, owner_copy, rrsigs->type, + rrsigs->rclass, rrsigs->ttl); + /* Store filtered signature. */ + info->synth_rrsig.rrs = synth_rrs; + + info->rrinfo = rrinfo; + add_tail(&qdata->extra->rrsigs, &info->n); + + return KNOT_EOK; +} + +int process_query_put_rr(knot_pkt_t *pkt, knotd_qdata_t *qdata, + const knot_rrset_t *rr, const knot_rrset_t *rrsigs, + uint16_t compr_hint, uint32_t flags) +{ + if (rr->rrs.count < 1) { + return KNOT_EMALF; + } + + /* Wildcard expansion applies only for answers. */ + bool expand = false; + if (pkt->current == KNOT_ANSWER) { + /* Expand if RR is wildcard & we didn't query for wildcard. */ + expand = (knot_dname_is_wildcard(rr->owner) && !knot_dname_is_wildcard(qdata->name)); + } + + int ret = KNOT_EOK; + + /* If we already have compressed name on the wire and compression hint, + * we can just insert RRSet and fake synthesis by using compression + * hint. */ + knot_rrset_t to_add; + if (compr_hint == KNOT_COMPR_HINT_NONE && expand) { + knot_dname_t *qname_cpy = knot_dname_copy(qdata->name, &pkt->mm); + if (qname_cpy == NULL) { + return KNOT_ENOMEM; + } + knot_rrset_init(&to_add, qname_cpy, rr->type, rr->rclass, rr->ttl); + ret = knot_rdataset_copy(&to_add.rrs, &rr->rrs, &pkt->mm); + if (ret != KNOT_EOK) { + knot_dname_free(qname_cpy, &pkt->mm); + return ret; + } + to_add.additional = rr->additional; + flags |= KNOT_PF_FREE; + } else { + to_add = *rr; + } + + uint16_t rotate = conf()->cache.srv_ans_rotate ? knot_wire_get_id(qdata->query->wire) : 0; + uint16_t prev_count = pkt->rrset_count; + ret = knot_pkt_put_rotate(pkt, compr_hint, &to_add, rotate, flags); + if (ret != KNOT_EOK && (flags & KNOT_PF_FREE)) { + knot_rrset_clear(&to_add, &pkt->mm); + return ret; + } + + const bool inserted = (prev_count != pkt->rrset_count); + if (inserted && + !knot_rrset_empty(rrsigs) && rr->type != KNOT_RRTYPE_RRSIG) { + // Get rrinfo of just inserted RR. + knot_rrinfo_t *rrinfo = &pkt->rr_info[pkt->rrset_count - 1]; + ret = put_rrsig(rr->owner, rr->type, rrsigs, rrinfo, qdata); + } + + return ret; +} + +/*! \brief Module implementation. */ +const knot_layer_api_t *process_query_layer(void) +{ + static const knot_layer_api_t api = { + .begin = &process_query_begin, + .reset = &process_query_reset, + .finish = &process_query_finish, + .consume = &process_query_in, + .produce = &process_query_out, + }; + return &api; +} diff --git a/src/knot/nameserver/process_query.h b/src/knot/nameserver/process_query.h new file mode 100644 index 0000000..c91a7be --- /dev/null +++ b/src/knot/nameserver/process_query.h @@ -0,0 +1,133 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "knot/include/module.h" +#include "knot/query/layer.h" +#include "knot/updates/acl.h" +#include "knot/zone/zone.h" + +/* Query processing module implementation. */ +const knot_layer_api_t *process_query_layer(void); + +/*! \brief Query processing intermediate data. */ +typedef struct knotd_qdata_extra { + const zone_t *zone; /*!< Zone from which is answered. */ + list_t wildcards; /*!< Visited wildcards. */ + list_t rrsigs; /*!< Section RRSIGs. */ + uint8_t *opt_rr_pos; /*!< Place of the OPT RR in wire. */ + + /* Currently processed nodes. */ + const zone_node_t *node, *encloser, *previous; + + /* Original QNAME case. */ + uint8_t orig_qname[KNOT_DNAME_MAXLEN]; + + /* Extensions. */ + void *ext; + void (*ext_cleanup)(knotd_qdata_t *); /*!< Extensions cleanup callback. */ +} knotd_qdata_extra_t; + +/*! \brief Visited wildcard node list. */ +struct wildcard_hit { + node_t n; + const zone_node_t *node; /* Visited node. */ + const zone_node_t *prev; /* Previous node from the SNAME. */ + const knot_dname_t *sname; /* Name leading to this node. */ +}; + +/*! \brief RRSIG info node list. */ +struct rrsig_info { + node_t n; + knot_rrset_t synth_rrsig; /* Synthesized RRSIG. */ + knot_rrinfo_t *rrinfo; /* RR info. */ +}; + +/*! + * \brief Check current query against ACL. + * + * \param conf Configuration. + * \param zone_name Current zone name. + * \param action ACL action. + * \param qdata Query data. + * \return true if accepted, false if denied. + */ +bool process_query_acl_check(conf_t *conf, const knot_dname_t *zone_name, + acl_action_t action, knotd_qdata_t *qdata); + +/*! + * \brief Verify current query transaction security and update query data. + * + * \param qdata + * \retval KNOT_EOK + * \retval KNOT_TSIG_EBADKEY + * \retval KNOT_TSIG_EBADSIG + * \retval KNOT_TSIG_EBADTIME + * \retval (other generic errors) + */ +int process_query_verify(knotd_qdata_t *qdata); + +/*! + * \brief Sign current query using configured TSIG keys. + * + * \param pkt Outgoing message. + * \param qdata Query data. + * + * \retval KNOT_E* + */ +int process_query_sign_response(knot_pkt_t *pkt, knotd_qdata_t *qdata); + +/*! + * \brief Restore QNAME letter case. + * + * \param pkt Incoming message. + * \param qdata Query data. + */ +static inline void process_query_qname_case_restore(knot_pkt_t *pkt, knotd_qdata_t *qdata) +{ + // If original QNAME is empty, query is either unparsed or for root domain. + if (qdata->extra->orig_qname[0] != '\0') { + memcpy(pkt->wire + KNOT_WIRE_HEADER_SIZE, + qdata->extra->orig_qname, qdata->query->qname_size); + } +} + +/*! + * \brief Convert QNAME to lowercase format for processing. + * + * \param pkt Incoming message. + */ +static inline void process_query_qname_case_lower(knot_pkt_t *pkt) +{ + knot_dname_to_lower(knot_pkt_qname(pkt)); +} + +/*! + * \brief Puts RRSet to packet, will store its RRSIG for later use. + * + * \param pkt Packet to store RRSet into. + * \param qdata Query data structure. + * \param rr RRSet to be stored. + * \param rrsigs RRSIGs to be stored. + * \param compr_hint Compression hint. + * \param flags Flags. + * + * \return KNOT_E* + */ +int process_query_put_rr(knot_pkt_t *pkt, knotd_qdata_t *qdata, + const knot_rrset_t *rr, const knot_rrset_t *rrsigs, + uint16_t compr_hint, uint32_t flags); diff --git a/src/knot/nameserver/query_module.c b/src/knot/nameserver/query_module.c new file mode 100644 index 0000000..de85bbc --- /dev/null +++ b/src/knot/nameserver/query_module.c @@ -0,0 +1,641 @@ +/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "contrib/sockaddr.h" +#include "libknot/attribute.h" +#include "knot/common/log.h" +#include "knot/conf/module.h" +#include "knot/conf/tools.h" +#include "knot/dnssec/rrset-sign.h" +#include "knot/dnssec/zone-sign.h" +#include "knot/nameserver/query_module.h" +#include "knot/nameserver/process_query.h" + +#ifdef HAVE_ATOMIC + #define ATOMIC_ADD(dst, val) __atomic_add_fetch(&(dst), (val), __ATOMIC_RELAXED) + #define ATOMIC_SUB(dst, val) __atomic_sub_fetch(&(dst), (val), __ATOMIC_RELAXED) + #define ATOMIC_SET(dst, val) __atomic_store_n(&(dst), (val), __ATOMIC_RELAXED) +#else + #warning "Statistics data can be inaccurate if configured with multiple udp/tcp workers" + #define ATOMIC_ADD(dst, val) ((dst) += (val)) + #define ATOMIC_SUB(dst, val) ((dst) -= (val)) + #define ATOMIC_SET(dst, val) ((dst) = (val)) +#endif + +_public_ +int knotd_conf_check_ref(knotd_conf_check_args_t *args) +{ + return check_ref(args); +} + +struct query_plan *query_plan_create(void) +{ + struct query_plan *plan = malloc(sizeof(struct query_plan)); + if (plan == NULL) { + return NULL; + } + + for (unsigned i = 0; i < KNOTD_STAGES; ++i) { + init_list(&plan->stage[i]); + } + + return plan; +} + +void query_plan_free(struct query_plan *plan) +{ + if (plan == NULL) { + return; + } + + for (unsigned i = 0; i < KNOTD_STAGES; ++i) { + struct query_step *step = NULL, *next = NULL; + WALK_LIST_DELSAFE(step, next, plan->stage[i]) { + free(step); + } + } + + free(plan); +} + +static struct query_step *make_step(query_step_process_f process, void *ctx) +{ + struct query_step *step = calloc(1, sizeof(struct query_step)); + if (step == NULL) { + return NULL; + } + + step->process = process; + step->ctx = ctx; + + return step; +} + +int query_plan_step(struct query_plan *plan, knotd_stage_t stage, + query_step_process_f process, void *ctx) +{ + struct query_step *step = make_step(process, ctx); + if (step == NULL) { + return KNOT_ENOMEM; + } + + add_tail(&plan->stage[stage], &step->node); + + return KNOT_EOK; +} + +_public_ +int knotd_mod_hook(knotd_mod_t *mod, knotd_stage_t stage, knotd_mod_hook_f hook) +{ + if (stage != KNOTD_STAGE_BEGIN && stage != KNOTD_STAGE_END) { + return KNOT_EINVAL; + } + + return query_plan_step(mod->plan, stage, hook, mod); +} + +_public_ +int knotd_mod_in_hook(knotd_mod_t *mod, knotd_stage_t stage, knotd_mod_in_hook_f hook) +{ + if (stage == KNOTD_STAGE_BEGIN || stage == KNOTD_STAGE_END) { + return KNOT_EINVAL; + } + + return query_plan_step(mod->plan, stage, hook, mod); +} + +knotd_mod_t *query_module_open(conf_t *conf, conf_mod_id_t *mod_id, + struct query_plan *plan, const knot_dname_t *zone) +{ + if (conf == NULL || mod_id == NULL || plan == NULL) { + return NULL; + } + + /* Locate the module. */ + const module_t *mod = conf_mod_find(conf, mod_id->name + 1, + mod_id->name[0], false); + if (mod == NULL) { + return NULL; + } + + /* Create query module. */ + knotd_mod_t *module = calloc(1, sizeof(knotd_mod_t)); + if (module == NULL) { + return NULL; + } + + module->plan = plan; + module->config = conf; + module->zone = zone; + module->id = mod_id; + module->api = mod->api; + + return module; +} + +void query_module_close(knotd_mod_t *module) +{ + if (module == NULL) { + return; + } + + knotd_mod_stats_free(module); + conf_free_mod_id(module->id); + + zone_sign_ctx_free(module->sign_ctx); + free_zone_keys(module->keyset); + free(module->keyset); + kdnssec_ctx_deinit(module->dnssec); + free(module->dnssec); + + free(module); +} + +_public_ +void *knotd_mod_ctx(knotd_mod_t *mod) +{ + return (mod != NULL) ? mod->ctx : NULL; +} + +_public_ +void knotd_mod_ctx_set(knotd_mod_t *mod, void *ctx) +{ + if (mod != NULL) mod->ctx = ctx; +} + +_public_ +const knot_dname_t *knotd_mod_zone(knotd_mod_t *mod) +{ + return (mod != NULL) ? mod->zone : NULL; +} + +_public_ +void knotd_mod_log(knotd_mod_t *mod, int priority, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + knotd_mod_vlog(mod, priority, fmt, args); + va_end(args); +} + +_public_ +void knotd_mod_vlog(knotd_mod_t *mod, int priority, const char *fmt, va_list args) +{ + if (mod == NULL || fmt == NULL) { + return; + } + + char msg[512]; + + if (vsnprintf(msg, sizeof(msg), fmt, args) < 0) { + msg[0] = '\0'; + } + + #define LOG_ARGS(mod_id, msg) "module '%s%s%.*s', %s", \ + mod_id->name + 1, (mod_id->len > 0) ? "/" : "", (int)mod_id->len, \ + mod_id->data, msg + + if (mod->zone == NULL) { + log_fmt(priority, LOG_SOURCE_SERVER, LOG_ARGS(mod->id, msg)); + } else { + log_fmt_zone(priority, LOG_SOURCE_ZONE, mod->zone, NULL, + LOG_ARGS(mod->id, msg)); + } + + #undef LOG_ARGS +} + +_public_ +int knotd_mod_stats_add(knotd_mod_t *mod, const char *ctr_name, uint32_t idx_count, + knotd_mod_idx_to_str_f idx_to_str) +{ + if (mod == NULL || idx_count == 0) { + return KNOT_EINVAL; + } + + mod_ctr_t *stats = NULL; + if (mod->stats == NULL) { + assert(mod->stats_count == 0); + stats = malloc(sizeof(*stats)); + if (stats == NULL) { + return KNOT_ENOMEM; + } + mod->stats = stats; + } else { + assert(mod->stats_count > 0); + size_t old_size = mod->stats_count * sizeof(*stats); + size_t new_size = old_size + sizeof(*stats); + stats = realloc(mod->stats, new_size); + if (stats == NULL) { + knotd_mod_stats_free(mod); + return KNOT_ENOMEM; + } + mod->stats = stats; + stats += mod->stats_count; + } + + mod->stats_count++; + + if (idx_count == 1) { + stats->counter = 0; + } else { + size_t size = idx_count * sizeof(((mod_ctr_t *)0)->counter); + stats->counters = calloc(1, size); + if (stats->counters == NULL) { + knotd_mod_stats_free(mod); + return KNOT_ENOMEM; + } + stats->idx_to_str = idx_to_str; + } + stats->name = ctr_name; + stats->count = idx_count; + + return KNOT_EOK; +} + +_public_ +void knotd_mod_stats_free(knotd_mod_t *mod) +{ + if (mod == NULL || mod->stats == NULL) { + return; + } + + for (int i = 0; i < mod->stats_count; i++) { + if (mod->stats[i].count > 1) { + free(mod->stats[i].counters); + } + } + + free(mod->stats); +} + +#define STATS_BODY(OPERATION) { \ + if (mod == NULL) return; \ + \ + mod_ctr_t *ctr = mod->stats + ctr_id; \ + if (ctr->count == 1) { \ + assert(idx == 0); \ + OPERATION(ctr->counter, val); \ + } else { \ + assert(idx < ctr->count); \ + OPERATION(ctr->counters[idx], val); \ + } \ +} + +_public_ +void knotd_mod_stats_incr(knotd_mod_t *mod, uint32_t ctr_id, uint32_t idx, uint64_t val) +{ + STATS_BODY(ATOMIC_ADD) +} + +_public_ +void knotd_mod_stats_decr(knotd_mod_t *mod, uint32_t ctr_id, uint32_t idx, uint64_t val) +{ + STATS_BODY(ATOMIC_SUB) +} + +_public_ +void knotd_mod_stats_store(knotd_mod_t *mod, uint32_t ctr_id, uint32_t idx, uint64_t val) +{ + STATS_BODY(ATOMIC_SET) +} + +_public_ +knotd_conf_t knotd_conf_env(knotd_mod_t *mod, knotd_conf_env_t env) +{ + static const char *version = "Knot DNS " PACKAGE_VERSION; + + knotd_conf_t out = { { 0 } }; + + if (mod == NULL) { + return out; + } + + conf_t *config = (mod->config != NULL) ? mod->config : conf(); + + switch (env) { + case KNOTD_CONF_ENV_VERSION: + out.single.string = version; + break; + case KNOTD_CONF_ENV_HOSTNAME: + out.single.string = config->hostname; + break; + case KNOTD_CONF_ENV_WORKERS_UDP: + out.single.integer = conf_udp_threads(config); + break; + case KNOTD_CONF_ENV_WORKERS_TCP: + out.single.integer = conf_tcp_threads(config); + break; + default: + return out; + } + + out.count = 1; + + return out; +} + +static void set_val(yp_type_t type, knotd_conf_val_t *item, conf_val_t *val) +{ + switch (type) { + case YP_TINT: + item->integer = conf_int(val); + break; + case YP_TBOOL: + item->boolean = conf_bool(val); + break; + case YP_TOPT: + item->option = conf_opt(val); + break; + case YP_TSTR: + item->string = conf_str(val); + break; + case YP_TDNAME: + item->dname = conf_dname(val); + break; + case YP_TADDR: + item->addr = conf_addr(val, NULL); + break; + case YP_TNET: + item->addr = conf_addr_range(val, &item->addr_max, + &item->addr_mask); + break; + case YP_TREF: + if (val->code == KNOT_EOK) { + conf_val(val); + item->data_len = val->len; + item->data = val->data; + } + break; + case YP_THEX: + case YP_TB64: + item->data = conf_bin(val, &item->data_len); + break; + case YP_TDATA: + item->data = conf_data(val, &item->data_len); + break; + default: + return; + } +} + +static void set_conf_out(knotd_conf_t *out, conf_val_t *val) +{ + if (!(val->item->flags & YP_FMULTI)) { + out->count = (val->code == KNOT_EOK) ? 1 : 0; + set_val(val->item->type, &out->single, val); + } else { + size_t count = conf_val_count(val); + if (count == 0) { + return; + } + + out->multi = malloc(count * sizeof(*out->multi)); + if (out->multi == NULL) { + return; + } + memset(out->multi, 0, count * sizeof(*out->multi)); + + for (size_t i = 0; i < count; i++) { + set_val(val->item->type, &out->multi[i], val); + conf_val_next(val); + } + out->count = count; + } +} + +_public_ +knotd_conf_t knotd_conf(knotd_mod_t *mod, const yp_name_t *section_name, + const yp_name_t *item_name, const knotd_conf_t *id) +{ + knotd_conf_t out = { { 0 } }; + + if (mod == NULL || section_name == NULL || item_name == NULL) { + return out; + } + + conf_t *config = (mod->config != NULL) ? mod->config : conf(); + + const uint8_t *raw_id = (id != NULL) ? id->single.data : NULL; + size_t raw_id_len = (id != NULL) ? id->single.data_len : 0; + conf_val_t val = conf_rawid_get(config, section_name, item_name, + raw_id, raw_id_len); + + set_conf_out(&out, &val); + + return out; +} + +_public_ +knotd_conf_t knotd_conf_mod(knotd_mod_t *mod, const yp_name_t *item_name) +{ + knotd_conf_t out = { { 0 } }; + + if (mod == NULL || item_name == NULL) { + return out; + } + + conf_t *config = (mod->config != NULL) ? mod->config : conf(); + + conf_val_t val = conf_mod_get(config, item_name, mod->id); + if (val.item == NULL) { + return out; + } + + set_conf_out(&out, &val); + + return out; +} + +_public_ +knotd_conf_t knotd_conf_zone(knotd_mod_t *mod, const yp_name_t *item_name, + const knot_dname_t *zone) +{ + knotd_conf_t out = { { 0 } }; + + if (mod == NULL || item_name == NULL || zone == NULL) { + return out; + } + + conf_t *config = (mod->config != NULL) ? mod->config : conf(); + + conf_val_t val = conf_zone_get(config, item_name, zone); + + set_conf_out(&out, &val); + + return out; +} + +_public_ +knotd_conf_t knotd_conf_check_item(knotd_conf_check_args_t *args, + const yp_name_t *item_name) +{ + knotd_conf_t out = { { 0 } }; + + conf_val_t val = conf_rawid_get_txn(args->extra->conf, args->extra->txn, + args->item->name, item_name, + args->id, args->id_len); + + set_conf_out(&out, &val); + + return out; +} + +_public_ +bool knotd_conf_addr_range_match(const knotd_conf_t *range, + const struct sockaddr_storage *addr) +{ + if (range == NULL || addr == NULL) { + return false; + } + + for (size_t i = 0; i < range->count; i++) { + knotd_conf_val_t *val = &range->multi[i]; + if (val->addr_max.ss_family == AF_UNSPEC) { + if (sockaddr_net_match((struct sockaddr *)addr, + (struct sockaddr *)&val->addr, + val->addr_mask)) { + return true; + } + } else { + if (sockaddr_range_match((struct sockaddr *)addr, + (struct sockaddr *)&val->addr, + (struct sockaddr *)&val->addr_max)) { + return true; + } + } + } + + return false; +} + +_public_ +void knotd_conf_free(knotd_conf_t *conf) +{ + if (conf == NULL) { + return; + } + + if (conf->count > 0 && conf->multi != NULL) { + memset(conf->multi, 0, conf->count * sizeof(*conf->multi)); + free(conf->multi); + } + memset(conf, 0, sizeof(*conf)); +} + +_public_ +const knot_dname_t *knotd_qdata_zone_name(knotd_qdata_t *qdata) +{ + if (qdata == NULL || qdata->extra->zone == NULL) { + return NULL; + } + + return qdata->extra->zone->name; +} + +_public_ +knot_rrset_t knotd_qdata_zone_apex_rrset(knotd_qdata_t *qdata, uint16_t type) +{ + if (qdata == NULL || qdata->extra->zone == NULL || + qdata->extra->zone->contents == NULL) { + return node_rrset(NULL, type); + } + + return node_rrset(qdata->extra->zone->contents->apex, type); +} + +_public_ +int knotd_mod_dnssec_init(knotd_mod_t *mod) +{ + if (mod == NULL) { + return KNOT_EINVAL; + } + + mod->dnssec = calloc(1, sizeof(*(mod->dnssec))); + if (mod->dnssec == NULL) { + return KNOT_ENOMEM; + } + + conf_val_t conf = conf_zone_get(mod->config, C_DNSSEC_SIGNING, mod->zone); + int ret = kdnssec_ctx_init(mod->config, mod->dnssec, mod->zone, + conf_bool(&conf) ? NULL : mod->id); + if (ret != KNOT_EOK) { + free(mod->dnssec); + return ret; + } + + return KNOT_EOK; +} + +_public_ +int knotd_mod_dnssec_load_keyset(knotd_mod_t *mod, bool verbose) +{ + if (mod == NULL || mod->dnssec == NULL) { + return KNOT_EINVAL; + } + + mod->keyset = calloc(1, sizeof(*(mod->keyset))); + if (mod->keyset == NULL) { + return KNOT_ENOMEM; + } + + int ret = load_zone_keys(mod->dnssec, mod->keyset, verbose); + if (ret != KNOT_EOK) { + free(mod->keyset); + mod->keyset = NULL; + return ret; + } + + mod->sign_ctx = zone_sign_ctx(mod->keyset, mod->dnssec); + if (mod->sign_ctx == NULL) { + free_zone_keys(mod->keyset); + free(mod->keyset); + mod->keyset = NULL; + return KNOT_ENOMEM; + } + + return KNOT_EOK; +} + +_public_ +void knotd_mod_dnssec_unload_keyset(knotd_mod_t *mod) +{ + if (mod != NULL && mod->keyset != NULL) { + zone_sign_ctx_free(mod->sign_ctx); + mod->sign_ctx = NULL; + + free_zone_keys(mod->keyset); + free(mod->keyset); + mod->keyset = NULL; + } +} + +_public_ +int knotd_mod_dnssec_sign_rrset(knotd_mod_t *mod, knot_rrset_t *rrsigs, + const knot_rrset_t *rrset, knot_mm_t *mm) +{ + if (mod == NULL || rrsigs == NULL || rrset == NULL) { + return KNOT_EINVAL; + } + + return knot_sign_rrset2(rrsigs, rrset, mod->sign_ctx, mm); +} diff --git a/src/knot/nameserver/query_module.h b/src/knot/nameserver/query_module.h new file mode 100644 index 0000000..3d2e867 --- /dev/null +++ b/src/knot/nameserver/query_module.h @@ -0,0 +1,98 @@ +/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "libknot/libknot.h" +#include "knot/conf/conf.h" +#include "knot/dnssec/context.h" +#include "knot/dnssec/zone-keys.h" +#include "knot/include/module.h" +#include "contrib/ucw/lists.h" + +#ifdef HAVE_ATOMIC + #define ATOMIC_GET(src) __atomic_load_n(&(src), __ATOMIC_RELAXED) +#else + #define ATOMIC_GET(src) (src) +#endif + +#define KNOTD_STAGES (KNOTD_STAGE_END + 1) + +typedef unsigned (*query_step_process_f) + (unsigned state, knot_pkt_t *pkt, knotd_qdata_t *qdata, knotd_mod_t *mod); + +/*! \brief Single processing step in query processing. */ +struct query_step { + node_t node; + void *ctx; + query_step_process_f process; +}; + +/*! Query plan represents a sequence of steps needed for query processing + * divided into several stages, where each stage represents a current response + * assembly phase, for example 'before processing', 'answer section' and so on. + */ +struct query_plan { + list_t stage[KNOTD_STAGES]; +}; + +/*! \brief Create an empty query plan. */ +struct query_plan *query_plan_create(void); + +/*! \brief Free query plan and all planned steps. */ +void query_plan_free(struct query_plan *plan); + +/*! \brief Plan another step for given stage. */ +int query_plan_step(struct query_plan *plan, knotd_stage_t stage, + query_step_process_f process, void *ctx); + +/*! \brief Open query module identified by name. */ +knotd_mod_t *query_module_open(conf_t *conf, conf_mod_id_t *mod_id, + struct query_plan *plan, const knot_dname_t *zone); + +/*! \brief Close query module. */ +void query_module_close(knotd_mod_t *module); + +typedef char* (*mod_idx_to_str_f)(uint32_t idx, uint32_t count); + +typedef struct { + const char *name; + union { + uint64_t counter; + struct { + uint64_t *counters; + mod_idx_to_str_f idx_to_str; + }; + }; + uint32_t count; +} mod_ctr_t; + +struct knotd_mod { + node_t node; + conf_t *config; + conf_mod_id_t *id; + struct query_plan *plan; + const knot_dname_t *zone; + const knotd_mod_api_t *api; + kdnssec_ctx_t *dnssec; + zone_keyset_t *keyset; + zone_sign_ctx_t *sign_ctx; + mod_ctr_t *stats; + uint32_t stats_count; + void *ctx; +}; + +void knotd_mod_stats_free(knotd_mod_t *mod); diff --git a/src/knot/nameserver/tsig_ctx.c b/src/knot/nameserver/tsig_ctx.c new file mode 100644 index 0000000..8b4f9b7 --- /dev/null +++ b/src/knot/nameserver/tsig_ctx.c @@ -0,0 +1,188 @@ +/* Copyright (C) 2015 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> + +#include "knot/nameserver/tsig_ctx.h" +#include "libknot/libknot.h" + +/*! + * Maximal total size for unsigned messages. + */ +static const size_t TSIG_BUFFER_MAX_SIZE = (UINT16_MAX * 100); + +void tsig_init(tsig_ctx_t *ctx, const knot_tsig_key_t *key) +{ + if (!ctx) { + return; + } + + memset(ctx, 0, sizeof(*ctx)); + ctx->key = key; +} + +void tsig_cleanup(tsig_ctx_t *ctx) +{ + if (!ctx) { + return; + } + + free(ctx->buffer); + memset(ctx, 0, sizeof(*ctx)); +} + +void tsig_reset(tsig_ctx_t *ctx) +{ + if (!ctx) { + return; + } + + const knot_tsig_key_t *backup = ctx->key; + tsig_cleanup(ctx); + tsig_init(ctx, backup); +} + +int tsig_sign_packet(tsig_ctx_t *ctx, knot_pkt_t *packet) +{ + if (!ctx || !packet) { + return KNOT_EINVAL; + } + + if (ctx->key == NULL) { + return KNOT_EOK; + } + + int ret = KNOT_ERROR; + if (ctx->digest_size == 0) { + ctx->digest_size = dnssec_tsig_algorithm_size(ctx->key->algorithm); + ret = knot_tsig_sign(packet->wire, &packet->size, packet->max_size, + NULL, 0, + ctx->digest, &ctx->digest_size, + ctx->key, 0, 0); + } else { + uint8_t previous_digest[ctx->digest_size]; + memcpy(previous_digest, ctx->digest, ctx->digest_size); + + ret = knot_tsig_sign_next(packet->wire, &packet->size, packet->max_size, + previous_digest, ctx->digest_size, + ctx->digest, &ctx->digest_size, + ctx->key, packet->wire, packet->size); + } + + return ret; +} + +static int update_ctx_after_verify(tsig_ctx_t *ctx, knot_rrset_t *tsig_rr) +{ + assert(ctx); + assert(tsig_rr); + + if (ctx->digest_size != knot_tsig_rdata_mac_length(tsig_rr)) { + return KNOT_EMALF; + } + + memcpy(ctx->digest, knot_tsig_rdata_mac(tsig_rr), ctx->digest_size); + ctx->prev_signed_time = knot_tsig_rdata_time_signed(tsig_rr); + ctx->unsigned_count = 0; + ctx->buffer_used = 0; + + return KNOT_EOK; +} + +static int buffer_add_packet(tsig_ctx_t *ctx, knot_pkt_t *packet) +{ + size_t need = ctx->buffer_used + packet->size; + + // Inflate the buffer if necessary. + + if (need > TSIG_BUFFER_MAX_SIZE) { + return KNOT_ENOMEM; + } + + if (need > ctx->buffer_size) { + uint8_t *buffer = realloc(ctx->buffer, need); + if (!buffer) { + return KNOT_ENOMEM; + } + + ctx->buffer = buffer; + ctx->buffer_size = need; + } + + // Buffer the packet. + + uint8_t *write = ctx->buffer + ctx->buffer_used; + memcpy(write, packet->wire, packet->size); + ctx->buffer_used = need; + + return KNOT_EOK; +} + +int tsig_verify_packet(tsig_ctx_t *ctx, knot_pkt_t *packet) +{ + if (!ctx || !packet) { + return KNOT_EINVAL; + } + + if (ctx->key == NULL) { + return KNOT_EOK; + } + + int ret = buffer_add_packet(ctx, packet); + if (ret != KNOT_EOK) { + return ret; + } + + // Unsigned packet. + + if (packet->tsig_rr == NULL) { + ctx->unsigned_count += 1; + return KNOT_EOK; + } + + // Signed packet. + + if (ctx->prev_signed_time == 0) { + ret = knot_tsig_client_check(packet->tsig_rr, ctx->buffer, + ctx->buffer_used, ctx->digest, + ctx->digest_size, ctx->key, 0); + } else { + ret = knot_tsig_client_check_next(packet->tsig_rr, ctx->buffer, + ctx->buffer_used, ctx->digest, + ctx->digest_size, ctx->key, + ctx->prev_signed_time); + } + + if (ret != KNOT_EOK) { + return ret; + } + + ret = update_ctx_after_verify(ctx, packet->tsig_rr); + if (ret != KNOT_EOK) { + return ret; + } + + return KNOT_EOK; +} + +unsigned tsig_unsigned_count(tsig_ctx_t *ctx) +{ + if (!ctx) { + return -1; + } + + return ctx->unsigned_count; +} diff --git a/src/knot/nameserver/tsig_ctx.h b/src/knot/nameserver/tsig_ctx.h new file mode 100644 index 0000000..256ada3 --- /dev/null +++ b/src/knot/nameserver/tsig_ctx.h @@ -0,0 +1,97 @@ +/* Copyright (C) 2015 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <stdint.h> + +#include "libknot/packet/pkt.h" +#include "libknot/tsig.h" + +#define TSIG_MAX_DIGEST_SIZE 64 + +/*! + \brief TSIG context. + */ +typedef struct tsig_ctx { + const knot_tsig_key_t *key; + uint64_t prev_signed_time; + + uint8_t digest[TSIG_MAX_DIGEST_SIZE]; + size_t digest_size; + + /* Unsigned packets handling. */ + unsigned unsigned_count; + uint8_t *buffer; + size_t buffer_used; + size_t buffer_size; +} tsig_ctx_t; + +/*! + * \brief Initialize TSIG context. + * + * \param ctx TSIG context to be initialized. + * \param key Key to be used for signing. If NULL, all performed operations + * will do nothing and always successful. + */ +void tsig_init(tsig_ctx_t *ctx, const knot_tsig_key_t *key); + +/*! + * \brief Cleanup TSIG context. + * + * \param ctx TSIG context to be cleaned up. + */ +void tsig_cleanup(tsig_ctx_t *ctx); + +/*! + * \brief Reset TSIG context for new message exchange. + */ +void tsig_reset(tsig_ctx_t *ctx); + +/*! + * \brief Sign outgoing packet. + * + * \param ctx TSIG signing context. + * \param packet Packet to be signed. + * + * \return Error code, KNOT_EOK if successful. + */ +int tsig_sign_packet(tsig_ctx_t *ctx, knot_pkt_t *packet); + +/*! + * \brief Verify incoming packet. + * + * If the packet is not signed, the function will succeed, but an internal + * counter of unsigned packets is increased. When a packet is signed, the + * same counter is reset to zero. + * + * \see tsig_unsigned_count + * + * \param ctx TSIG signing context. + * \param packet Packet to be verified. + * + * \return Error code, KNOT_EOK if successful. + */ +int tsig_verify_packet(tsig_ctx_t *ctx, knot_pkt_t *packet); + +/*! + * \brief Get number of unsigned packets since the last signed one. + * + * \param ctx TSIG signing context. + * + * \return Number of unsigned packets since the last signed one. + */ +unsigned tsig_unsigned_count(tsig_ctx_t *ctx); diff --git a/src/knot/nameserver/update.c b/src/knot/nameserver/update.c new file mode 100644 index 0000000..a9d0475 --- /dev/null +++ b/src/knot/nameserver/update.c @@ -0,0 +1,477 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <sys/socket.h> + +#include "libdnssec/random.h" +#include "knot/common/log.h" +#include "knot/dnssec/zone-events.h" +#include "knot/events/handlers.h" +#include "knot/query/capture.h" +#include "knot/query/requestor.h" +#include "knot/nameserver/update.h" +#include "knot/nameserver/internet.h" +#include "knot/nameserver/process_query.h" +#include "knot/nameserver/log.h" +#include "knot/updates/ddns.h" +#include "knot/updates/apply.h" +#include "knot/events/events.h" +#include "libknot/libknot.h" +#include "contrib/net.h" +#include "contrib/time.h" + +#define UPDATE_LOG(priority, qdata, fmt...) \ + ns_log(priority, knot_pkt_qname(qdata->query), LOG_OPERATION_UPDATE, \ + LOG_DIRECTION_IN, (struct sockaddr *)qdata->params->remote, fmt) + +static void init_qdata_from_request(knotd_qdata_t *qdata, + const zone_t *zone, + struct knot_request *req, + knotd_qdata_params_t *params, + knotd_qdata_extra_t *extra) +{ + memset(qdata, 0, sizeof(*qdata)); + qdata->params = params; + qdata->query = req->query; + qdata->sign = req->sign; + qdata->extra = extra; + memset(extra, 0, sizeof(*extra)); + qdata->extra->zone = zone; +} + +static int check_prereqs(struct knot_request *request, + const zone_t *zone, zone_update_t *update, + knotd_qdata_t *qdata) +{ + uint16_t rcode = KNOT_RCODE_NOERROR; + int ret = ddns_process_prereqs(request->query, update, &rcode); + if (ret != KNOT_EOK) { + UPDATE_LOG(LOG_WARNING, qdata, "prerequisites not met (%s)", + knot_strerror(ret)); + assert(rcode != KNOT_RCODE_NOERROR); + knot_wire_set_rcode(request->resp->wire, rcode); + return ret; + } + + return KNOT_EOK; +} + +static int process_single_update(struct knot_request *request, + const zone_t *zone, zone_update_t *update, + knotd_qdata_t *qdata) +{ + uint16_t rcode = KNOT_RCODE_NOERROR; + int ret = ddns_process_update(zone, request->query, update, &rcode); + if (ret != KNOT_EOK) { + UPDATE_LOG(LOG_WARNING, qdata, "failed to apply (%s)", + knot_strerror(ret)); + assert(rcode != KNOT_RCODE_NOERROR); + knot_wire_set_rcode(request->resp->wire, rcode); + return ret; + } + + return KNOT_EOK; +} + +static void set_rcodes(list_t *requests, const uint16_t rcode) +{ + ptrnode_t *node = NULL; + WALK_LIST(node, *requests) { + struct knot_request *req = node->d; + if (knot_wire_get_rcode(req->resp->wire) == KNOT_RCODE_NOERROR) { + knot_wire_set_rcode(req->resp->wire, rcode); + } + } +} + +static void store_original_qname(knotd_qdata_t *qdata, const knot_pkt_t *pkt) +{ + memcpy(qdata->extra->orig_qname, knot_pkt_qname(pkt), pkt->qname_size); +} + +static int process_bulk(zone_t *zone, list_t *requests, zone_update_t *up) +{ + // Walk all the requests and process. + ptrnode_t *node = NULL; + WALK_LIST(node, *requests) { + struct knot_request *req = node->d; + // Init qdata structure for logging (unique per-request). + knotd_qdata_params_t params = { + .remote = &req->remote + }; + knotd_qdata_t qdata; + knotd_qdata_extra_t extra; + init_qdata_from_request(&qdata, zone, req, ¶ms, &extra); + + store_original_qname(&qdata, req->query); + process_query_qname_case_lower(req->query); + + int ret = check_prereqs(req, zone, up, &qdata); + if (ret != KNOT_EOK) { + // Skip updates with failed prereqs. + continue; + } + + ret = process_single_update(req, zone, up, &qdata); + if (ret != KNOT_EOK) { + return ret; + } + + process_query_qname_case_restore(req->query, &qdata); + } + + return KNOT_EOK; +} + +static int process_normal(conf_t *conf, zone_t *zone, list_t *requests) +{ + assert(requests); + + // Init zone update structure + zone_update_t up; + int ret = zone_update_init(&up, zone, UPDATE_INCREMENTAL | UPDATE_SIGN); + if (ret != KNOT_EOK) { + set_rcodes(requests, KNOT_RCODE_SERVFAIL); + return ret; + } + + // Process all updates. + ret = process_bulk(zone, requests, &up); + if (ret != KNOT_EOK) { + zone_update_clear(&up); + set_rcodes(requests, KNOT_RCODE_SERVFAIL); + return ret; + } + + // Sign update. + conf_val_t val = conf_zone_get(conf, C_DNSSEC_SIGNING, zone->name); + bool dnssec_enable = (up.flags & UPDATE_SIGN) && conf_bool(&val); + if (dnssec_enable) { + zone_sign_reschedule_t resch = { 0 }; + ret = knot_dnssec_sign_update(&up, &resch); + if (ret != KNOT_EOK) { + zone_update_clear(&up); + set_rcodes(requests, KNOT_RCODE_SERVFAIL); + return ret; + } + event_dnssec_reschedule(conf, zone, &resch, false); // false since we handle NOTIFY after processing ddns queue + } + + // Apply changes. + ret = zone_update_commit(conf, &up); + zone_update_clear(&up); + if (ret != KNOT_EOK) { + if (ret == KNOT_EZONESIZE) { + set_rcodes(requests, KNOT_RCODE_REFUSED); + } else { + set_rcodes(requests, KNOT_RCODE_SERVFAIL); + } + return ret; + } + + return KNOT_EOK; +} + +static void process_requests(conf_t *conf, zone_t *zone, list_t *requests) +{ + assert(zone); + assert(requests); + + /* Keep original state. */ + struct timespec t_start = time_now(); + const uint32_t old_serial = zone_contents_serial(zone->contents); + + /* Process authenticated packet. */ + int ret = process_normal(conf, zone, requests); + if (ret != KNOT_EOK) { + log_zone_error(zone->name, "DDNS, processing failed (%s)", + knot_strerror(ret)); + return; + } + + /* Evaluate response. */ + const uint32_t new_serial = zone_contents_serial(zone->contents); + if (new_serial == old_serial) { + log_zone_info(zone->name, "DDNS, finished, no changes to the zone were made"); + return; + } + + struct timespec t_end = time_now(); + log_zone_info(zone->name, "DDNS, update finished, serial %u -> %u, " + "%.02f seconds", old_serial, new_serial, + time_diff_ms(&t_start, &t_end) / 1000.0); + + zone_events_schedule_at(zone, ZONE_EVENT_NOTIFY, time(NULL) + 1); +} + +static int remote_forward(conf_t *conf, struct knot_request *request, conf_remote_t *remote) +{ + /* Copy request and assign new ID. */ + knot_pkt_t *query = knot_pkt_new(NULL, request->query->max_size, NULL); + int ret = knot_pkt_copy(query, request->query); + if (ret != KNOT_EOK) { + knot_pkt_free(query); + return ret; + } + knot_wire_set_id(query->wire, dnssec_random_uint16_t()); + knot_tsig_append(query->wire, &query->size, query->max_size, query->tsig_rr); + + /* Prepare packet capture layer. */ + const knot_layer_api_t *capture = query_capture_api(); + struct capture_param capture_param = { + .sink = request->resp + }; + + /* Create requestor instance. */ + struct knot_requestor re; + ret = knot_requestor_init(&re, capture, &capture_param, NULL); + if (ret != KNOT_EOK) { + knot_pkt_free(query); + return ret; + } + + /* Create a request. */ + const struct sockaddr *dst = (const struct sockaddr *)&remote->addr; + const struct sockaddr *src = (const struct sockaddr *)&remote->via; + struct knot_request *req = knot_request_make(re.mm, dst, src, query, NULL, 0); + if (req == NULL) { + knot_requestor_clear(&re); + knot_pkt_free(query); + return KNOT_ENOMEM; + } + + /* Execute the request. */ + int timeout = 1000 * conf->cache.srv_tcp_reply_timeout; + ret = knot_requestor_exec(&re, req, timeout); + + knot_request_free(req, re.mm); + knot_requestor_clear(&re); + + return ret; +} + +static void forward_request(conf_t *conf, zone_t *zone, struct knot_request *request) +{ + /* Read the ddns master or the first master. */ + conf_val_t remote = conf_zone_get(conf, C_DDNS_MASTER, zone->name); + if (remote.code != KNOT_EOK) { + remote = conf_zone_get(conf, C_MASTER, zone->name); + } + + /* Get the number of remote addresses. */ + conf_val_t addr = conf_id_get(conf, C_RMT, C_ADDR, &remote); + size_t addr_count = conf_val_count(&addr); + assert(addr_count > 0); + + /* Try all remote addresses to forward the request to. */ + int ret = KNOT_EOK; + for (size_t i = 0; i < addr_count; i++) { + conf_remote_t master = conf_remote(conf, &remote, i); + + ret = remote_forward(conf, request, &master); + if (ret == KNOT_EOK) { + break; + } + } + + /* Restore message ID and TSIG. */ + knot_wire_set_id(request->resp->wire, knot_wire_get_id(request->query->wire)); + knot_tsig_append(request->resp->wire, &request->resp->size, + request->resp->max_size, request->resp->tsig_rr); + + /* Set RCODE if forwarding failed. */ + if (ret != KNOT_EOK) { + knot_wire_set_rcode(request->resp->wire, KNOT_RCODE_SERVFAIL); + log_zone_error(zone->name, "DDNS, failed to forward updates to the master (%s)", + knot_strerror(ret)); + } else { + log_zone_info(zone->name, "DDNS, updates forwarded to the master"); + } +} + +static void forward_requests(conf_t *conf, zone_t *zone, list_t *requests) +{ + assert(zone); + assert(requests); + + ptrnode_t *node = NULL; + WALK_LIST(node, *requests) { + struct knot_request *req = node->d; + forward_request(conf, zone, req); + } +} + +static bool update_tsig_check(conf_t *conf, knotd_qdata_t *qdata, struct knot_request *req) +{ + // Check that ACL is still valid. + if (!process_query_acl_check(conf, qdata->extra->zone->name, ACL_ACTION_UPDATE, qdata) || + process_query_verify(qdata) != KNOT_EOK) { + knot_wire_set_rcode(req->resp->wire, qdata->rcode); + return false; + } + + // Store signing context for response. + req->sign = qdata->sign; + + return true; +} + +static void send_update_response(conf_t *conf, const zone_t *zone, struct knot_request *req) +{ + if (req->resp) { + if (!zone_is_slave(conf, zone)) { + // Sign the response with TSIG where applicable + knotd_qdata_t qdata; + knotd_qdata_extra_t extra; + init_qdata_from_request(&qdata, zone, req, NULL, &extra); + + (void)process_query_sign_response(req->resp, &qdata); + } + + if (net_is_stream(req->fd)) { + int timeout = 1000 * conf->cache.srv_tcp_reply_timeout; + net_dns_tcp_send(req->fd, req->resp->wire, req->resp->size, + timeout); + } else { + net_dgram_send(req->fd, req->resp->wire, req->resp->size, + (struct sockaddr *)&req->remote); + } + } +} + +static void free_request(struct knot_request *req) +{ + close(req->fd); + knot_pkt_free(req->query); + knot_pkt_free(req->resp); + free(req); +} + +static void send_update_responses(conf_t *conf, const zone_t *zone, list_t *updates) +{ + ptrnode_t *node = NULL, *nxt = NULL; + WALK_LIST_DELSAFE(node, nxt, *updates) { + struct knot_request *req = node->d; + send_update_response(conf, zone, req); + free_request(req); + } + ptrlist_free(updates, NULL); +} + +static int init_update_responses(conf_t *conf, const zone_t *zone, list_t *updates, + size_t *update_count) +{ + ptrnode_t *node = NULL, *nxt = NULL; + WALK_LIST_DELSAFE(node, nxt, *updates) { + struct knot_request *req = node->d; + req->resp = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, NULL); + if (req->resp == NULL) { + return KNOT_ENOMEM; + } + + assert(req->query); + knot_pkt_init_response(req->resp, req->query); + if (zone_is_slave(conf, zone)) { + // Don't check TSIG for forwards. + continue; + } + + knotd_qdata_params_t params = { + .remote = &req->remote + }; + knotd_qdata_t qdata; + knotd_qdata_extra_t extra; + init_qdata_from_request(&qdata, zone, req, ¶ms, &extra); + + if (!update_tsig_check(conf, &qdata, req)) { + // ACL/TSIG check failed, send response. + send_update_response(conf, zone, req); + // Remove this request from processing list. + free_request(req); + ptrlist_rem(node, NULL); + *update_count -= 1; + } + } + + return KNOT_EOK; +} + +int update_process_query(knot_pkt_t *pkt, knotd_qdata_t *qdata) +{ + /* RFC1996 require SOA question. */ + NS_NEED_QTYPE(qdata, KNOT_RRTYPE_SOA, KNOT_RCODE_FORMERR); + + /* Check valid zone. */ + NS_NEED_ZONE(qdata, KNOT_RCODE_NOTAUTH); + + /* Need valid transaction security. */ + zone_t *zone = (zone_t *)qdata->extra->zone; + NS_NEED_AUTH(qdata, zone->name, ACL_ACTION_UPDATE); + /* Check expiration. */ + NS_NEED_ZONE_CONTENTS(qdata, KNOT_RCODE_SERVFAIL); + + NS_NEED_NOT_FROZEN(qdata, KNOT_RCODE_REFUSED); + + /* Restore original QNAME for DDNS ACL checks. */ + process_query_qname_case_restore(qdata->query, qdata); + /* Store update into DDNS queue. */ + int ret = zone_update_enqueue(zone, qdata->query, qdata->params); + if (ret != KNOT_EOK) { + return KNOT_STATE_FAIL; + } + + /* No immediate response. */ + return KNOT_STATE_NOOP; +} + +void updates_execute(conf_t *conf, zone_t *zone) +{ + /* Get list of pending updates. */ + list_t updates; + size_t update_count = zone_update_dequeue(zone, &updates); + if (update_count == 0) { + return; + } + + /* Init updates respones. */ + int ret = init_update_responses(conf, zone, &updates, &update_count); + if (ret != KNOT_EOK) { + /* Send what responses we can. */ + set_rcodes(&updates, KNOT_RCODE_SERVFAIL); + send_update_responses(conf, zone, &updates); + return; + } + + if (update_count == 0) { + /* All updates failed their ACL checks. */ + return; + } + + /* Process update list - forward if zone has master, or execute. + RCODEs are set. */ + if (zone_is_slave(conf, zone)) { + log_zone_info(zone->name, + "DDNS, forwarding %zu updates", update_count); + forward_requests(conf, zone, &updates); + } else { + log_zone_info(zone->name, + "DDNS, processing %zu updates", update_count); + process_requests(conf, zone, &updates); + } + + /* Send responses. */ + send_update_responses(conf, zone, &updates); +} diff --git a/src/knot/nameserver/update.h b/src/knot/nameserver/update.h new file mode 100644 index 0000000..8de10b6 --- /dev/null +++ b/src/knot/nameserver/update.h @@ -0,0 +1,34 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "libknot/packet/pkt.h" +#include "knot/nameserver/process_query.h" +#include "knot/zone/zone.h" + +/*! + * \brief UPDATE query processing module. + * + * \return KNOT_STATE_* processing states + */ +int update_process_query(knot_pkt_t *pkt, knotd_qdata_t *qdata); + +/*! + * \brief Processes serialized packet with DDNS. Function expects that the + * query is already authenticated and TSIG signature is verified. + */ +void updates_execute(conf_t *conf, zone_t *zone); diff --git a/src/knot/nameserver/xfr.c b/src/knot/nameserver/xfr.c new file mode 100644 index 0000000..c9b3f9e --- /dev/null +++ b/src/knot/nameserver/xfr.c @@ -0,0 +1,96 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "knot/nameserver/xfr.h" +#include "contrib/mempattern.h" + +int xfr_process_list(knot_pkt_t *pkt, xfr_put_cb put, knotd_qdata_t *qdata) +{ + if (pkt == NULL || qdata == NULL || qdata->extra->ext == NULL) { + return KNOT_EINVAL; + } + + int ret = KNOT_EOK; + knot_mm_t *mm = qdata->mm; + struct xfr_proc *xfer = qdata->extra->ext; + + /* Check if the zone wasn't expired during multi-message transfer. */ + zone_contents_t *contents = qdata->extra->zone->contents; + if (contents == NULL) { + return KNOT_ENOZONE; + } + knot_rrset_t soa_rr = node_rrset(contents->apex, KNOT_RRTYPE_SOA); + + /* Prepend SOA on first packet. */ + if (xfer->stats.messages == 0) { + ret = knot_pkt_put(pkt, 0, &soa_rr, KNOT_PF_NOTRUNC); + if (ret != KNOT_EOK) { + return ret; + } + } + + /* Process all items in the list. */ + while (!EMPTY_LIST(xfer->nodes)) { + ptrnode_t *head = HEAD(xfer->nodes); + ret = put(pkt, head->d, xfer); + if (ret == KNOT_EOK) { /* Finished. */ + /* Complete change set. */ + rem_node((node_t *)head); + mm_free(mm, head); + } else { /* Packet full or other error. */ + break; + } + } + + /* Append SOA on last packet. */ + if (ret == KNOT_EOK) { + ret = knot_pkt_put(pkt, 0, &soa_rr, KNOT_PF_NOTRUNC); + } + + /* Update counters. */ + xfr_stats_add(&xfer->stats, pkt->size + knot_rrset_size(&qdata->opt_rr)); + + /* If a rrset is larger than the message, + * fail to avoid infinite loop of empty messages */ + if (ret == KNOT_ESPACE && pkt->rrset_count < 1) { + return KNOT_ENOXFR; + } + + return ret; +} + +void xfr_stats_begin(struct xfr_stats *stats) +{ + assert(stats); + + memset(stats, 0, sizeof(*stats)); + stats->begin = time_now(); +} + +void xfr_stats_add(struct xfr_stats *stats, unsigned bytes) +{ + assert(stats); + + stats->messages += 1; + stats->bytes += bytes; +} + +void xfr_stats_end(struct xfr_stats *stats) +{ + assert(stats); + + stats->end = time_now(); +} diff --git a/src/knot/nameserver/xfr.h b/src/knot/nameserver/xfr.h new file mode 100644 index 0000000..579e317 --- /dev/null +++ b/src/knot/nameserver/xfr.h @@ -0,0 +1,69 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "contrib/time.h" +#include "contrib/ucw/lists.h" +#include "knot/nameserver/log.h" +#include "knot/nameserver/process_query.h" +#include "knot/zone/contents.h" +#include "libknot/packet/pkt.h" + +struct xfr_stats { + unsigned messages; + unsigned bytes; + struct timespec begin; + struct timespec end; +}; + +void xfr_stats_begin(struct xfr_stats *stats); +void xfr_stats_add(struct xfr_stats *stats, unsigned bytes); +void xfr_stats_end(struct xfr_stats *stats); + +static inline +void xfr_log_finished(const knot_dname_t *zone, enum log_operation op, + enum log_direction dir, const struct sockaddr *remote, + const struct xfr_stats *stats) +{ + ns_log(LOG_INFO, zone, op, dir, remote, + "finished, %0.2f seconds, %u messages, %u bytes", + time_diff_ms(&stats->begin, &stats->end) / 1000.0, + stats->messages, stats->bytes); +} + +/*! + * \brief Generic transfer processing state. + */ +struct xfr_proc { + list_t nodes; //!< Items to process (ptrnode_t). + zone_contents_t *contents; //!< Processed zone. + struct xfr_stats stats; //!< Packet transfer statistics. +}; + +/*! + * \brief Generic transfer processing. + * + * \return KNOT_EOK or an error + */ +typedef int (*xfr_put_cb)(knot_pkt_t *pkt, const void *item, struct xfr_proc *xfer); + +/*! + * \brief Put all items from xfr_proc.nodes to packet using a callback function. + * + * \note qdata->extra->ext points to struct xfr_proc* (this is xfer-specific context) + */ +int xfr_process_list(knot_pkt_t *pkt, xfr_put_cb put, knotd_qdata_t *qdata); diff --git a/src/knot/query/capture.c b/src/knot/query/capture.c new file mode 100644 index 0000000..983692f --- /dev/null +++ b/src/knot/query/capture.c @@ -0,0 +1,72 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> + +#include "knot/query/capture.h" + +static int reset(knot_layer_t *ctx) +{ + return KNOT_STATE_PRODUCE; +} + +static int finish(knot_layer_t *ctx) +{ + return KNOT_STATE_NOOP; +} + +static int begin(knot_layer_t *ctx, void *module_param) +{ + ctx->data = module_param; /* struct capture_param */ + return reset(ctx); +} + +static int prepare_query(knot_layer_t *ctx, knot_pkt_t *pkt) +{ + assert(pkt && ctx && ctx->data); + struct capture_param *param = ctx->data; + + // Restore to original QNAME if requested. + if (param->orig_qname != NULL && param->orig_qname[0] != '\0') { + memcpy(pkt->wire + KNOT_WIRE_HEADER_SIZE, + param->orig_qname, pkt->qname_size); + } + + return KNOT_STATE_CONSUME; +} + +static int capture(knot_layer_t *ctx, knot_pkt_t *pkt) +{ + assert(pkt && ctx && ctx->data); + struct capture_param *param = ctx->data; + + knot_pkt_copy(param->sink, pkt); + + return KNOT_STATE_DONE; +} + +const knot_layer_api_t *query_capture_api(void) +{ + static const knot_layer_api_t API = { + .begin = begin, + .reset = reset, + .finish = finish, + .consume = capture, + .produce = prepare_query, + }; + + return &API; +} diff --git a/src/knot/query/capture.h b/src/knot/query/capture.h new file mode 100644 index 0000000..5c78479 --- /dev/null +++ b/src/knot/query/capture.h @@ -0,0 +1,33 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "knot/query/layer.h" +#include "libknot/packet/pkt.h" + +/*! + * \brief Processing module for packet capture. + */ +const knot_layer_api_t *query_capture_api(void); + +/*! + * \brief Processing module parameters. + */ +struct capture_param { + knot_pkt_t *sink; /*!< Container for captured response. */ + uint8_t *orig_qname; /*!< Original query name (case sensitive). */ +}; diff --git a/src/knot/query/layer.h b/src/knot/query/layer.h new file mode 100644 index 0000000..d0fb12f --- /dev/null +++ b/src/knot/query/layer.h @@ -0,0 +1,135 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "libknot/packet/pkt.h" +#include "libknot/mm_ctx.h" +#include "knot/nameserver/tsig_ctx.h" + +/*! + * \brief Layer processing states. + * + * Each state represents the state machine transition, + * and determines readiness for the next action. + */ +typedef enum { + KNOT_STATE_NOOP = 0, //!< Invalid. + KNOT_STATE_CONSUME, //!< Consume data. + KNOT_STATE_PRODUCE, //!< Produce data. + KNOT_STATE_RESET, //!< Restart processing. + KNOT_STATE_DONE, //!< Finished. + KNOT_STATE_FAIL, //!< Error. + KNOT_STATE_FINAL, //!< Finished and finalized. +} knot_layer_state_t; + +typedef struct knot_layer_api knot_layer_api_t; + +/*! \brief Packet processing context. */ +typedef struct { + const knot_layer_api_t *api; //!< Layer API. + knot_mm_t *mm; //!< Processing memory context. + knot_layer_state_t state; //!< Processing state. + void *data; //!< Module specific. + tsig_ctx_t *tsig; //!< TODO: remove + unsigned flags; //!< Custom flags. +} knot_layer_t; + +/*! \brief Packet processing module API. */ +struct knot_layer_api { + int (*begin)(knot_layer_t *ctx, void *params); + int (*reset)(knot_layer_t *ctx); + int (*finish)(knot_layer_t *ctx); + int (*consume)(knot_layer_t *ctx, knot_pkt_t *pkt); + int (*produce)(knot_layer_t *ctx, knot_pkt_t *pkt); +}; + +/*! \brief Helper for conditional layer call. */ +#define LAYER_CALL(layer, func, ...) \ + assert(layer->api); \ + if (layer->api->func) { \ + layer->state = layer->api->func(layer, ##__VA_ARGS__); \ + } + +/*! + * \brief Initialize packet processing context. + * + * \param ctx Layer context. + * \param mm Memory context. + * \param api Layer API. + */ +inline static void knot_layer_init(knot_layer_t *ctx, knot_mm_t *mm, + const knot_layer_api_t *api) +{ + memset(ctx, 0, sizeof(*ctx)); + + ctx->mm = mm; + ctx->api = api; + ctx->state = KNOT_STATE_NOOP; +} + +/*! + * \brief Prepare packet processing. + * + * \param ctx Layer context. + * \param params Initialization params. + */ +inline static void knot_layer_begin(knot_layer_t *ctx, void *params) +{ + LAYER_CALL(ctx, begin, params); +} + +/*! + * \brief Reset current packet processing context. + * + * \param ctx Layer context. + */ +inline static void knot_layer_reset(knot_layer_t *ctx) +{ + LAYER_CALL(ctx, reset); +} + +/*! + * \brief Finish and close packet processing context. + * + * \param ctx Layer context. + */ +inline static void knot_layer_finish(knot_layer_t *ctx) +{ + LAYER_CALL(ctx, finish); +} + +/*! + * \brief Add more data to layer processing. + * + * \param ctx Layer context. + * \param pkt Data packet. + */ +inline static void knot_layer_consume(knot_layer_t *ctx, knot_pkt_t *pkt) +{ + LAYER_CALL(ctx, consume, pkt); +} + +/*! + * \brief Generate output from layer. + * + * \param ctx Layer context. + * \param pkt Data packet. + */ +inline static void knot_layer_produce(knot_layer_t *ctx, knot_pkt_t *pkt) +{ + LAYER_CALL(ctx, produce, pkt); +} diff --git a/src/knot/query/query.c b/src/knot/query/query.c new file mode 100644 index 0000000..b995d53 --- /dev/null +++ b/src/knot/query/query.c @@ -0,0 +1,118 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "knot/query/query.h" + +#include <stdint.h> + +#include "contrib/wire_ctx.h" +#include "libdnssec/random.h" +#include "knot/conf/conf.h" +#include "libknot/yparser/yptrafo.h" +#include "libknot/rrset.h" + +int query_init_pkt(knot_pkt_t *pkt) +{ + if (!pkt) { + return KNOT_EINVAL; + } + + knot_pkt_clear(pkt); + knot_wire_set_id(pkt->wire, dnssec_random_uint16_t()); + + return KNOT_EOK; +} + +int query_edns_data_init(struct query_edns_data *edns_ptr, conf_t *conf, + const knot_dname_t *zone, int remote_family) +{ + if (!edns_ptr || !conf || !zone) { + return KNOT_EINVAL; + } + + struct query_edns_data edns = { 0 }; + + // Determine max payload + + switch (remote_family) { + case AF_INET: + edns.max_payload = conf->cache.srv_max_ipv4_udp_payload; + break; + case AF_INET6: + edns.max_payload = conf->cache.srv_max_ipv6_udp_payload; + break; + default: + return KNOT_EINVAL; + } + + // Determine custom option + + conf_val_t val = conf_zone_get(conf, C_REQUEST_EDNS_OPTION, zone); + size_t opt_len = 0; + const uint8_t *opt_data = conf_data(&val, &opt_len); + if (opt_data != NULL) { + wire_ctx_t ctx = wire_ctx_init_const(opt_data, opt_len); + edns.custom_code = wire_ctx_read_u64(&ctx); + edns.custom_len = wire_ctx_read_u16(&ctx); + edns.custom_data = ctx.position; + assert(ctx.error == KNOT_EOK); + assert(wire_ctx_available(&ctx) == edns.custom_len); + } + + *edns_ptr = edns; + return KNOT_EOK; +} + +int query_put_edns(knot_pkt_t *pkt, const struct query_edns_data *edns) +{ + if (!pkt || !edns) { + return KNOT_EINVAL; + } + + // Construct EDNS RR + + knot_rrset_t opt_rr = { 0 }; + int ret = knot_edns_init(&opt_rr, edns->max_payload, 0, KNOT_EDNS_VERSION, &pkt->mm); + if (ret != KNOT_EOK) { + return ret; + } + + if (edns->do_flag) { + knot_edns_set_do(&opt_rr); + } + + if (edns->custom_code != 0) { + ret = knot_edns_add_option(&opt_rr, edns->custom_code, + edns->custom_len, edns->custom_data, + &pkt->mm); + if (ret != KNOT_EOK) { + knot_rrset_clear(&opt_rr, &pkt->mm); + return ret; + } + } + + // Add result into the packet + + knot_pkt_begin(pkt, KNOT_ADDITIONAL); + + ret = knot_pkt_put(pkt, KNOT_COMPR_HINT_NOCOMP, &opt_rr, KNOT_PF_FREE); + if (ret != KNOT_EOK) { + knot_rrset_clear(&opt_rr, &pkt->mm); + return ret; + } + + return KNOT_EOK; +} diff --git a/src/knot/query/query.h b/src/knot/query/query.h new file mode 100644 index 0000000..57a12d2 --- /dev/null +++ b/src/knot/query/query.h @@ -0,0 +1,67 @@ +/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "knot/nameserver/log.h" +#include "libknot/packet/pkt.h" + +/*! + * \brief EDNS data. + */ +struct query_edns_data { + uint16_t max_payload; + bool do_flag; + + // Custom EDNS option: + uint16_t custom_code; + const uint8_t *custom_data; + uint16_t custom_len; +}; + +/*! + * \brief Initialize new packet. + * + * Clear the packet and generate random transaction ID. + * + * \param pkt Packet to initialize. + * + * \return Always KNOT_EOK if valid parameters supplied. + */ +int query_init_pkt(knot_pkt_t *pkt); + +/*! + * \brief Initialize EDNS parameters from server configuration. + * + * \param[out] edns EDNS parameters to initialize. + * \param[in] conf Server configuration. + * \param[in] zone Zone name. + * \param[in] remote_family Address family for remote host. + * + * \return KNOT_E* + */ +int query_edns_data_init(struct query_edns_data *edns, conf_t *conf, + const knot_dname_t *zone, int remote_family); + +/*! + * \brief Append EDNS into the packet. + * + * \param pkt Packet to add EDNS into. + * \param edns EDNS data. + * + * \return KNOT_E* + */ +int query_put_edns(knot_pkt_t *pkt, const struct query_edns_data *edns); diff --git a/src/knot/query/requestor.c b/src/knot/query/requestor.c new file mode 100644 index 0000000..d28b9c9 --- /dev/null +++ b/src/knot/query/requestor.c @@ -0,0 +1,328 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> + +#include "libknot/attribute.h" +#include "knot/query/requestor.h" +#include "libknot/errcode.h" +#include "contrib/mempattern.h" +#include "contrib/net.h" +#include "contrib/sockaddr.h" + +static bool use_tcp(struct knot_request *request) +{ + return (request->flags & KNOT_RQ_UDP) == 0; +} + +static bool is_answer_to_query(const knot_pkt_t *query, const knot_pkt_t *answer) +{ + return knot_wire_get_id(query->wire) == knot_wire_get_id(answer->wire); +} + +/*! \brief Ensure a socket is connected. */ +static int request_ensure_connected(struct knot_request *request) +{ + if (request->fd >= 0) { + return KNOT_EOK; + } + + int sock_type = use_tcp(request) ? SOCK_STREAM : SOCK_DGRAM; + request->fd = net_connected_socket(sock_type, + (struct sockaddr *)&request->remote, + (struct sockaddr *)&request->source); + if (request->fd < 0) { + return KNOT_ECONN; + } + + return KNOT_EOK; +} + +static int request_send(struct knot_request *request, int timeout_ms) +{ + /* Initiate non-blocking connect if not connected. */ + int ret = request_ensure_connected(request); + if (ret != KNOT_EOK) { + return ret; + } + + /* Send query, construct if not exists. */ + knot_pkt_t *query = request->query; + uint8_t *wire = query->wire; + size_t wire_len = query->size; + + /* Send query. */ + if (use_tcp(request)) { + ret = net_dns_tcp_send(request->fd, wire, wire_len, timeout_ms); + } else { + ret = net_dgram_send(request->fd, wire, wire_len, NULL); + } + if (ret != wire_len) { + return KNOT_ECONN; + } + + return KNOT_EOK; +} + +static int request_recv(struct knot_request *request, int timeout_ms) +{ + knot_pkt_t *resp = request->resp; + knot_pkt_clear(resp); + + /* Wait for readability */ + int ret = request_ensure_connected(request); + if (ret != KNOT_EOK) { + return ret; + } + + /* Receive it */ + if (use_tcp(request)) { + ret = net_dns_tcp_recv(request->fd, resp->wire, resp->max_size, timeout_ms); + } else { + ret = net_dgram_recv(request->fd, resp->wire, resp->max_size, timeout_ms); + } + if (ret <= 0) { + resp->size = 0; + if (ret == 0) { + return KNOT_ECONN; + } + return ret; + } + + resp->size = ret; + return ret; +} + +struct knot_request *knot_request_make(knot_mm_t *mm, + const struct sockaddr *remote, + const struct sockaddr *source, + knot_pkt_t *query, + const knot_tsig_key_t *tsig_key, + unsigned flags) +{ + if (remote == NULL || query == NULL) { + return NULL; + } + + struct knot_request *request = mm_alloc(mm, sizeof(*request)); + if (request == NULL) { + return NULL; + } + memset(request, 0, sizeof(*request)); + + request->resp = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, mm); + if (request->resp == NULL) { + mm_free(mm, request); + return NULL; + } + + request->query = query; + request->fd = -1; + request->flags = flags; + memcpy(&request->remote, remote, sockaddr_len(remote)); + if (source) { + memcpy(&request->source, source, sockaddr_len(source)); + } else { + request->source.ss_family = AF_UNSPEC; + } + + if (tsig_key && tsig_key->algorithm == DNSSEC_TSIG_UNKNOWN) { + tsig_key = NULL; + } + tsig_init(&request->tsig, tsig_key); + + return request; +} + +void knot_request_free(struct knot_request *request, knot_mm_t *mm) +{ + if (request == NULL) { + return; + } + + if (request->fd >= 0) { + close(request->fd); + } + knot_pkt_free(request->query); + knot_pkt_free(request->resp); + tsig_cleanup(&request->tsig); + + mm_free(mm, request); +} + +int knot_requestor_init(struct knot_requestor *requestor, + const knot_layer_api_t *proc, void *proc_param, + knot_mm_t *mm) +{ + if (requestor == NULL || proc == NULL) { + return KNOT_EINVAL; + } + + memset(requestor, 0, sizeof(*requestor)); + + requestor->mm = mm; + knot_layer_init(&requestor->layer, mm, proc); + knot_layer_begin(&requestor->layer, proc_param); + + return KNOT_EOK; +} + +void knot_requestor_clear(struct knot_requestor *requestor) +{ + if (requestor == NULL) { + return; + } + + knot_layer_finish(&requestor->layer); + + memset(requestor, 0, sizeof(*requestor)); +} + +static int request_reset(struct knot_requestor *req, + struct knot_request *last) +{ + knot_layer_reset(&req->layer); + tsig_reset(&last->tsig); + + if (req->layer.flags & KNOT_RQ_LAYER_CLOSE) { + req->layer.flags &= ~KNOT_RQ_LAYER_CLOSE; + if (last->fd >= 0) { + close(last->fd); + last->fd = -1; + } + } + + if (req->layer.state == KNOT_STATE_RESET) { + return KNOT_LAYER_ERROR; + } + + return KNOT_EOK; +} + +static int request_produce(struct knot_requestor *req, + struct knot_request *last, + int timeout_ms) +{ + knot_layer_produce(&req->layer, last->query); + + int ret = tsig_sign_packet(&last->tsig, last->query); + if (ret != KNOT_EOK) { + return ret; + } + + // TODO: verify condition + if (req->layer.state == KNOT_STATE_CONSUME) { + ret = request_send(last, timeout_ms); + } + + return ret; +} + +static int request_consume(struct knot_requestor *req, + struct knot_request *last, + int timeout_ms) +{ + int ret = request_recv(last, timeout_ms); + if (ret < 0) { + return ret; + } + + ret = knot_pkt_parse(last->resp, 0); + if (ret != KNOT_EOK) { + return ret; + } + + if (!is_answer_to_query(last->query, last->resp)) { + return KNOT_EMALF; + } + + ret = tsig_verify_packet(&last->tsig, last->resp); + if (ret != KNOT_EOK) { + return ret; + } + + if (tsig_unsigned_count(&last->tsig) >= 100) { + return KNOT_TSIG_EBADSIG; + } + + knot_layer_consume(&req->layer, last->resp); + + return KNOT_EOK; +} + +static bool layer_active(knot_layer_state_t state) +{ + switch (state) { + case KNOT_STATE_CONSUME: + case KNOT_STATE_PRODUCE: + case KNOT_STATE_RESET: + return true; + default: + return false; + } +} + +static int request_io(struct knot_requestor *req, struct knot_request *last, + int timeout_ms) +{ + switch (req->layer.state) { + case KNOT_STATE_CONSUME: + return request_consume(req, last, timeout_ms); + case KNOT_STATE_PRODUCE: + return request_produce(req, last, timeout_ms); + case KNOT_STATE_RESET: + return request_reset(req, last); + default: + return KNOT_EINVAL; + } +} + +int knot_requestor_exec(struct knot_requestor *requestor, + struct knot_request *request, + int timeout_ms) +{ + if (!requestor || !request) { + return KNOT_EINVAL; + } + + int ret = KNOT_EOK; + + requestor->layer.tsig = &request->tsig; + + /* Do I/O until the processing is satisifed or fails. */ + while (layer_active(requestor->layer.state)) { + ret = request_io(requestor, request, timeout_ms); + if (ret != KNOT_EOK) { + knot_layer_finish(&requestor->layer); + return ret; + } + } + + /* Expect complete request. */ + if (requestor->layer.state != KNOT_STATE_DONE) { + ret = KNOT_LAYER_ERROR; + } + + /* Verify last TSIG */ + if (tsig_unsigned_count(&request->tsig) != 0) { + ret = KNOT_TSIG_EBADSIG; + } + + /* Finish current query processing. */ + knot_layer_finish(&requestor->layer); + + return ret; +} diff --git a/src/knot/query/requestor.h b/src/knot/query/requestor.h new file mode 100644 index 0000000..e0ee14e --- /dev/null +++ b/src/knot/query/requestor.h @@ -0,0 +1,119 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <sys/socket.h> +#include <sys/time.h> + +#include "knot/nameserver/tsig_ctx.h" +#include "knot/query/layer.h" +#include "libknot/mm_ctx.h" +#include "libknot/rrtype/tsig.h" + +struct knot_request; + +/* Requestor flags. */ +enum { + KNOT_RQ_UDP = 1 << 0 /* Use UDP for requests. */ +}; + +enum { + KNOT_RQ_LAYER_CLOSE = 1 << 0 +}; + +/*! \brief Requestor structure. + * + * Requestor holds a FIFO of pending queries. + */ +struct knot_requestor { + knot_mm_t *mm; /*!< Memory context. */ + knot_layer_t layer; /*!< Response processing layer. */ +}; + +/*! \brief Request data (socket, payload, response, TSIG and endpoints). */ +struct knot_request { + int fd; + unsigned flags; + struct sockaddr_storage remote, source; + knot_pkt_t *query; + knot_pkt_t *resp; + tsig_ctx_t tsig; + + knot_sign_context_t sign; /* TODO: Remove. Used in updates only, should + be part of the zone update context. */ +}; + +/*! + * \brief Make request out of endpoints and query. + * + * \param mm Memory context. + * \param dst Remote endpoint address. + * \param src Source address (or NULL). + * \param query Query message. + * \param key TSIG key for authentication. + * \param flags Request flags. + * + * \return Prepared request or NULL in case of error. + */ +struct knot_request *knot_request_make(knot_mm_t *mm, + const struct sockaddr *dst, + const struct sockaddr *src, + knot_pkt_t *query, + const knot_tsig_key_t *key, + unsigned flags); + +/*! + * \brief Free request and associated data. + * + * \param request Freed request. + * \param mm Memory context. + */ +void knot_request_free(struct knot_request *request, knot_mm_t *mm); + +/*! + * \brief Initialize requestor structure. + * + * \param requestor Requestor instance. + * \param proc Response processing module. + * \param proc_param Processing module context. + * \param mm Memory context. + * + * \return KNOT_EOK or error + */ +int knot_requestor_init(struct knot_requestor *requestor, + const knot_layer_api_t *proc, void *proc_param, + knot_mm_t *mm); + +/*! + * \brief Clear the requestor structure and close pending queries. + * + * \param requestor Requestor instance. + */ +void knot_requestor_clear(struct knot_requestor *requestor); + +/*! + * \brief Execute a request. + * + * \param requestor Requestor instance. + * \param request Request instance. + * \param timeout_ms Timeout of each operation in miliseconds (-1 for infinity). + * + * \return KNOT_EOK or error + */ +int knot_requestor_exec(struct knot_requestor *requestor, + struct knot_request *request, + int timeout_ms); diff --git a/src/knot/server/dthreads.c b/src/knot/server/dthreads.c new file mode 100644 index 0000000..30689f5 --- /dev/null +++ b/src/knot/server/dthreads.c @@ -0,0 +1,766 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <signal.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <unistd.h> +#include <errno.h> +#include <urcu.h> + +#ifdef HAVE_PTHREAD_NP_H +#include <pthread_np.h> +#endif /* HAVE_PTHREAD_NP_H */ + +#include "knot/server/dthreads.h" +#include "libknot/libknot.h" + +/* BSD cpu set compatibility. */ +#if defined(HAVE_CPUSET_BSD) +typedef cpuset_t cpu_set_t; +#endif + +/*! \brief Lock thread state for R/W. */ +static inline void lock_thread_rw(dthread_t *thread) +{ + pthread_mutex_lock(&thread->_mx); +} +/*! \brief Unlock thread state for R/W. */ +static inline void unlock_thread_rw(dthread_t *thread) +{ + pthread_mutex_unlock(&thread->_mx); +} + +/*! \brief Signalize thread state change. */ +static inline void unit_signalize_change(dt_unit_t *unit) +{ + pthread_mutex_lock(&unit->_report_mx); + pthread_cond_signal(&unit->_report); + pthread_mutex_unlock(&unit->_report_mx); +} + +/*! + * \brief Update thread state with notification. + * \param thread Given thread. + * \param state New state for thread. + * \retval 0 on success. + * \retval <0 on error (EINVAL, ENOTSUP). + */ +static inline int dt_update_thread(dthread_t *thread, int state) +{ + if (thread == 0) { + return KNOT_EINVAL; + } + + // Cancel with lone thread + dt_unit_t *unit = thread->unit; + if (unit == 0) { + return KNOT_ENOTSUP; + } + + // Cancel current runnable if running + pthread_mutex_lock(&unit->_notify_mx); + lock_thread_rw(thread); + if (thread->state & (ThreadIdle | ThreadActive)) { + + // Update state + thread->state = state; + unlock_thread_rw(thread); + + // Notify thread + pthread_cond_broadcast(&unit->_notify); + pthread_mutex_unlock(&unit->_notify_mx); + } else { + /* Unable to update thread, it is already dead. */ + unlock_thread_rw(thread); + pthread_mutex_unlock(&unit->_notify_mx); + return KNOT_EINVAL; + } + + return KNOT_EOK; +} + +/*! + * \brief Thread entrypoint function. + * + * When a thread is created and started, it immediately enters this function. + * Depending on thread state, it either enters runnable or + * blocks until it is awakened. + * + * This function also handles "ThreadIdle" state to quickly suspend and resume + * threads and mitigate thread creation costs. Also, thread runnable may + * be changed to alter the thread behavior on runtime + */ +static void *thread_ep(void *data) +{ + dthread_t *thread = (dthread_t *)data; + if (thread == 0) { + return 0; + } + + // Check if is a member of unit + dt_unit_t *unit = thread->unit; + if (unit == 0) { + return 0; + } + + // Unblock SIGALRM for synchronization + sigset_t mask; + (void)sigemptyset(&mask); + sigaddset(&mask, SIGALRM); + pthread_sigmask(SIG_UNBLOCK, &mask, NULL); + + rcu_register_thread(); + + // Run loop + for (;;) { + + // Check thread state + lock_thread_rw(thread); + if (thread->state == ThreadDead) { + unlock_thread_rw(thread); + break; + } + + // Update data + thread->data = thread->_adata; + runnable_t _run = thread->run; + + // Start runnable if thread is marked Active + if ((thread->state == ThreadActive) && (thread->run != 0)) { + unlock_thread_rw(thread); + _run(thread); + } else { + unlock_thread_rw(thread); + } + + // If the runnable was cancelled, start new iteration + lock_thread_rw(thread); + if (thread->state & ThreadCancelled) { + thread->state &= ~ThreadCancelled; + unlock_thread_rw(thread); + continue; + } + unlock_thread_rw(thread); + + // Runnable finished without interruption, mark as Idle + pthread_mutex_lock(&unit->_notify_mx); + lock_thread_rw(thread); + if (thread->state & ThreadActive) { + thread->state &= ~ThreadActive; + thread->state |= ThreadIdle; + } + + // Go to sleep if idle + if (thread->state & ThreadIdle) { + unlock_thread_rw(thread); + + // Signalize state change + unit_signalize_change(unit); + + // Wait for notification from unit + pthread_cond_wait(&unit->_notify, &unit->_notify_mx); + pthread_mutex_unlock(&unit->_notify_mx); + } else { + unlock_thread_rw(thread); + pthread_mutex_unlock(&unit->_notify_mx); + } + } + + // Thread destructor + if (thread->destruct) { + thread->destruct(thread); + } + + // Report thread state change + unit_signalize_change(unit); + lock_thread_rw(thread); + thread->state |= ThreadJoinable; + unlock_thread_rw(thread); + rcu_unregister_thread(); + + // Return + return 0; +} + +/*! + * \brief Create single thread. + * \retval New thread instance on success. + * \retval NULL on error. + */ +static dthread_t *dt_create_thread(dt_unit_t *unit) +{ + // Alloc thread + dthread_t *thread = malloc(sizeof(dthread_t)); + if (thread == 0) { + return 0; + } + + memset(thread, 0, sizeof(dthread_t)); + + // Blank thread state + thread->state = ThreadJoined; + pthread_mutex_init(&thread->_mx, 0); + + // Set membership in unit + thread->unit = unit; + + // Initialize attribute + pthread_attr_t *attr = &thread->_attr; + pthread_attr_init(attr); + //pthread_attr_setinheritsched(attr, PTHREAD_INHERIT_SCHED); + //pthread_attr_setschedpolicy(attr, SCHED_OTHER); + pthread_attr_setstacksize(attr, 1024*1024); + return thread; +} + +/*! \brief Delete single thread. */ +static void dt_delete_thread(dthread_t **thread) +{ + if (!thread || !*thread) { + return; + } + + dthread_t* thr = *thread; + thr->unit = 0; + *thread = 0; + + // Delete attribute + pthread_attr_destroy(&(thr)->_attr); + + // Delete mutex + pthread_mutex_destroy(&(thr)->_mx); + + // Free memory + free(thr); +} + +static dt_unit_t *dt_create_unit(int count) +{ + if (count <= 0) { + return 0; + } + + dt_unit_t *unit = malloc(sizeof(dt_unit_t)); + if (unit == 0) { + return 0; + } + + // Initialize conditions + if (pthread_cond_init(&unit->_notify, 0) != 0) { + free(unit); + return 0; + } + if (pthread_cond_init(&unit->_report, 0) != 0) { + pthread_cond_destroy(&unit->_notify); + free(unit); + return 0; + } + + // Initialize mutexes + if (pthread_mutex_init(&unit->_notify_mx, 0) != 0) { + pthread_cond_destroy(&unit->_notify); + pthread_cond_destroy(&unit->_report); + free(unit); + return 0; + } + if (pthread_mutex_init(&unit->_report_mx, 0) != 0) { + pthread_cond_destroy(&unit->_notify); + pthread_cond_destroy(&unit->_report); + pthread_mutex_destroy(&unit->_notify_mx); + free(unit); + return 0; + } + if (pthread_mutex_init(&unit->_mx, 0) != 0) { + pthread_cond_destroy(&unit->_notify); + pthread_cond_destroy(&unit->_report); + pthread_mutex_destroy(&unit->_notify_mx); + pthread_mutex_destroy(&unit->_report_mx); + free(unit); + return 0; + } + + // Save unit size + unit->size = count; + + // Alloc threads + unit->threads = calloc(count, sizeof(dthread_t *)); + if (unit->threads == 0) { + pthread_cond_destroy(&unit->_notify); + pthread_cond_destroy(&unit->_report); + pthread_mutex_destroy(&unit->_notify_mx); + pthread_mutex_destroy(&unit->_report_mx); + pthread_mutex_destroy(&unit->_mx); + free(unit); + return 0; + } + + // Initialize threads + int init_success = 1; + for (int i = 0; i < count; ++i) { + unit->threads[i] = dt_create_thread(unit); + if (unit->threads[i] == 0) { + init_success = 0; + break; + } + } + + // Check thread initialization + if (!init_success) { + + // Delete created threads + for (int i = 0; i < count; ++i) { + dt_delete_thread(&unit->threads[i]); + } + + // Free rest of the unit + pthread_cond_destroy(&unit->_notify); + pthread_cond_destroy(&unit->_report); + pthread_mutex_destroy(&unit->_notify_mx); + pthread_mutex_destroy(&unit->_report_mx); + pthread_mutex_destroy(&unit->_mx); + free(unit->threads); + free(unit); + return 0; + } + + return unit; +} + +dt_unit_t *dt_create(int count, runnable_t runnable, runnable_t destructor, void *data) +{ + if (count <= 0) { + return 0; + } + + // Create unit + dt_unit_t *unit = dt_create_unit(count); + if (unit == 0) { + return 0; + } + + // Set threads common purpose + pthread_mutex_lock(&unit->_notify_mx); + dt_unit_lock(unit); + + for (int i = 0; i < count; ++i) { + dthread_t *thread = unit->threads[i]; + lock_thread_rw(thread); + thread->run = runnable; + thread->destruct = destructor; + thread->_adata = data; + unlock_thread_rw(thread); + } + + dt_unit_unlock(unit); + pthread_mutex_unlock(&unit->_notify_mx); + + return unit; +} + +void dt_delete(dt_unit_t **unit) +{ + /* + * All threads must be stopped or idle at this point, + * or else the behavior is undefined. + * Sorry. + */ + + if (unit == 0) { + return; + } + if (*unit == 0) { + return; + } + + // Compact and reclaim idle threads + dt_unit_t *d_unit = *unit; + dt_compact(d_unit); + + // Delete threads + for (int i = 0; i < d_unit->size; ++i) { + dt_delete_thread(&d_unit->threads[i]); + } + + // Deinit mutexes + pthread_mutex_destroy(&d_unit->_notify_mx); + pthread_mutex_destroy(&d_unit->_report_mx); + pthread_mutex_destroy(&d_unit->_mx); + + // Deinit conditions + pthread_cond_destroy(&d_unit->_notify); + pthread_cond_destroy(&d_unit->_report); + + // Free memory + free(d_unit->threads); + free(d_unit); + *unit = 0; +} + +static int dt_start_id(dthread_t *thread) +{ + if (thread == 0) { + return KNOT_EINVAL; + } + + lock_thread_rw(thread); + + // Update state + int prev_state = thread->state; + thread->state |= ThreadActive; + thread->state &= ~ThreadIdle; + thread->state &= ~ThreadDead; + thread->state &= ~ThreadJoined; + thread->state &= ~ThreadJoinable; + + // Do not re-create running threads + if (prev_state != ThreadJoined) { + unlock_thread_rw(thread); + return 0; + } + + // Start thread + sigset_t mask_all, mask_old; + sigfillset(&mask_all); + sigdelset(&mask_all, SIGPROF); + pthread_sigmask(SIG_SETMASK, &mask_all, &mask_old); + int res = pthread_create(&thread->_thr, /* pthread_t */ + &thread->_attr, /* pthread_attr_t */ + thread_ep, /* routine: thread_ep */ + thread); /* passed object: dthread_t */ + pthread_sigmask(SIG_SETMASK, &mask_old, NULL); + + // Unlock thread + unlock_thread_rw(thread); + return res; +} + +int dt_start(dt_unit_t *unit) +{ + if (unit == 0) { + return KNOT_EINVAL; + } + + // Lock unit + pthread_mutex_lock(&unit->_notify_mx); + dt_unit_lock(unit); + for (int i = 0; i < unit->size; ++i) { + + dthread_t *thread = unit->threads[i]; + int res = dt_start_id(thread); + if (res != 0) { + dt_unit_unlock(unit); + pthread_mutex_unlock(&unit->_notify_mx); + return res; + } + } + + // Unlock unit + dt_unit_unlock(unit); + pthread_cond_broadcast(&unit->_notify); + pthread_mutex_unlock(&unit->_notify_mx); + return KNOT_EOK; +} + +int dt_signalize(dthread_t *thread, int signum) +{ + if (thread == 0) { + return KNOT_EINVAL; + } + + int ret = pthread_kill(thread->_thr, signum); + + /* Not thread id found or invalid signum. */ + if (ret == EINVAL || ret == ESRCH) { + return KNOT_EINVAL; + } + + /* Generic error. */ + if (ret < 0) { + return KNOT_ERROR; + } + + return KNOT_EOK; +} + +int dt_join(dt_unit_t *unit) +{ + if (unit == 0) { + return KNOT_EINVAL; + } + + for (;;) { + + // Lock unit + pthread_mutex_lock(&unit->_report_mx); + dt_unit_lock(unit); + + // Browse threads + int active_threads = 0; + for (int i = 0; i < unit->size; ++i) { + + // Count active or cancelled but pending threads + dthread_t *thread = unit->threads[i]; + lock_thread_rw(thread); + if (thread->state & (ThreadActive|ThreadCancelled)) { + ++active_threads; + } + + // Reclaim dead threads, but only fast + if (thread->state & ThreadJoinable) { + unlock_thread_rw(thread); + pthread_join(thread->_thr, 0); + lock_thread_rw(thread); + thread->state = ThreadJoined; + unlock_thread_rw(thread); + } else { + unlock_thread_rw(thread); + } + } + + // Unlock unit + dt_unit_unlock(unit); + + // Check result + if (active_threads == 0) { + pthread_mutex_unlock(&unit->_report_mx); + break; + } + + // Wait for a thread to finish + pthread_cond_wait(&unit->_report, &unit->_report_mx); + pthread_mutex_unlock(&unit->_report_mx); + } + + return KNOT_EOK; +} + +int dt_stop(dt_unit_t *unit) +{ + if (unit == 0) { + return KNOT_EINVAL; + } + + // Lock unit + pthread_mutex_lock(&unit->_notify_mx); + dt_unit_lock(unit); + + // Signalize all threads to stop + for (int i = 0; i < unit->size; ++i) { + + // Lock thread + dthread_t *thread = unit->threads[i]; + lock_thread_rw(thread); + if (thread->state & (ThreadIdle | ThreadActive)) { + thread->state = ThreadDead | ThreadCancelled; + dt_signalize(thread, SIGALRM); + } + unlock_thread_rw(thread); + } + + // Unlock unit + dt_unit_unlock(unit); + + // Broadcast notification + pthread_cond_broadcast(&unit->_notify); + pthread_mutex_unlock(&unit->_notify_mx); + + return KNOT_EOK; +} + +int dt_setaffinity(dthread_t *thread, unsigned* cpu_id, size_t cpu_count) +{ + if (thread == NULL) { + return KNOT_EINVAL; + } + +#ifdef HAVE_PTHREAD_SETAFFINITY_NP + int ret = -1; + +/* Linux, FreeBSD interface. */ +#if defined(HAVE_CPUSET_LINUX) || defined(HAVE_CPUSET_BSD) + cpu_set_t set; + CPU_ZERO(&set); + for (unsigned i = 0; i < cpu_count; ++i) { + CPU_SET(cpu_id[i], &set); + } + ret = pthread_setaffinity_np(thread->_thr, sizeof(cpu_set_t), &set); +/* NetBSD interface. */ +#elif defined(HAVE_CPUSET_NETBSD) + cpuset_t *set = cpuset_create(); + if (set == NULL) { + return KNOT_ENOMEM; + } + cpuset_zero(set); + for (unsigned i = 0; i < cpu_count; ++i) { + cpuset_set(cpu_id[i], set); + } + ret = pthread_setaffinity_np(thread->_thr, cpuset_size(set), set); + cpuset_destroy(set); +#endif /* interface */ + + if (ret < 0) { + return KNOT_ERROR; + } + +#else /* HAVE_PTHREAD_SETAFFINITY_NP */ + return KNOT_ENOTSUP; +#endif + + return KNOT_EOK; +} + +int dt_activate(dthread_t *thread) +{ + return dt_update_thread(thread, ThreadActive); +} + +int dt_cancel(dthread_t *thread) +{ + return dt_update_thread(thread, ThreadIdle | ThreadCancelled); +} + +int dt_compact(dt_unit_t *unit) +{ + if (unit == 0) { + return KNOT_EINVAL; + } + + // Lock unit + pthread_mutex_lock(&unit->_notify_mx); + dt_unit_lock(unit); + + // Reclaim all Idle threads + for (int i = 0; i < unit->size; ++i) { + + // Locked state update + dthread_t *thread = unit->threads[i]; + lock_thread_rw(thread); + if (thread->state & (ThreadIdle)) { + thread->state = ThreadDead | ThreadCancelled; + dt_signalize(thread, SIGALRM); + } + unlock_thread_rw(thread); + } + + // Notify all threads + pthread_cond_broadcast(&unit->_notify); + pthread_mutex_unlock(&unit->_notify_mx); + + // Join all threads + for (int i = 0; i < unit->size; ++i) { + + // Reclaim all dead threads + dthread_t *thread = unit->threads[i]; + lock_thread_rw(thread); + if (thread->state & (ThreadDead)) { + unlock_thread_rw(thread); + pthread_join(thread->_thr, 0); + lock_thread_rw(thread); + thread->state = ThreadJoined; + unlock_thread_rw(thread); + } else { + unlock_thread_rw(thread); + } + } + + // Unlock unit + dt_unit_unlock(unit); + + return KNOT_EOK; +} + +int dt_online_cpus(void) +{ + int ret = -1; +/* Linux, Solaris, OS X 10.4+ */ +#ifdef _SC_NPROCESSORS_ONLN + ret = (int) sysconf(_SC_NPROCESSORS_ONLN); +#else +/* FreeBSD, NetBSD, OpenBSD, OS X < 10.4 */ +#if HAVE_SYSCTLBYNAME + size_t rlen = sizeof(int); + if (sysctlbyname("hw.ncpu", &ret, &rlen, NULL, 0) < 0) { + ret = -1; + } +#endif +#endif + return ret; +} + +int dt_optimal_size(void) +{ + int ret = dt_online_cpus(); + if (ret > 1) { + return ret; + } + + return DEFAULT_THR_COUNT; +} + +int dt_is_cancelled(dthread_t *thread) +{ + if (thread == 0) { + return 0; + } + + return thread->state & ThreadCancelled; /* No need to be locked. */ +} + +unsigned dt_get_id(dthread_t *thread) +{ + if (thread == NULL || thread->unit == NULL) { + return 0; + } + + dt_unit_t *unit = thread->unit; + for(int tid = 0; tid < unit->size; ++tid) { + if (thread == unit->threads[tid]) { + return tid; + } + } + + return 0; +} + +int dt_unit_lock(dt_unit_t *unit) +{ + if (unit == 0) { + return KNOT_EINVAL; + } + + int ret = pthread_mutex_lock(&unit->_mx); + if (ret < 0) { + return knot_map_errno(); + } + + return KNOT_EOK; +} + +int dt_unit_unlock(dt_unit_t *unit) +{ + if (unit == 0) { + return KNOT_EINVAL; + } + + int ret = pthread_mutex_unlock(&unit->_mx); + if (ret < 0) { + return knot_map_errno(); + } + + return KNOT_EOK; +} diff --git a/src/knot/server/dthreads.h b/src/knot/server/dthreads.h new file mode 100644 index 0000000..bb6c7b8 --- /dev/null +++ b/src/knot/server/dthreads.h @@ -0,0 +1,294 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \brief Threading API. + * + * Dynamic threads provide: + * - coherent and incoherent threading capabilities + * - thread repurposing + * - thread prioritization + * - on-the-fly changing of threading unit size + * + * Coherent threading unit is when all threads execute + * the same runnable function. + * + * Incoherent function is when at least one thread executes + * a different runnable than the others. + */ + +#pragma once + +#include <pthread.h> + +#define DEFAULT_THR_COUNT 2 /*!< Default thread count. */ + +/* Forward decls */ +struct dthread; +struct dt_unit; + +/*! + * \brief Thread state enumeration. + */ +typedef enum { + ThreadJoined = 1 << 0, /*!< Thread is finished and joined. */ + ThreadJoinable = 1 << 1, /*!< Thread is waiting to be reclaimed. */ + ThreadCancelled = 1 << 2, /*!< Thread is cancelled, finishing task. */ + ThreadDead = 1 << 3, /*!< Thread is finished, exiting. */ + ThreadIdle = 1 << 4, /*!< Thread is idle, waiting for purpose. */ + ThreadActive = 1 << 5 /*!< Thread is active, working on a task. */ +} dt_state_t; + +/*! + * \brief Thread runnable prototype. + * + * Runnable is basically a pointer to function which is called on active + * thread runtime. + * + * \note When implementing a runnable, keep in mind to check thread state as + * it may change, and implement a cooperative cancellation point. + * + * Implement this by checking dt_is_cancelled() and return + * as soon as possible. + */ +typedef int (*runnable_t)(struct dthread *); + +/*! + * \brief Single thread descriptor public API. + */ +typedef struct dthread { + volatile unsigned state; /*!< Bitfield of dt_flag flags. */ + runnable_t run; /*!< Runnable function or 0. */ + runnable_t destruct; /*!< Destructor function or 0. */ + void *data; /*!< Currently active data */ + struct dt_unit *unit; /*!< Reference to assigned unit. */ + void *_adata; /*!< Thread-specific data. */ + pthread_t _thr; /*!< Thread */ + pthread_attr_t _attr; /*!< Thread attributes */ + pthread_mutex_t _mx; /*!< Thread state change lock. */ +} dthread_t; + +/*! + * \brief Thread unit descriptor API. + * + * Thread unit consists of 1..N threads. + * Unit is coherent if all threads execute + * the same runnable. + */ +typedef struct dt_unit { + int size; /*!< Unit width (number of threads) */ + struct dthread **threads; /*!< Array of threads */ + pthread_cond_t _notify; /*!< Notify thread */ + pthread_mutex_t _notify_mx; /*!< Condition mutex */ + pthread_cond_t _report; /*!< Report thread state */ + pthread_mutex_t _report_mx; /*!< Condition mutex */ + pthread_mutex_t _mx; /*!< Unit lock */ +} dt_unit_t; + +/*! + * \brief Create a set of coherent threads. + * + * Coherent means, that the threads will share a common runnable and the data. + * + * \param count Requested thread count. + * \param runnable Runnable function for all threads. + * \param destructor Destructor for all threads. + * \param data Any data passed onto threads. + * + * \retval New instance if successful + * \retval NULL on error + */ +dt_unit_t *dt_create(int count, runnable_t runnable, runnable_t destructor, void *data); + +/*! + * \brief Free unit. + * + * \warning Behavior is undefined if threads are still active, make sure + * to call dt_join() first. + * + * \param unit Unit to be deleted. + */ +void dt_delete(dt_unit_t **unit); + +/*! + * \brief Start all threads in selected unit. + * + * \param unit Unit to be started. + * + * \retval KNOT_EOK on success. + * \retval KNOT_EINVAL on invalid parameters (unit is null). + */ +int dt_start(dt_unit_t *unit); + +/*! + * \brief Send given signal to thread. + * + * \note This is useful to interrupt some blocking I/O as well, for example + * with SIGALRM, which is handled by default. + * \note Signal handler may be overriden in runnable. + * + * \param thread Target thread instance. + * \param signum Signal code. + * + * \retval KNOT_EOK on success. + * \retval KNOT_EINVAL on invalid parameters. + * \retval KNOT_ERROR unspecified error. + */ +int dt_signalize(dthread_t *thread, int signum); + +/*! + * \brief Wait for all thread in unit to finish. + * + * \param unit Unit to be joined. + * + * \retval KNOT_EOK on success. + * \retval KNOT_EINVAL on invalid parameters. + */ +int dt_join(dt_unit_t *unit); + +/*! + * \brief Stop all threads in unit. + * + * Thread is interrupted at the nearest runnable cancellation point. + * + * \param unit Unit to be stopped. + * + * \retval KNOT_EOK on success. + * \retval KNOT_EINVAL on invalid parameters. + */ +int dt_stop(dt_unit_t *unit); + +/*! + * \brief Set thread affinity to masked CPU's. + * + * \param thread Target thread instance. + * \param cpu_id Array of CPU IDs to set affinity to. + * \param cpu_count Number of CPUs in the array, set to 0 for no CPU. + * + * \retval KNOT_EOK on success. + * \retval KNOT_EINVAL on invalid parameters. + */ +int dt_setaffinity(dthread_t *thread, unsigned* cpu_id, size_t cpu_count); + +/*! + * \brief Wake up thread from idle state. + * + * Thread is awoken from idle state and reenters runnable. + * This function only affects idle threads. + * + * \note Unit needs to be started with dt_start() first, as the function + * doesn't affect dead threads. + * + * \param thread Target thread instance. + * + * \retval KNOT_EOK on success. + * \retval KNOT_EINVAL on invalid parameters. + * \retval KNOT_ENOTSUP operation not supported. + */ +int dt_activate(dthread_t *thread); + +/*! + * \brief Put thread to idle state, cancells current runnable function. + * + * Thread is flagged with Cancel flag and returns from runnable at the nearest + * cancellation point, which requires complying runnable function. + * + * \note Thread isn't disposed, but put to idle state until it's requested + * again or collected by dt_compact(). + * + * \param thread Target thread instance. + * + * \retval KNOT_EOK on success. + * \retval KNOT_EINVAL on invalid parameters. + */ +int dt_cancel(dthread_t *thread); + +/*! + * \brief Collect and dispose idle threads. + * + * \param unit Target unit instance. + * + * \retval KNOT_EOK on success. + * \retval KNOT_EINVAL on invalid parameters. + */ +int dt_compact(dt_unit_t *unit); + +/*! + * \brief Return number of online processors. + * + * \retval Number of online CPU's if success. + * \retval <0 on failure. + */ +int dt_online_cpus(void); + +/*! + * \brief Return optimal number of threads for instance. + * + * It is estimated as NUM_CPUs + CONSTANT. + * Fallback is DEFAULT_THR_COUNT (\see common.h). + * + * \return Number of threads. + */ +int dt_optimal_size(void); + +/*! + * \brief Return true if thread is cancelled. + * + * Synchronously check for ThreadCancelled flag. + * + * \param thread Target thread instance. + * + * \retval 1 if cancelled. + * \retval 0 if not cancelled. + */ +int dt_is_cancelled(dthread_t *thread); + +/*! + * \brief Return thread index in threading unit. + * + * \note Returns 0 when thread doesn't have a unit. + * + * \param thread Target thread instance. + * + * \return Thread index. + */ +unsigned dt_get_id(dthread_t *thread); + +/*! + * \brief Lock unit to prevent parallel operations which could alter unit + * at the same time. + * + * \param unit Target unit instance. + * + * \retval KNOT_EOK on success. + * \retval KNOT_EINVAL on invalid parameters. + * \retval KNOT_EAGAIN lack of resources to lock unit, try again. + * \retval KNOT_ERROR unspecified error. + */ +int dt_unit_lock(dt_unit_t *unit); + +/*! + * \brief Unlock unit. + * + * \see dt_unit_lock() + * + * \param unit Target unit instance. + * + * \retval KNOT_EOK on success. + * \retval KNOT_EINVAL on invalid parameters. + * \retval KNOT_EAGAIN lack of resources to unlock unit, try again. + * \retval KNOT_ERROR unspecified error. + */ +int dt_unit_unlock(dt_unit_t *unit); diff --git a/src/knot/server/server.c b/src/knot/server/server.c new file mode 100644 index 0000000..6d4ac44 --- /dev/null +++ b/src/knot/server/server.c @@ -0,0 +1,932 @@ +/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#define __APPLE_USE_RFC_3542 + +#include <stdlib.h> +#include <assert.h> +#include <urcu.h> +#include <netinet/tcp.h> + +#include "libknot/errcode.h" +#include "libknot/yparser/ypschema.h" +#include "knot/common/log.h" +#include "knot/common/stats.h" +#include "knot/conf/confio.h" +#include "knot/conf/migration.h" +#include "knot/conf/module.h" +#include "knot/dnssec/kasp/kasp_db.h" +#include "knot/server/server.h" +#include "knot/server/udp-handler.h" +#include "knot/server/tcp-handler.h" +#include "knot/zone/timers.h" +#include "knot/zone/zonedb-load.h" +#include "knot/worker/pool.h" +#include "contrib/net.h" +#include "contrib/sockaddr.h" +#include "contrib/trim.h" + +/*! \brief Minimal send/receive buffer sizes. */ +enum { + UDP_MIN_RCVSIZE = 4096, + UDP_MIN_SNDSIZE = 4096, + TCP_MIN_RCVSIZE = 4096, + TCP_MIN_SNDSIZE = sizeof(uint16_t) + UINT16_MAX +}; + +/*! \brief Unbind interface and clear the structure. */ +static void server_deinit_iface(iface_t *iface) +{ + /* Free UDP handler. */ + for (int i = 0; i < iface->fd_udp_count; i++) { + if (iface->fd_udp[i] > -1) { + close(iface->fd_udp[i]); + } + } + free(iface->fd_udp); + + /* Free TCP handler. */ + if (iface->fd_tcp > -1) { + close(iface->fd_tcp); + } + + memset(iface, 0, sizeof(*iface)); +} + +/*! \brief Unbind and dispose given interface. */ +static void server_remove_iface(iface_t *iface) +{ + if (!iface) { + return; + } + + server_deinit_iface(iface); + free(iface); +} + +/*! \brief Set lower bound for socket option. */ +static bool setsockopt_min(int sock, int option, int min) +{ + int value = 0; + socklen_t len = sizeof(value); + + if (getsockopt(sock, SOL_SOCKET, option, &value, &len) != 0) { + return false; + } + + assert(len == sizeof(value)); + if (value >= min) { + return true; + } + + return setsockopt(sock, SOL_SOCKET, option, &min, sizeof(min)) == 0; +} + +/*! + * \brief Enlarge send/receive buffers. + */ +static bool enlarge_net_buffers(int sock, int min_recvsize, int min_sndsize) +{ + return setsockopt_min(sock, SO_RCVBUF, min_recvsize) && + setsockopt_min(sock, SO_SNDBUF, min_sndsize); +} + +/*! + * \brief Enable source packet information retrieval. + */ +static bool enable_pktinfo(int sock, int family) +{ + int level = 0; + int option = 0; + + switch (family) { + case AF_INET: + level = IPPROTO_IP; +#if defined(IP_PKTINFO) + option = IP_PKTINFO; /* Linux */ +#elif defined(IP_RECVDSTADDR) + option = IP_RECVDSTADDR; /* BSD */ +#else + return false; +#endif + break; + case AF_INET6: + level = IPPROTO_IPV6; + option = IPV6_RECVPKTINFO; + break; + default: + return false; + } + + const int on = 1; + return setsockopt(sock, level, option, &on, sizeof(on)) == 0; +} + +/** + * \brief Enable TCP Fast Open. + */ +static int enable_fastopen(int sock, int backlog) +{ +#if defined(TCP_FASTOPEN) +#if __APPLE__ + if (backlog > 0) { + backlog = 1; // just on-off switch on macOS + } +#endif + if (setsockopt(sock, IPPROTO_TCP, TCP_FASTOPEN, &backlog, sizeof(backlog)) != 0) { + return knot_map_errno(); + } +#endif + return KNOT_EOK; +} + +/*! + * \brief Initialize new interface from config value. + * + * Both TCP and UDP sockets will be created for the interface. + * + * \param new_if Allocated memory for the interface. + * \param cfg_if Interface template from config. + * + * \retval 0 if successful (EOK). + * \retval <0 on errors (EACCES, EINVAL, ENOMEM, EADDRINUSE). + */ +static int server_init_iface(iface_t *new_if, struct sockaddr_storage *addr, int udp_thread_count) +{ + /* Initialize interface. */ + int ret = 0; + memset(new_if, 0, sizeof(iface_t)); + memcpy(&new_if->addr, addr, sizeof(struct sockaddr_storage)); + + /* Convert to string address format. */ + char addr_str[SOCKADDR_STRLEN] = { 0 }; + sockaddr_tostr(addr_str, sizeof(addr_str), (struct sockaddr *)addr); + + int udp_socket_count = 1; + int udp_bind_flags = 0; + +#ifdef ENABLE_REUSEPORT + udp_socket_count = udp_thread_count; + udp_bind_flags |= NET_BIND_MULTIPLE; +#endif + + new_if->fd_udp = malloc(udp_socket_count * sizeof(int)); + if (!new_if->fd_udp) { + return KNOT_ENOMEM; + } + + /* Initialize the sockets to ensure safe early deinitialization. */ + for (int i = 0; i < udp_socket_count; i++) { + new_if->fd_udp[i] = -1; + } + new_if->fd_tcp = -1; + + bool warn_bind = false; + bool warn_bufsize = false; + + /* Create bound UDP sockets. */ + for (int i = 0; i < udp_socket_count; i++ ) { + int sock = net_bound_socket(SOCK_DGRAM, (struct sockaddr *)addr, udp_bind_flags); + if (sock == KNOT_EADDRNOTAVAIL) { + udp_bind_flags |= NET_BIND_NONLOCAL; + sock = net_bound_socket(SOCK_DGRAM, (struct sockaddr *)addr, udp_bind_flags); + if (sock >= 0 && !warn_bind) { + log_warning("address %s UDP bound, but required nonlocal bind", addr_str); + warn_bind = true; + } + } + + if (sock < 0) { + log_error("cannot bind address %s (%s)", addr_str, + knot_strerror(sock)); + server_deinit_iface(new_if); + return sock; + } + + if (!enlarge_net_buffers(sock, UDP_MIN_RCVSIZE, UDP_MIN_SNDSIZE) && + !warn_bufsize) { + log_warning("failed to set network buffer sizes for UDP"); + warn_bufsize = true; + } + + if (sockaddr_is_any((struct sockaddr *)addr) && !enable_pktinfo(sock, addr->ss_family)) { + log_warning("failed to enable received packet information retrieval"); + } + + new_if->fd_udp[new_if->fd_udp_count] = sock; + new_if->fd_udp_count += 1; + } + + /* Create bound TCP socket. */ + int tcp_bind_flags = 0; + int sock = net_bound_socket(SOCK_STREAM, (struct sockaddr *)addr, tcp_bind_flags); + if (sock == KNOT_EADDRNOTAVAIL) { + tcp_bind_flags |= NET_BIND_NONLOCAL; + sock = net_bound_socket(SOCK_STREAM, (struct sockaddr *)addr, tcp_bind_flags); + if (sock >= 0) { + log_warning("address %s TCP bound, but required nonlocal bind", addr_str); + } + } + + if (sock < 0) { + log_error("cannot bind address %s (%s)", addr_str, + knot_strerror(sock)); + server_deinit_iface(new_if); + return sock; + } + + if (!enlarge_net_buffers(sock, TCP_MIN_RCVSIZE, TCP_MIN_SNDSIZE)) { + log_warning("failed to set network buffer sizes for TCP"); + } + + new_if->fd_tcp = sock; + + /* Listen for incoming connections. */ + ret = listen(sock, TCP_BACKLOG_SIZE); + if (ret < 0) { + log_error("failed to listen on TCP interface %s", addr_str); + server_deinit_iface(new_if); + return KNOT_ERROR; + } + + /* TCP Fast Open. */ + ret = enable_fastopen(sock, TCP_BACKLOG_SIZE); + if (ret < 0) { + log_warning("failed to enable TCP Fast Open on %s (%s)", + addr_str, knot_strerror(ret)); + } + + return KNOT_EOK; +} + +static void remove_ifacelist(struct ref *p) +{ + ifacelist_t *ifaces = (ifacelist_t *)p; + + /* Remove deprecated interfaces. */ + char addr_str[SOCKADDR_STRLEN] = {0}; + iface_t *n = NULL, *m = NULL; + WALK_LIST_DELSAFE(n, m, ifaces->u) { + sockaddr_tostr(addr_str, sizeof(addr_str), (struct sockaddr *)&n->addr); + log_info("removing interface %s", addr_str); + server_remove_iface(n); + } + WALK_LIST_DELSAFE(n, m, ifaces->l) { + free(n); + } + + free(ifaces); +} + +/*! + * \brief Update bound sockets according to configuration. + * + * \param server Server instance. + * \return number of added sockets. + */ +static int reconfigure_sockets(conf_t *conf, server_t *s) +{ + /* Prepare helper lists. */ + int bound = 0; + ifacelist_t *oldlist = s->ifaces; + ifacelist_t *newlist = malloc(sizeof(ifacelist_t)); + ref_init(&newlist->ref, &remove_ifacelist); + ref_retain(&newlist->ref); + init_list(&newlist->u); + init_list(&newlist->l); + + /* Duplicate current list. */ + /*! \note Pointers to addr, handlers etc. will be shared. */ + if (s->ifaces) { + list_dup(&s->ifaces->u, &s->ifaces->l, sizeof(iface_t)); + } + + /* Update bound interfaces. */ + conf_val_t listen_val = conf_get(conf, C_SRV, C_LISTEN); + conf_val_t rundir_val = conf_get(conf, C_SRV, C_RUNDIR); + char *rundir = conf_abs_path(&rundir_val, NULL); + while (listen_val.code == KNOT_EOK) { + iface_t *m = NULL; + + /* Find already matching interface. */ + int found_match = 0; + struct sockaddr_storage addr = conf_addr(&listen_val, rundir); + if (s->ifaces) { + WALK_LIST(m, s->ifaces->u) { + /* Matching port and address. */ + if (sockaddr_cmp((struct sockaddr *)&addr, + (struct sockaddr *)&m->addr) == 0) { + found_match = 1; + break; + } + } + } + + /* Found already bound interface. */ + if (found_match) { + rem_node((node_t *)m); + } else { + char addr_str[SOCKADDR_STRLEN] = { 0 }; + sockaddr_tostr(addr_str, sizeof(addr_str), (struct sockaddr *)&addr); + log_info("binding to interface %s", addr_str); + + /* Create new interface. */ + m = malloc(sizeof(iface_t)); + unsigned size = s->handlers[IO_UDP].handler.unit->size; + if (server_init_iface(m, &addr, size) < 0) { + free(m); + m = 0; + } + } + + /* Move to new list. */ + if (m) { + add_tail(&newlist->l, (node_t *)m); + ++bound; + } + + conf_val_next(&listen_val); + } + free(rundir); + + /* Wait for readers that are reconfiguring right now. */ + /*! \note This subsystem will be reworked in #239 */ + for (unsigned proto = IO_UDP; proto <= IO_TCP; ++proto) { + dt_unit_t *tu = s->handlers[proto].handler.unit; + iohandler_t *ioh = &s->handlers[proto].handler; + for (unsigned i = 0; i < tu->size; ++i) { + while (ioh->thread_state[i] & ServerReload) { + sleep(1); + } + } + } + + /* Publish new list. */ + s->ifaces = newlist; + + /* Update TCP+UDP ifacelist (reload all threads). */ + unsigned thread_count = 0; + for (unsigned proto = IO_UDP; proto <= IO_TCP; ++proto) { + dt_unit_t *tu = s->handlers[proto].handler.unit; + for (unsigned i = 0; i < tu->size; ++i) { + ref_retain((ref_t *)newlist); + s->handlers[proto].handler.thread_state[i] |= ServerReload; + s->handlers[proto].handler.thread_id[i] = thread_count++; + if (s->state & ServerRunning) { + dt_activate(tu->threads[i]); + dt_signalize(tu->threads[i], SIGALRM); + } + } + } + + ref_release(&oldlist->ref); + + return bound; +} + +int server_init(server_t *server, int bg_workers) +{ + if (server == NULL) { + return KNOT_EINVAL; + } + + /* Clear the structure. */ + memset(server, 0, sizeof(server_t)); + + /* Initialize event scheduler. */ + if (evsched_init(&server->sched, server) != KNOT_EOK) { + return KNOT_ENOMEM; + } + + server->workers = worker_pool_create(bg_workers); + if (server->workers == NULL) { + evsched_deinit(&server->sched); + return KNOT_ENOMEM; + } + + char *journal_dir = conf_journalfile(conf()); + conf_val_t journal_size = conf_default_get(conf(), C_MAX_JOURNAL_DB_SIZE); + conf_val_t journal_mode = conf_default_get(conf(), C_JOURNAL_DB_MODE); + int ret = journal_db_init(&server->journal_db, journal_dir, + conf_int(&journal_size), conf_opt(&journal_mode)); + free(journal_dir); + if (ret != KNOT_EOK) { + worker_pool_destroy(server->workers); + evsched_deinit(&server->sched); + return ret; + } + + char *kasp_dir = conf_kaspdir(conf()); + conf_val_t kasp_size = conf_default_get(conf(), C_MAX_KASP_DB_SIZE); + ret = kasp_db_init(kaspdb(), kasp_dir, conf_int(&kasp_size)); + free(kasp_dir); + if (ret != KNOT_EOK) { + journal_db_close(&server->journal_db); + worker_pool_destroy(server->workers); + evsched_deinit(&server->sched); + return ret; + } + + return KNOT_EOK; +} + +void server_deinit(server_t *server) +{ + if (server == NULL) { + return; + } + + /* Free remaining interfaces. */ + if (server->ifaces) { + iface_t *n = NULL, *m = NULL; + WALK_LIST_DELSAFE(n, m, server->ifaces->l) { + server_remove_iface(n); + } + free(server->ifaces); + } + + /* Free threads and event handlers. */ + worker_pool_destroy(server->workers); + + /* Free zone database. */ + knot_zonedb_deep_free(&server->zone_db); + + /* Free remaining events. */ + evsched_deinit(&server->sched); + + /* Close kasp_db. */ + kasp_db_close(kaspdb()); + + /* Close journal database if open. */ + journal_db_close(&server->journal_db); + + /* Close persistent timers database. */ + zone_timers_close(server->timers_db); + + /* Clear the structure. */ + memset(server, 0, sizeof(server_t)); +} + +static int server_init_handler(server_t *server, int index, int thread_count, + runnable_t runnable, runnable_t destructor) +{ + /* Initialize */ + iohandler_t *h = &server->handlers[index].handler; + memset(h, 0, sizeof(iohandler_t)); + h->server = server; + h->unit = dt_create(thread_count, runnable, destructor, h); + if (h->unit == NULL) { + return KNOT_ENOMEM; + } + + h->thread_state = calloc(thread_count, sizeof(unsigned)); + if (h->thread_state == NULL) { + dt_delete(&h->unit); + return KNOT_ENOMEM; + } + + h->thread_id = calloc(thread_count, sizeof(unsigned)); + if (h->thread_id == NULL) { + free(h->thread_state); + dt_delete(&h->unit); + return KNOT_ENOMEM; + } + + return KNOT_EOK; +} + +static void server_free_handler(iohandler_t *h) +{ + if (h == NULL || h->server == NULL) { + return; + } + + /* Wait for threads to finish */ + if (h->unit) { + dt_stop(h->unit); + dt_join(h->unit); + } + + /* Destroy worker context. */ + dt_delete(&h->unit); + free(h->thread_state); + free(h->thread_id); + memset(h, 0, sizeof(iohandler_t)); +} + +int server_start(server_t *server, bool async) +{ + if (server == NULL) { + return KNOT_EINVAL; + } + + /* Start workers. */ + worker_pool_start(server->workers); + + /* Wait for enqueued events if not asynchronous. */ + if (!async) { + worker_pool_wait(server->workers); + } + + /* Start evsched handler. */ + evsched_start(&server->sched); + + /* Start I/O handlers. */ + server->state |= ServerRunning; + for (int proto = IO_UDP; proto <= IO_TCP; ++proto) { + if (server->handlers[proto].size > 0) { + int ret = dt_start(server->handlers[proto].handler.unit); + if (ret != KNOT_EOK) { + return ret; + } + } + } + + return KNOT_EOK; +} + +void server_wait(server_t *server) +{ + if (server == NULL) { + return; + } + + evsched_join(&server->sched); + worker_pool_join(server->workers); + + for (int proto = IO_UDP; proto <= IO_TCP; ++proto) { + if (server->handlers[proto].size > 0) { + server_free_handler(&server->handlers[proto].handler); + } + } +} + +static int reload_conf(conf_t *new_conf) +{ + yp_schema_purge_dynamic(new_conf->schema); + + /* Re-load common modules. */ + int ret = conf_mod_load_common(new_conf); + if (ret != KNOT_EOK) { + return ret; + } + + /* Re-import zonefile if specified. */ + const char *filename = conf()->filename; + if (filename != NULL) { + log_info("reloading configuration file '%s'", filename); + + /* Import the configuration file. */ + ret = conf_import(new_conf, filename, true); + if (ret != KNOT_EOK) { + log_error("failed to load configuration file (%s)", + knot_strerror(ret)); + return ret; + } + } else { + log_info("reloading configuration database"); + + /* Re-load extra modules. */ + for (conf_iter_t iter = conf_iter(new_conf, C_MODULE); + iter.code == KNOT_EOK; conf_iter_next(new_conf, &iter)) { + conf_val_t id = conf_iter_id(new_conf, &iter); + conf_val_t file = conf_id_get(new_conf, C_MODULE, C_FILE, &id); + ret = conf_mod_load_extra(new_conf, conf_str(&id), conf_str(&file), false); + if (ret != KNOT_EOK) { + conf_iter_finish(new_conf, &iter); + return ret; + } + } + } + + conf_mod_load_purge(new_conf, false); + + // Migrate from old schema. + ret = conf_migrate(new_conf); + if (ret != KNOT_EOK) { + log_error("failed to migrate configuration (%s)", knot_strerror(ret)); + } + + /* Refresh hostname. */ + conf_refresh_hostname(new_conf); + + return KNOT_EOK; +} + +int server_reload(server_t *server) +{ + if (server == NULL) { + return KNOT_EINVAL; + } + + /* Check for no edit mode. */ + if (conf()->io.txn != NULL) { + log_warning("reload aborted due to active configuration transaction"); + return KNOT_TXN_EEXISTS; + } + + conf_t *new_conf = NULL; + int ret = conf_clone(&new_conf); + if (ret != KNOT_EOK) { + log_error("failed to initialize configuration (%s)", + knot_strerror(ret)); + return ret; + } + + yp_flag_t flags = conf()->io.flags; + bool full = !(flags & CONF_IO_FACTIVE); + bool reuse_modules = !full && !(flags & CONF_IO_FRLD_MOD); + + /* Reload configuration and modules if full reload or a module change. */ + if (full || !reuse_modules) { + ret = reload_conf(new_conf); + if (ret != KNOT_EOK) { + conf_free(new_conf); + return ret; + } + + conf_activate_modules(new_conf, NULL, new_conf->query_modules, + &new_conf->query_plan); + } + + conf_update_flag_t upd_flags = CONF_UPD_FNOFREE; + if (!full) { + upd_flags |= CONF_UPD_FCONFIO; + } + if (reuse_modules) { + upd_flags |= CONF_UPD_FMODULES; + } + + /* Update to the new config. */ + conf_t *old_conf = conf_update(new_conf, upd_flags); + + /* Reload each component if full reload or a specific one if required. */ + if (full || (flags & CONF_IO_FRLD_LOG)) { + log_reconfigure(conf()); + } + if (full || (flags & CONF_IO_FRLD_SRV)) { + server_reconfigure(conf(), server); + stats_reconfigure(conf(), server); + } + if (full || (flags & (CONF_IO_FRLD_ZONES | CONF_IO_FRLD_ZONE))) { + server_update_zones(conf(), server); + } + + /* Free old config needed for module unload in zone reload. */ + conf_free(old_conf); + + if (full) { + log_info("configuration reloaded"); + } else { + // Reset confio reload context. + conf()->io.flags = YP_FNONE; + if (conf()->io.zones != NULL) { + trie_clear(conf()->io.zones); + } + } + + return KNOT_EOK; +} + +void server_stop(server_t *server) +{ + log_info("stopping server"); + + /* Stop scheduler. */ + evsched_stop(&server->sched); + /* Interrupt background workers. */ + worker_pool_stop(server->workers); + + /* Clear 'running' flag. */ + server->state &= ~ServerRunning; +} + +static int reset_handler(server_t *server, int index, unsigned size, runnable_t run) +{ + if (server->handlers[index].size != size) { + /* Free old handlers */ + if (server->handlers[index].size > 0) { + server_free_handler(&server->handlers[index].handler); + } + + /* Initialize I/O handlers. */ + int ret = server_init_handler(server, index, size, run, NULL); + if (ret != KNOT_EOK) { + return ret; + } + + /* Start if server is running. */ + if (server->state & ServerRunning) { + ret = dt_start(server->handlers[index].handler.unit); + if (ret != KNOT_EOK) { + return ret; + } + } + server->handlers[index].size = size; + } + + return KNOT_EOK; +} + +/*! \brief Reconfigure UDP and TCP query processing threads. */ +static int reconfigure_threads(conf_t *conf, server_t *server) +{ + int ret = reset_handler(server, IO_UDP, conf_udp_threads(conf), udp_master); + if (ret != KNOT_EOK) { + return ret; + } + + return reset_handler(server, IO_TCP, conf_tcp_threads(conf), tcp_master); +} + +static int reconfigure_journal_db(conf_t *conf, server_t *server) +{ + char *journal_dir = conf_journalfile(conf); + conf_val_t journal_size = conf_default_get(conf, C_MAX_JOURNAL_DB_SIZE); + conf_val_t journal_mode = conf_default_get(conf, C_JOURNAL_DB_MODE); + bool changed_path = (strcmp(journal_dir, server->journal_db->path) != 0); + bool changed_size = (conf_int(&journal_size) != server->journal_db->fslimit); + bool changed_mode = (conf_opt(&journal_mode) != server->journal_db->mode); + int ret = KNOT_EOK; + + if (server->journal_db->db != NULL) { + if (changed_path) { + log_warning("ignored reconfiguration of journal DB path (already open)"); + } + if (changed_size) { + log_warning("ignored reconfiguration of journal DB max size (already open)"); + } + if (changed_mode) { + log_warning("ignored reconfiguration of journal DB mode (already open)"); + } + } else if (changed_path || changed_size || changed_mode) { + journal_db_t *newjdb = NULL; + ret = journal_db_init(&newjdb, journal_dir, conf_int(&journal_size), + conf_opt(&journal_mode)); + if (ret == KNOT_EOK) { + journal_db_close(&server->journal_db); + server->journal_db = newjdb; + } + } + free(journal_dir); + + return ret; +} + +static int reconfigure_kasp_db(conf_t *conf, server_t *server) +{ + char *kasp_dir = conf_kaspdir(conf); + conf_val_t kasp_size = conf_default_get(conf, C_MAX_KASP_DB_SIZE); + int ret = kasp_db_reconfigure(kaspdb(), kasp_dir, conf_int(&kasp_size)); + switch (ret) { + case KNOT_EBUSY: + log_warning("ignored reconfiguration of KASP DB path (already open)"); + break; + case KNOT_EEXIST: + ret = KNOT_EBUSY; + log_warning("ignored reconfiguration of KASP DB max size (already open)"); + break; + case KNOT_ENODIFF: + case KNOT_EOK: + ret = KNOT_EOK; + break; + default: + break; + } + free(kasp_dir); + + return ret; +} + +void server_reconfigure(conf_t *conf, server_t *server) +{ + if (conf == NULL || server == NULL) { + return; + } + + /* First reconfiguration. */ + if (!(server->state & ServerRunning)) { + log_info("Knot DNS %s starting", PACKAGE_VERSION); + } + + /* Reconfigure server threads. */ + int ret; + if ((ret = reconfigure_threads(conf, server)) < 0) { + log_error("failed to reconfigure server threads (%s)", + knot_strerror(ret)); + } + + /* Reconfigure journal DB. */ + if ((ret = reconfigure_journal_db(conf, server)) < 0) { + log_error("failed to reconfigure journal DB (%s)", + knot_strerror(ret)); + } + + /* Reconfigure KASP DB. */ + if ((ret = reconfigure_kasp_db(conf, server)) < 0) { + log_error("failed to reconfigure KASP DB (%s)", + knot_strerror(ret)); + } + + /* Update bound sockets. */ + if ((ret = reconfigure_sockets(conf, server)) < 0) { + log_error("failed to reconfigure server sockets (%s)", + knot_strerror(ret)); + } +} + +static void reopen_timers_database(conf_t *conf, server_t *server) +{ + zone_timers_close(server->timers_db); + server->timers_db = NULL; + + conf_val_t val = conf_default_get(conf, C_STORAGE); + char *storage = conf_abs_path(&val, NULL); + val = conf_default_get(conf, C_TIMER_DB); + char *timer_db = conf_abs_path(&val, storage); + free(storage); + val = conf_default_get(conf, C_MAX_TIMER_DB_SIZE); + size_t mapsize = conf_int(&val); + + int ret = zone_timers_open(timer_db, &server->timers_db, mapsize); + if (ret != KNOT_EOK) { + log_warning("cannot open persistent timer DB '%s' (%s)", + timer_db, knot_strerror(ret)); + } + + free(timer_db); +} + +void server_update_zones(conf_t *conf, server_t *server) +{ + if (conf == NULL || server == NULL) { + return; + } + + /* Prevent emitting of new zone events. */ + if (server->zone_db) { + knot_zonedb_foreach(server->zone_db, zone_events_freeze); + } + + /* Suspend adding events to worker pool queue, wait for queued events. */ + evsched_pause(&server->sched); + worker_pool_wait(server->workers); + + /* Reload zone database and free old zones. */ + reopen_timers_database(conf, server); + zonedb_reload(conf, server); + + /* Trim extra heap. */ + mem_trim(); + + /* Resume processing events on new zones. */ + evsched_resume(&server->sched); + if (server->zone_db) { + knot_zonedb_foreach(server->zone_db, zone_events_start); + } +} + +ref_t *server_set_ifaces(server_t *server, fdset_t *fds, int index, int thread_id) +{ + if (server == NULL || server->ifaces == NULL || fds == NULL) { + return NULL; + } + + rcu_read_lock(); + fdset_clear(fds); + + iface_t *i = NULL; + WALK_LIST(i, server->ifaces->l) { +#ifdef ENABLE_REUSEPORT + int udp_id = thread_id % i->fd_udp_count; +#else + int udp_id = 0; +#endif + switch(index) { + case IO_TCP: + fdset_add(fds, i->fd_tcp, POLLIN, NULL); + break; + case IO_UDP: + fdset_add(fds, i->fd_udp[udp_id], POLLIN, NULL); + break; + default: + assert(0); + } + } + rcu_read_unlock(); + + return &server->ifaces->ref; +} diff --git a/src/knot/server/server.h b/src/knot/server/server.h new file mode 100644 index 0000000..76fcc86 --- /dev/null +++ b/src/knot/server/server.h @@ -0,0 +1,181 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "sys/socket.h" + +#include "knot/conf/conf.h" +#include "knot/common/evsched.h" +#include "knot/common/fdset.h" +#include "knot/server/dthreads.h" +#include "knot/common/ref.h" +#include "knot/worker/pool.h" +#include "knot/zone/zonedb.h" +#include "contrib/ucw/lists.h" + +/* Forwad declarations. */ +struct server; + +/*! \brief I/O handler structure. + */ +typedef struct iohandler { + struct node n; + struct server *server; /*!< Reference to server */ + dt_unit_t *unit; /*!< Threading unit */ + unsigned *thread_state; /*!< Thread state */ + unsigned *thread_id; /*!< Thread identifier. */ +} iohandler_t; + +/*! \brief Server state flags. + */ +typedef enum { + ServerIdle = 0 << 0, /*!< Server is idle. */ + ServerRunning = 1 << 0, /*!< Server is running. */ + ServerReload = 1 << 1 /*!< Server reload requested. */ +} server_state; + +/*! + * \brief Server interface structure. + */ +typedef struct iface { + struct node n; + int *fd_udp; + int fd_udp_count; + int fd_tcp; + struct sockaddr_storage addr; +} iface_t; + +/* Handler indexes. */ +enum { + IO_UDP = 0, + IO_TCP = 1 +}; + +typedef struct ifacelist { + ref_t ref; + list_t l; + list_t u; +} ifacelist_t; + +/*! + * \brief Main server structure. + * + * Keeps references to all important structures needed for operation. + */ +typedef struct server { + + /*! \brief Server state tracking. */ + volatile unsigned state; + + /*! \brief Zone database. */ + knot_zonedb_t *zone_db; + knot_db_t *timers_db; + journal_db_t *journal_db; + + /*! \brief I/O handlers. */ + struct { + unsigned size; + iohandler_t handler; + } handlers[2]; + + /*! \brief Background jobs. */ + worker_pool_t *workers; + + /*! \brief Event scheduler. */ + evsched_t sched; + + /*! \brief List of interfaces. */ + ifacelist_t *ifaces; + +} server_t; + +/*! + * \brief Initializes the server structure. + * + * \retval KNOT_EOK on success. + * \retval KNOT_EINVAL on invalid parameters. + */ +int server_init(server_t *server, int bg_workers); + +/*! + * \brief Properly destroys the server structure. + * + * \param server Server structure to be used for operation. + */ +void server_deinit(server_t *server); + +/*! + * \brief Starts the server. + * + * \param server Server structure to be used for operation. + * \param async Don't wait for zones to load if true. + * + * \retval KNOT_EOK on success. + * \retval KNOT_EINVAL on invalid parameters. + * + */ +int server_start(server_t *server, bool async); + +/*! + * \brief Waits for the server to finish. + * + * \param server Server structure to be used for operation. + * + */ +void server_wait(server_t *server); + +/*! + * \brief Reload server configuration. + * + * \param server Server instance. + * + * \return Error code, KNOT_EOK if success. + */ +int server_reload(server_t *server); + +/*! + * \brief Requests server to stop. + * + * \param server Server structure to be used for operation. + */ +void server_stop(server_t *server); + +/*! + * \brief Server reconfiguration routine. + * + * Routine for dynamic server reconfiguration. + */ +void server_reconfigure(conf_t *conf, server_t *server); + +/*! + * \brief Reconfigure zone database. + * + * Routine for dynamic server zones reconfiguration. + */ +void server_update_zones(conf_t *conf, server_t *server); + +/*! + * \brief Update fdsets from current interfaces list. + * + * \param server Server. + * \param fds File descriptor set. + * \param index I/O index (UDP/TCP). + * \param thread_id Thread ID used for geting UDP ID. + * + * \return new interface list + */ +ref_t *server_set_ifaces(server_t *server, fdset_t *fds, int index, int thread_id); diff --git a/src/knot/server/tcp-handler.c b/src/knot/server/tcp-handler.c new file mode 100644 index 0000000..2a0a6ff --- /dev/null +++ b/src/knot/server/tcp-handler.c @@ -0,0 +1,373 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/tcp.h> +#include <netinet/in.h> +#include <stdio.h> +#include <stdlib.h> +#include <urcu.h> +#ifdef HAVE_SYS_UIO_H // struct iovec (OpenBSD) +#include <sys/uio.h> +#endif // HAVE_SYS_UIO_H + +#include "libdnssec/random.h" +#include "knot/server/server.h" +#include "knot/server/tcp-handler.h" +#include "knot/common/log.h" +#include "knot/nameserver/process_query.h" +#include "knot/query/layer.h" +#include "contrib/macros.h" +#include "contrib/mempattern.h" +#include "contrib/net.h" +#include "contrib/sockaddr.h" +#include "contrib/time.h" +#include "contrib/ucw/mempool.h" + +/*! \brief TCP context data. */ +typedef struct tcp_context { + knot_layer_t layer; /*!< Query processing layer. */ + server_t *server; /*!< Name server structure. */ + struct iovec iov[2]; /*!< TX/RX buffers. */ + unsigned client_threshold; /*!< Index of first TCP client. */ + struct timespec last_poll_time; /*!< Time of the last socket poll. */ + struct timespec throttle_end; /*!< End of accept() throttling. */ + fdset_t set; /*!< Set of server/client sockets. */ + unsigned thread_id; /*!< Thread identifier. */ +} tcp_context_t; + +#define TCP_SWEEP_INTERVAL 2 /*!< [secs] granularity of connection sweeping. */ +#define TCP_THROTTLE_LO 0 /*!< Minimum recovery time on errors. */ +#define TCP_THROTTLE_HI 2 /*!< Maximum recovery time on errors. */ + +/*! \brief Calculate TCP throttle time (random). */ +static inline int tcp_throttle(void) { + return TCP_THROTTLE_LO + (dnssec_random_uint16_t() % TCP_THROTTLE_HI); +} + +/*! \brief Sweep TCP connection. */ +static enum fdset_sweep_state tcp_sweep(fdset_t *set, int i, void *data) +{ + UNUSED(data); + assert(set && i < set->n && i >= 0); + int fd = set->pfd[i].fd; + + /* Best-effort, name and shame. */ + struct sockaddr_storage ss; + socklen_t len = sizeof(struct sockaddr_storage); + if (getpeername(fd, (struct sockaddr*)&ss, &len) == 0) { + char addr_str[SOCKADDR_STRLEN] = {0}; + sockaddr_tostr(addr_str, sizeof(addr_str), (struct sockaddr *)&ss); + log_notice("TCP, terminated inactive client, address %s", addr_str); + } + + close(fd); + + return FDSET_SWEEP; +} + +static bool tcp_active_state(int state) +{ + return (state == KNOT_STATE_PRODUCE || state == KNOT_STATE_FAIL); +} + +static bool tcp_send_state(int state) +{ + return (state != KNOT_STATE_FAIL && state != KNOT_STATE_NOOP); +} + +static int tcp_handle(tcp_context_t *tcp, int fd, + struct iovec *rx, struct iovec *tx) +{ + /* Get peer name. */ + struct sockaddr_storage ss; + socklen_t addrlen = sizeof(struct sockaddr_storage); + if (getpeername(fd, (struct sockaddr *)&ss, &addrlen) != 0) { + return KNOT_EINVAL; + } + + /* Create query processing parameter. */ + knotd_qdata_params_t params = { + .remote = &ss, + .socket = fd, + .server = tcp->server, + .thread_id = tcp->thread_id + }; + + rx->iov_len = KNOT_WIRE_MAX_PKTSIZE; + tx->iov_len = KNOT_WIRE_MAX_PKTSIZE; + + /* Timeout. */ + rcu_read_lock(); + int timeout = 1000 * conf()->cache.srv_tcp_reply_timeout; + rcu_read_unlock(); + + /* Receive data. */ + int ret = net_dns_tcp_recv(fd, rx->iov_base, rx->iov_len, timeout); + if (ret <= 0) { + if (ret == KNOT_EAGAIN) { + char addr_str[SOCKADDR_STRLEN] = {0}; + sockaddr_tostr(addr_str, sizeof(addr_str), (struct sockaddr *)&ss); + log_warning("TCP, connection timed out, address %s", + addr_str); + } + return KNOT_ECONNREFUSED; + } else { + rx->iov_len = ret; + } + + /* Initialize processing layer. */ + knot_layer_begin(&tcp->layer, ¶ms); + + /* Create packets. */ + knot_pkt_t *ans = knot_pkt_new(tx->iov_base, tx->iov_len, tcp->layer.mm); + knot_pkt_t *query = knot_pkt_new(rx->iov_base, rx->iov_len, tcp->layer.mm); + + /* Input packet. */ + (void) knot_pkt_parse(query, 0); + knot_layer_consume(&tcp->layer, query); + + /* Resolve until NOOP or finished. */ + ret = KNOT_EOK; + while (tcp_active_state(tcp->layer.state)) { + knot_layer_produce(&tcp->layer, ans); + /* Send, if response generation passed and wasn't ignored. */ + if (ans->size > 0 && tcp_send_state(tcp->layer.state)) { + if (net_dns_tcp_send(fd, ans->wire, ans->size, timeout) != ans->size) { + ret = KNOT_ECONNREFUSED; + break; + } + } + } + + /* Reset after processing. */ + knot_layer_finish(&tcp->layer); + + /* Flush per-query memory (including query and answer packets). */ + mp_flush(tcp->layer.mm->ctx); + + return ret; +} + +static int tcp_accept(int fd) +{ + /* Accept incoming connection. */ + int incoming = net_accept(fd, NULL); + + /* Evaluate connection. */ + if (incoming >= 0) { +#ifdef SO_RCVTIMEO + struct timeval tv; + rcu_read_lock(); + tv.tv_sec = conf()->cache.srv_tcp_idle_timeout; + rcu_read_unlock(); + tv.tv_usec = 0; + if (setsockopt(incoming, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) { + log_warning("TCP, failed to set up watchdog timer" + ", fd %d", incoming); + } +#endif + } + + return incoming; +} + +static int tcp_event_accept(tcp_context_t *tcp, unsigned i) +{ + /* Accept client. */ + int fd = tcp->set.pfd[i].fd; + int client = tcp_accept(fd); + if (client >= 0) { + /* Assign to fdset. */ + int next_id = fdset_add(&tcp->set, client, POLLIN, NULL); + if (next_id < 0) { + close(client); + return next_id; /* Contains errno. */ + } + + /* Update watchdog timer. */ + rcu_read_lock(); + int timeout = conf()->cache.srv_tcp_hshake_timeout; + fdset_set_watchdog(&tcp->set, next_id, timeout); + rcu_read_unlock(); + + return KNOT_EOK; + } + + return client; +} + +static int tcp_event_serve(tcp_context_t *tcp, unsigned i) +{ + int fd = tcp->set.pfd[i].fd; + int ret = tcp_handle(tcp, fd, &tcp->iov[0], &tcp->iov[1]); + if (ret == KNOT_EOK) { + /* Update socket activity timer. */ + rcu_read_lock(); + int timeout = conf()->cache.srv_tcp_idle_timeout; + fdset_set_watchdog(&tcp->set, i, timeout); + rcu_read_unlock(); + } + + return ret; +} + +static int tcp_wait_for_events(tcp_context_t *tcp) +{ + /* Wait for events. */ + fdset_t *set = &tcp->set; + int nfds = poll(set->pfd, set->n, TCP_SWEEP_INTERVAL * 1000); + + /* Mark the time of last poll call. */ + tcp->last_poll_time = time_now(); + bool is_throttled = (tcp->last_poll_time.tv_sec < tcp->throttle_end.tv_sec); + if (!is_throttled) { + /* Configuration limit, infer maximal pool size. */ + rcu_read_lock(); + int clients = conf()->cache.srv_max_tcp_clients; + unsigned max_per_set = MAX(clients / conf_tcp_threads(conf()), 1); + rcu_read_unlock(); + /* Subtract master sockets check limits. */ + is_throttled = (set->n - tcp->client_threshold) >= max_per_set; + } + + /* Process events. */ + unsigned i = 0; + while (nfds > 0 && i < set->n) { + bool should_close = false; + int fd = set->pfd[i].fd; + if (set->pfd[i].revents & (POLLERR|POLLHUP|POLLNVAL)) { + should_close = (i >= tcp->client_threshold); + --nfds; + } else if (set->pfd[i].revents & (POLLIN)) { + /* Master sockets */ + if (i < tcp->client_threshold) { + if (!is_throttled && tcp_event_accept(tcp, i) == KNOT_EBUSY) { + tcp->throttle_end = time_now(); + tcp->throttle_end.tv_sec += tcp_throttle(); + } + /* Client sockets */ + } else { + if (tcp_event_serve(tcp, i) != KNOT_EOK) { + should_close = true; + } + } + --nfds; + } + + /* Evaluate */ + if (should_close) { + fdset_remove(set, i); + close(fd); + } else { + ++i; + } + } + + return nfds; +} + +int tcp_master(dthread_t *thread) +{ + if (thread == NULL || thread->data == NULL) { + return KNOT_EINVAL; + } + + iohandler_t *handler = (iohandler_t *)thread->data; + unsigned *iostate = &handler->thread_state[dt_get_id(thread)]; + + int ret = KNOT_EOK; + ref_t *ref = NULL; + + /* Create big enough memory cushion. */ + knot_mm_t mm; + mm_ctx_mempool(&mm, 16 * MM_DEFAULT_BLKSIZE); + + /* Create TCP answering context. */ + tcp_context_t tcp = { + .server = handler->server, + .thread_id = handler->thread_id[dt_get_id(thread)] + }; + knot_layer_init(&tcp.layer, &mm, process_query_layer()); + + /* Prepare structures for bound sockets. */ + conf_val_t val = conf_get(conf(), C_SRV, C_LISTEN); + fdset_init(&tcp.set, conf_val_count(&val) + CONF_XFERS); + + /* Create iovec abstraction. */ + for (unsigned i = 0; i < 2; ++i) { + tcp.iov[i].iov_len = KNOT_WIRE_MAX_PKTSIZE; + tcp.iov[i].iov_base = malloc(tcp.iov[i].iov_len); + if (tcp.iov[i].iov_base == NULL) { + ret = KNOT_ENOMEM; + goto finish; + } + } + + /* Initialize sweep interval. */ + struct timespec next_sweep = time_now(); + next_sweep.tv_sec += TCP_SWEEP_INTERVAL; + + for(;;) { + + /* Check handler state. */ + if (unlikely(*iostate & ServerReload)) { + *iostate &= ~ServerReload; + + /* Cancel client connections. */ + for (unsigned i = tcp.client_threshold; i < tcp.set.n; ++i) { + close(tcp.set.pfd[i].fd); + } + + ref_release(ref); + ref = server_set_ifaces(handler->server, &tcp.set, IO_TCP, tcp.thread_id); + if (tcp.set.n == 0) { + break; /* Terminate on zero interfaces. */ + } + + tcp.client_threshold = tcp.set.n; + } + + /* Check for cancellation. */ + if (dt_is_cancelled(thread)) { + break; + } + + /* Serve client requests. */ + tcp_wait_for_events(&tcp); + + /* Sweep inactive clients. */ + if (tcp.last_poll_time.tv_sec >= next_sweep.tv_sec) { + fdset_sweep(&tcp.set, &tcp_sweep, NULL); + next_sweep = time_now(); + next_sweep.tv_sec += TCP_SWEEP_INTERVAL; + } + } + +finish: + free(tcp.iov[0].iov_base); + free(tcp.iov[1].iov_base); + mp_delete(mm.ctx); + fdset_clear(&tcp.set); + ref_release(ref); + + return ret; +} diff --git a/src/knot/server/tcp-handler.h b/src/knot/server/tcp-handler.h new file mode 100644 index 0000000..9d048d7 --- /dev/null +++ b/src/knot/server/tcp-handler.h @@ -0,0 +1,42 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \brief TCP sockets threading model. + * + * The master socket distributes incoming connections among + * the worker threads ("buckets"). Each threads processes it's own + * set of sockets, and eliminates mutual exclusion problem by doing so. + */ + +#pragma once + +#include "knot/server/dthreads.h" + +#define TCP_BACKLOG_SIZE 10 /*!< TCP listen backlog size. */ + +/*! + * \brief TCP handler thread runnable. + * + * Listens to both bound TCP sockets for client connections and + * serves TCP clients. This runnable is designed to be used as coherent + * and implements cancellation point. + * + * \param thread Associated thread from DThreads unit. + * + * \retval KNOT_EOK on success. + * \retval KNOT_EINVAL invalid parameters. + */ +int tcp_master(dthread_t *thread); diff --git a/src/knot/server/udp-handler.c b/src/knot/server/udp-handler.c new file mode 100644 index 0000000..5076094 --- /dev/null +++ b/src/knot/server/udp-handler.c @@ -0,0 +1,495 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#define __APPLE_USE_RFC_3542 + +#include <dlfcn.h> +#include <unistd.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> +#include <assert.h> +#include <sys/param.h> +#include <urcu.h> +#ifdef HAVE_SYS_UIO_H // struct iovec (OpenBSD) +#include <sys/uio.h> +#endif /* HAVE_SYS_UIO_H */ + +#include "contrib/macros.h" +#include "contrib/mempattern.h" +#include "contrib/sockaddr.h" +#include "contrib/ucw/mempool.h" +#include "knot/nameserver/process_query.h" +#include "knot/query/layer.h" +#include "knot/server/server.h" +#include "knot/server/udp-handler.h" + +/* Buffer identifiers. */ +enum { + RX = 0, + TX = 1, + NBUFS = 2 +}; + +/*! \brief UDP context data. */ +typedef struct { + knot_layer_t layer; /*!< Query processing layer. */ + server_t *server; /*!< Name server structure. */ + unsigned thread_id; /*!< Thread identifier. */ +} udp_context_t; + +static bool udp_state_active(int state) +{ + return (state == KNOT_STATE_PRODUCE || state == KNOT_STATE_FAIL); +} + +static void udp_handle(udp_context_t *udp, int fd, struct sockaddr_storage *ss, + struct iovec *rx, struct iovec *tx) +{ + /* Create query processing parameter. */ + knotd_qdata_params_t params = { + .remote = ss, + .flags = KNOTD_QUERY_FLAG_NO_AXFR | KNOTD_QUERY_FLAG_NO_IXFR | /* No transfers. */ + KNOTD_QUERY_FLAG_LIMIT_SIZE | /* Enforce UDP packet size limit. */ + KNOTD_QUERY_FLAG_LIMIT_ANY, /* Limit ANY over UDP (depends on zone as well). */ + .socket = fd, + .server = udp->server, + .thread_id = udp->thread_id + }; + + /* Start query processing. */ + knot_layer_begin(&udp->layer, ¶ms); + + /* Create packets. */ + knot_pkt_t *query = knot_pkt_new(rx->iov_base, rx->iov_len, udp->layer.mm); + knot_pkt_t *ans = knot_pkt_new(tx->iov_base, tx->iov_len, udp->layer.mm); + + /* Input packet. */ + (void) knot_pkt_parse(query, 0); + knot_layer_consume(&udp->layer, query); + + /* Process answer. */ + while (udp_state_active(udp->layer.state)) { + knot_layer_produce(&udp->layer, ans); + } + + /* Send response only if finished successfully. */ + if (udp->layer.state == KNOT_STATE_DONE) { + tx->iov_len = ans->size; + } else { + tx->iov_len = 0; + } + + /* Reset after processing. */ + knot_layer_finish(&udp->layer); + + /* Flush per-query memory (including query and answer packets). */ + mp_flush(udp->layer.mm->ctx); +} + +/*! \brief Pointer to selected UDP master implementation. */ +static void* (*_udp_init)(void) = 0; +static void (*_udp_deinit)(void *) = 0; +static int (*_udp_recv)(int, void *) = 0; +static int (*_udp_handle)(udp_context_t *, void *) = 0; +static int (*_udp_send)(void *) = 0; + +/*! \brief Control message to fit IP_PKTINFO or IPv6_RECVPKTINFO. */ +typedef union { + struct cmsghdr cmsg; + uint8_t buf[CMSG_SPACE(sizeof(struct in6_pktinfo))]; +} cmsg_pktinfo_t; + +static void udp_pktinfo_handle(const struct msghdr *rx, struct msghdr *tx) +{ + tx->msg_controllen = rx->msg_controllen; + if (tx->msg_controllen > 0) { + tx->msg_control = rx->msg_control; + } else { + // BSD has problem with zero length and not-null pointer + tx->msg_control = NULL; + } + +#if defined(__linux__) || defined(__APPLE__) + struct cmsghdr *cmsg = CMSG_FIRSTHDR(tx); + if (cmsg == NULL) { + return; + } + + /* Unset the ifindex to not bypass the routing tables. */ + if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) { + struct in_pktinfo *info = (struct in_pktinfo *)CMSG_DATA(cmsg); + info->ipi_spec_dst = info->ipi_addr; + info->ipi_ifindex = 0; + } else if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) { + struct in6_pktinfo *info = (struct in6_pktinfo *)CMSG_DATA(cmsg); + info->ipi6_ifindex = 0; + } +#endif +} + +/* UDP recvfrom() request struct. */ +struct udp_recvfrom { + int fd; + struct sockaddr_storage addr; + struct msghdr msg[NBUFS]; + struct iovec iov[NBUFS]; + uint8_t buf[NBUFS][KNOT_WIRE_MAX_PKTSIZE]; + cmsg_pktinfo_t pktinfo; +}; + +static void *udp_recvfrom_init(void) +{ + struct udp_recvfrom *rq = malloc(sizeof(struct udp_recvfrom)); + if (rq == NULL) { + return NULL; + } + memset(rq, 0, sizeof(struct udp_recvfrom)); + + for (unsigned i = 0; i < NBUFS; ++i) { + rq->iov[i].iov_base = rq->buf + i; + rq->iov[i].iov_len = KNOT_WIRE_MAX_PKTSIZE; + rq->msg[i].msg_name = &rq->addr; + rq->msg[i].msg_namelen = sizeof(rq->addr); + rq->msg[i].msg_iov = &rq->iov[i]; + rq->msg[i].msg_iovlen = 1; + rq->msg[i].msg_control = &rq->pktinfo.cmsg; + rq->msg[i].msg_controllen = sizeof(rq->pktinfo); + } + return rq; +} + +static void udp_recvfrom_deinit(void *d) +{ + struct udp_recvfrom *rq = (struct udp_recvfrom *)d; + free(rq); +} + +static int udp_recvfrom_recv(int fd, void *d) +{ + /* Reset max lengths. */ + struct udp_recvfrom *rq = (struct udp_recvfrom *)d; + rq->iov[RX].iov_len = KNOT_WIRE_MAX_PKTSIZE; + rq->msg[RX].msg_namelen = sizeof(struct sockaddr_storage); + rq->msg[RX].msg_controllen = sizeof(rq->pktinfo); + + int ret = recvmsg(fd, &rq->msg[RX], MSG_DONTWAIT); + if (ret > 0) { + rq->fd = fd; + rq->iov[RX].iov_len = ret; + return 1; + } + + return 0; +} + +static int udp_recvfrom_handle(udp_context_t *ctx, void *d) +{ + struct udp_recvfrom *rq = (struct udp_recvfrom *)d; + + /* Prepare TX address. */ + rq->msg[TX].msg_namelen = rq->msg[RX].msg_namelen; + rq->iov[TX].iov_len = KNOT_WIRE_MAX_PKTSIZE; + + udp_pktinfo_handle(&rq->msg[RX], &rq->msg[TX]); + + /* Process received pkt. */ + udp_handle(ctx, rq->fd, &rq->addr, &rq->iov[RX], &rq->iov[TX]); + + return KNOT_EOK; +} + +static int udp_recvfrom_send(void *d) +{ + struct udp_recvfrom *rq = (struct udp_recvfrom *)d; + int rc = 0; + if (rq->iov[TX].iov_len > 0) { + rc = sendmsg(rq->fd, &rq->msg[TX], 0); + } + + /* Return number of packets sent. */ + if (rc > 1) { + return 1; + } + + return 0; +} + +#ifdef ENABLE_RECVMMSG + +/* UDP recvmmsg() request struct. */ +struct udp_recvmmsg { + int fd; + struct sockaddr_storage addrs[RECVMMSG_BATCHLEN]; + char *iobuf[NBUFS]; + struct iovec *iov[NBUFS]; + struct mmsghdr *msgs[NBUFS]; + unsigned rcvd; + knot_mm_t mm; + cmsg_pktinfo_t pktinfo[RECVMMSG_BATCHLEN]; +}; + +static void *udp_recvmmsg_init(void) +{ + knot_mm_t mm; + mm_ctx_mempool(&mm, sizeof(struct udp_recvmmsg)); + + struct udp_recvmmsg *rq = mm_alloc(&mm, sizeof(struct udp_recvmmsg)); + memset(rq, 0, sizeof(*rq)); + memcpy(&rq->mm, &mm, sizeof(knot_mm_t)); + + /* Initialize buffers. */ + for (unsigned i = 0; i < NBUFS; ++i) { + rq->iobuf[i] = mm_alloc(&mm, KNOT_WIRE_MAX_PKTSIZE * RECVMMSG_BATCHLEN); + rq->iov[i] = mm_alloc(&mm, sizeof(struct iovec) * RECVMMSG_BATCHLEN); + rq->msgs[i] = mm_alloc(&mm, sizeof(struct mmsghdr) * RECVMMSG_BATCHLEN); + memset(rq->msgs[i], 0, sizeof(struct mmsghdr) * RECVMMSG_BATCHLEN); + for (unsigned k = 0; k < RECVMMSG_BATCHLEN; ++k) { + rq->iov[i][k].iov_base = rq->iobuf[i] + k * KNOT_WIRE_MAX_PKTSIZE; + rq->iov[i][k].iov_len = KNOT_WIRE_MAX_PKTSIZE; + rq->msgs[i][k].msg_hdr.msg_iov = rq->iov[i] + k; + rq->msgs[i][k].msg_hdr.msg_iovlen = 1; + rq->msgs[i][k].msg_hdr.msg_name = rq->addrs + k; + rq->msgs[i][k].msg_hdr.msg_namelen = sizeof(struct sockaddr_storage); + rq->msgs[i][k].msg_hdr.msg_control = &rq->pktinfo[k].cmsg; + rq->msgs[i][k].msg_hdr.msg_controllen = sizeof(cmsg_pktinfo_t); + } + } + + return rq; +} + +static void udp_recvmmsg_deinit(void *d) +{ + struct udp_recvmmsg *rq = (struct udp_recvmmsg *)d; + if (rq != NULL) { + mp_delete(rq->mm.ctx); + } +} + +static int udp_recvmmsg_recv(int fd, void *d) +{ + struct udp_recvmmsg *rq = (struct udp_recvmmsg *)d; + + int n = recvmmsg(fd, rq->msgs[RX], RECVMMSG_BATCHLEN, MSG_DONTWAIT, NULL); + if (n > 0) { + rq->fd = fd; + rq->rcvd = n; + } + return n; +} + +static int udp_recvmmsg_handle(udp_context_t *ctx, void *d) +{ + struct udp_recvmmsg *rq = (struct udp_recvmmsg *)d; + + /* Handle each received msg. */ + for (unsigned i = 0; i < rq->rcvd; ++i) { + struct iovec *rx = rq->msgs[RX][i].msg_hdr.msg_iov; + struct iovec *tx = rq->msgs[TX][i].msg_hdr.msg_iov; + rx->iov_len = rq->msgs[RX][i].msg_len; /* Received bytes. */ + + udp_pktinfo_handle(&rq->msgs[RX][i].msg_hdr, &rq->msgs[TX][i].msg_hdr); + + udp_handle(ctx, rq->fd, rq->addrs + i, rx, tx); + rq->msgs[TX][i].msg_len = tx->iov_len; + rq->msgs[TX][i].msg_hdr.msg_namelen = 0; + if (tx->iov_len > 0) { + /* @note sendmmsg() workaround to prevent sending the packet */ + rq->msgs[TX][i].msg_hdr.msg_namelen = rq->msgs[RX][i].msg_hdr.msg_namelen; + } + } + + return KNOT_EOK; +} + +static int udp_recvmmsg_send(void *d) +{ + struct udp_recvmmsg *rq = (struct udp_recvmmsg *)d; + int rc = sendmmsg(rq->fd, rq->msgs[TX], rq->rcvd, 0); + for (unsigned i = 0; i < rq->rcvd; ++i) { + /* Reset buffer size and address len. */ + struct iovec *rx = rq->msgs[RX][i].msg_hdr.msg_iov; + struct iovec *tx = rq->msgs[TX][i].msg_hdr.msg_iov; + rx->iov_len = KNOT_WIRE_MAX_PKTSIZE; /* Reset RX buflen */ + tx->iov_len = KNOT_WIRE_MAX_PKTSIZE; + + memset(rq->addrs + i, 0, sizeof(struct sockaddr_storage)); + rq->msgs[RX][i].msg_hdr.msg_namelen = sizeof(struct sockaddr_storage); + rq->msgs[TX][i].msg_hdr.msg_namelen = sizeof(struct sockaddr_storage); + rq->msgs[RX][i].msg_hdr.msg_controllen = sizeof(cmsg_pktinfo_t); + } + return rc; +} +#endif /* ENABLE_RECVMMSG */ + +/*! \brief Initialize UDP master routine on run-time. */ +void __attribute__ ((constructor)) udp_master_init(void) +{ + /* Initialize defaults. */ + _udp_init = udp_recvfrom_init; + _udp_deinit = udp_recvfrom_deinit; + _udp_recv = udp_recvfrom_recv; + _udp_handle = udp_recvfrom_handle; + _udp_send = udp_recvfrom_send; + +#ifdef ENABLE_RECVMMSG + _udp_init = udp_recvmmsg_init; + _udp_deinit = udp_recvmmsg_deinit; + _udp_recv = udp_recvmmsg_recv; + _udp_handle = udp_recvmmsg_handle; + _udp_send = udp_recvmmsg_send; +#endif /* ENABLE_RECVMMSG */ +} + +/*! \brief Get interface UDP descriptor for a given thread. */ +static int iface_udp_fd(const iface_t *iface, int thread_id) +{ +#ifdef ENABLE_REUSEPORT + return iface->fd_udp[thread_id % iface->fd_udp_count]; +#else + return iface->fd_udp[0]; +#endif +} + +/*! \brief Release the interface list reference and free watched descriptor set. */ +static void forget_ifaces(ifacelist_t *ifaces, struct pollfd **fds_ptr) +{ + ref_release((ref_t *)ifaces); + free(*fds_ptr); + *fds_ptr = NULL; +} + +/*! + * \brief Make a set of watched descriptors based on the interface list. + * + * \param[in] ifaces New interface list. + * \param[in] thrid Thread ID. + * \param[out] fds_ptr Allocated set of descriptors. + * + * \return Number of watched descriptors, zero on error. + */ +static nfds_t track_ifaces(const ifacelist_t *ifaces, int thrid, + struct pollfd **fds_ptr) +{ + assert(ifaces && fds_ptr); + + nfds_t nfds = list_size(&ifaces->l); + struct pollfd *fds = malloc(nfds * sizeof(*fds)); + if (!fds) { + *fds_ptr = NULL; + return 0; + } + + iface_t *iface = NULL; + int i = 0; + WALK_LIST(iface, ifaces->l) { + fds[i].fd = iface_udp_fd(iface, thrid); + fds[i].events = POLLIN; + fds[i].revents = 0; + i += 1; + } + assert(i == nfds); + + *fds_ptr = fds; + return nfds; +} + +int udp_master(dthread_t *thread) +{ + if (thread == NULL || thread->data == NULL) { + return KNOT_EINVAL; + } + + unsigned cpu = dt_online_cpus(); + if (cpu > 1) { + unsigned cpu_mask = (dt_get_id(thread) % cpu); + dt_setaffinity(thread, &cpu_mask, 1); + } + + /* Prepare structures for bound sockets. */ + unsigned thr_id = dt_get_id(thread); + iohandler_t *handler = (iohandler_t *)thread->data; + unsigned *iostate = &handler->thread_state[thr_id]; + void *rq = _udp_init(); + ifacelist_t *ref = NULL; + + /* Create big enough memory cushion. */ + knot_mm_t mm; + mm_ctx_mempool(&mm, 16 * MM_DEFAULT_BLKSIZE); + + /* Create UDP answering context. */ + udp_context_t udp = { + .server = handler->server, + .thread_id = handler->thread_id[thr_id] + }; + knot_layer_init(&udp.layer, &mm, process_query_layer()); + + /* Event source. */ + struct pollfd *fds = NULL; + nfds_t nfds = 0; + + /* Loop until all data is read. */ + for (;;) { + + /* Check handler state. */ + if (unlikely(*iostate & ServerReload)) { + *iostate &= ~ServerReload; + udp.thread_id = handler->thread_id[thr_id]; + + rcu_read_lock(); + forget_ifaces(ref, &fds); + ref = handler->server->ifaces; + nfds = track_ifaces(ref, udp.thread_id, &fds); + rcu_read_unlock(); + if (nfds == 0) { + break; + } + } + + /* Cancellation point. */ + if (dt_is_cancelled(thread)) { + break; + } + + /* Wait for events. */ + int events = poll(fds, nfds, -1); + if (events <= 0) { + if (errno == EINTR) continue; + break; + } + + /* Process the events. */ + for (nfds_t i = 0; i < nfds && events > 0; i++) { + if (fds[i].revents == 0) { + continue; + } + events -= 1; + if (_udp_recv(fds[i].fd, rq) > 0) { + _udp_handle(&udp, rq); + _udp_send(rq); + } + } + } + + _udp_deinit(rq); + forget_ifaces(ref, &fds); + mp_delete(mm.ctx); + return KNOT_EOK; +} diff --git a/src/knot/server/udp-handler.h b/src/knot/server/udp-handler.h new file mode 100644 index 0000000..5555b98 --- /dev/null +++ b/src/knot/server/udp-handler.h @@ -0,0 +1,42 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \brief UDP sockets threading model. + * + * The master socket locks one worker thread at a time + * and saves events in it's own backing store for asynchronous processing. + * The worker threads work asynchronously in thread pool. + */ + +#pragma once + +#include "knot/server/dthreads.h" + +#define RECVMMSG_BATCHLEN 10 /*!< Default recvmmsg() batch size. */ + +/*! + * \brief UDP handler thread runnable. + * + * Listen to DNS datagrams in a loop on a UDP socket and + * reply to them. This runnable is designed to be used as coherent + * and implements cancellation point. + * + * \param thread Associated thread from DThreads unit. + * + * \retval KNOT_EOK on success. + * \retval KNOT_EINVAL invalid parameters. + */ +int udp_master(dthread_t *thread); diff --git a/src/knot/updates/acl.c b/src/knot/updates/acl.c new file mode 100644 index 0000000..28f9626 --- /dev/null +++ b/src/knot/updates/acl.c @@ -0,0 +1,104 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "knot/updates/acl.h" + +bool acl_allowed(conf_t *conf, conf_val_t *acl, acl_action_t action, + const struct sockaddr_storage *addr, knot_tsig_key_t *tsig) +{ + if (acl == NULL || addr == NULL || tsig == NULL) { + return NULL; + } + + while (acl->code == KNOT_EOK) { + /* Check if the address matches the current acl address list. */ + conf_val_t val = conf_id_get(conf, C_ACL, C_ADDR, acl); + if (val.code != KNOT_ENOENT && !conf_addr_range_match(&val, addr)) { + goto next_acl; + } + + /* Check if the key matches the current acl key list. */ + conf_val_t key_val = conf_id_get(conf, C_ACL, C_KEY, acl); + while (key_val.code == KNOT_EOK) { + /* No key provided, but required. */ + if (tsig->name == NULL) { + conf_val_next(&key_val); + continue; + } + + /* Compare key names (both in lower-case). */ + const knot_dname_t *key_name = conf_dname(&key_val); + if (!knot_dname_is_equal(key_name, tsig->name)) { + conf_val_next(&key_val); + continue; + } + + /* Compare key algorithms. */ + conf_val_t alg_val = conf_id_get(conf, C_KEY, C_ALG, + &key_val); + if (conf_opt(&alg_val) != tsig->algorithm) { + conf_val_next(&key_val); + continue; + } + + break; + } + /* Check for key match or empty list without key provided. */ + if (key_val.code != KNOT_EOK && + !(key_val.code == KNOT_ENOENT && tsig->name == NULL)) { + goto next_acl; + } + + /* Check if the action is allowed. */ + if (action != ACL_ACTION_NONE) { + val = conf_id_get(conf, C_ACL, C_ACTION, acl); + while (val.code == KNOT_EOK) { + if (conf_opt(&val) != action) { + conf_val_next(&val); + continue; + } + + break; + } + switch (val.code) { + case KNOT_EOK: /* Check for action match. */ + break; + case KNOT_ENOENT: /* Empty action list allowed with deny only. */ + return false; + default: /* No match. */ + goto next_acl; + } + } + + /* Check if denied. */ + val = conf_id_get(conf, C_ACL, C_DENY, acl); + if (conf_bool(&val)) { + return false; + } + + /* Fill the output with tsig secret if provided. */ + if (tsig->name != NULL) { + val = conf_id_get(conf, C_KEY, C_SECRET, &key_val); + tsig->secret.data = (uint8_t *)conf_bin(&val, &tsig->secret.size); + } + + return true; +next_acl: + conf_val_next(acl); + } + + return false; +} diff --git a/src/knot/updates/acl.h b/src/knot/updates/acl.h new file mode 100644 index 0000000..e15df39 --- /dev/null +++ b/src/knot/updates/acl.h @@ -0,0 +1,57 @@ +/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \file + * + * Access control list. + * + * \addtogroup server + * @{ + */ + +#pragma once + +#include <stdbool.h> +#include <sys/socket.h> + +#include "libknot/tsig.h" +#include "knot/conf/conf.h" + +/*! \brief ACL actions. */ +typedef enum { + ACL_ACTION_NONE = 0, + ACL_ACTION_NOTIFY = 1, + ACL_ACTION_TRANSFER = 2, + ACL_ACTION_UPDATE = 3 +} acl_action_t; + +/*! + * \brief Checks if the address and/or tsig key matches given ACL list. + * + * If a proper ACL rule is found and tsig.name is not empty, tsig.secret is filled. + * + * \param conf Configuration. + * \param acl Pointer to ACL config multivalued identifier. + * \param action ACL action. + * \param addr IP address. + * \param tsig TSIG parameters. + * + * \retval True if authenticated. + */ +bool acl_allowed(conf_t *conf, conf_val_t *acl, acl_action_t action, + const struct sockaddr_storage *addr, knot_tsig_key_t *tsig); + +/*! @} */ diff --git a/src/knot/updates/apply.c b/src/knot/updates/apply.c new file mode 100644 index 0000000..1b4cf5b --- /dev/null +++ b/src/knot/updates/apply.c @@ -0,0 +1,485 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> + +#include "knot/common/log.h" +#include "knot/updates/apply.h" +#include "libknot/libknot.h" +#include "contrib/macros.h" +#include "contrib/mempattern.h" + +/* --------------------------- Update cleanup ------------------------------- */ + +/*! + * \brief Post update cleanup: frees data that are in the tree that will not + * be used (old tree if success, new tree if failure). + * Freed data: + * - actual data inside knot_rrs_t. (the rest is part of the node) + */ +static void rrs_list_clear(list_t *l, knot_mm_t *mm) +{ + ptrnode_t *n; + node_t *nxt; + WALK_LIST_DELSAFE(n, nxt, *l) { + mm_free(mm, (void *)n->d); + mm_free(mm, n); + }; +} + +/*! \brief Frees additional data from single node */ +static int free_additional(zone_node_t **node, void *data) +{ + UNUSED(data); + + for (uint16_t i = 0; i < (*node)->rrset_count; ++i) { + struct rr_data *data = &(*node)->rrs[i]; + additional_clear(data->additional); + data->additional = NULL; + } + + return KNOT_EOK; +} + +/* -------------------- Changeset application helpers ----------------------- */ + +/*! \brief Replaces rdataset of given type with a copy. */ +static int replace_rdataset_with_copy(zone_node_t *node, uint16_t type) +{ + // Find data to copy. + struct rr_data *data = NULL; + for (uint16_t i = 0; i < node->rrset_count; ++i) { + if (node->rrs[i].type == type) { + data = &node->rrs[i]; + break; + } + } + assert(data); + + // Create new data. + knot_rdataset_t *rrs = &data->rrs; + void *copy = malloc(knot_rdataset_size(rrs)); + if (copy == NULL) { + return KNOT_ENOMEM; + } + + memcpy(copy, rrs->rdata, knot_rdataset_size(rrs)); + + // Store new data into node RRS. + rrs->rdata = copy; + + return KNOT_EOK; +} + +/*! \brief Frees RR dataset. For use when a copy was made. */ +static void clear_new_rrs(zone_node_t *node, uint16_t type) +{ + knot_rdataset_t *new_rrs = node_rdataset(node, type); + if (new_rrs) { + knot_rdataset_clear(new_rrs, NULL); + } +} + +/*! \brief Stores RR data for update cleanup. */ +static int add_old_data(apply_ctx_t *ctx, knot_rdata_t *old_data) +{ + if (ptrlist_add(&ctx->old_data, old_data, NULL) == NULL) { + return KNOT_ENOMEM; + } + + return KNOT_EOK; +} + +/*! \brief Stores RR data for update rollback. */ +static int add_new_data(apply_ctx_t *ctx, knot_rdata_t *new_data) +{ + if (ptrlist_add(&ctx->new_data, new_data, NULL) == NULL) { + return KNOT_ENOMEM; + } + + return KNOT_EOK; +} + +/*! \brief Returns true if given RR is present in node and can be removed. */ +static bool can_remove(const zone_node_t *node, const knot_rrset_t *rrset) +{ + if (node == NULL) { + // Node does not exist, cannot remove anything. + return false; + } + const knot_rdataset_t *node_rrs = node_rdataset(node, rrset->type); + if (node_rrs == NULL) { + // Node does not have this type at all. + return false; + } + + knot_rdata_t *rr_cmp = rrset->rrs.rdata; + for (uint16_t i = 0; i < rrset->rrs.count; ++i) { + if (knot_rdataset_member(node_rrs, rr_cmp)) { + // At least one RR matches. + return true; + } + rr_cmp = knot_rdataset_next(rr_cmp); + } + + // Node does have the type, but no RRs match. + return false; +} + +/*! \brief Removes all RRs from changeset from zone contents. */ +static int apply_remove(apply_ctx_t *ctx, const changeset_t *chset) +{ + changeset_iter_t itt; + changeset_iter_rem(&itt, chset); + + knot_rrset_t rr = changeset_iter_next(&itt); + while (!knot_rrset_empty(&rr)) { + int ret = apply_remove_rr(ctx, &rr); + if (ret != KNOT_EOK) { + changeset_iter_clear(&itt); + return ret; + } + + rr = changeset_iter_next(&itt); + } + changeset_iter_clear(&itt); + + return KNOT_EOK; +} + +/*! \brief Adds all RRs from changeset into zone contents. */ +static int apply_add(apply_ctx_t *ctx, const changeset_t *chset) +{ + changeset_iter_t itt; + changeset_iter_add(&itt, chset); + + knot_rrset_t rr = changeset_iter_next(&itt); + while(!knot_rrset_empty(&rr)) { + int ret = apply_add_rr(ctx, &rr); + if (ret != KNOT_EOK) { + changeset_iter_clear(&itt); + return ret; + } + rr = changeset_iter_next(&itt); + } + changeset_iter_clear(&itt); + + return KNOT_EOK; +} + +/*! \brief Apply single change to zone contents structure. */ +static int apply_single(apply_ctx_t *ctx, const changeset_t *chset) +{ + /* + * Applies one changeset to the zone. Checks if the changeset may be + * applied (i.e. the origin SOA (soa_from) has the same serial as + * SOA in the zone apex. + */ + + zone_contents_t *contents = ctx->contents; + + // check if serial matches + const knot_rdataset_t *soa = node_rdataset(contents->apex, KNOT_RRTYPE_SOA); + + // either we are in the mode of ignoring SOA (both NULL), or we shall be strict and apply SOA later + bool ignore_soa = (chset->soa_from == NULL && chset->soa_to == NULL); + bool soa_mismatch = (chset->soa_from == NULL || chset->soa_to == NULL || soa == NULL || + knot_soa_serial(soa->rdata) != knot_soa_serial(chset->soa_from->rrs.rdata)); + + if (soa == NULL || (!ignore_soa && soa_mismatch)) { + return KNOT_EINVAL; + } + + int ret = apply_remove(ctx, chset); + if (ret != KNOT_EOK) { + return ret; + } + + ret = apply_add(ctx, chset); + if (ret != KNOT_EOK) { + return ret; + } + + return (ignore_soa ? KNOT_EOK : apply_replace_soa(ctx, chset)); +} + +/* ------------------------------- API -------------------------------------- */ + +void apply_init_ctx(apply_ctx_t *ctx, zone_contents_t *contents, uint32_t flags) +{ + assert(ctx); + + ctx->contents = contents; + + init_list(&ctx->old_data); + init_list(&ctx->new_data); + + ctx->flags = flags; +} + +int apply_prepare_zone_copy(zone_contents_t *old_contents, + zone_contents_t **new_contents) +{ + if (old_contents == NULL || new_contents == NULL) { + return KNOT_EINVAL; + } + + /* + * Create a shallow copy of the zone, so that the structures may be + * updated. + * + * This will create new zone contents structures (normal nodes' tree, + * NSEC3 tree), and copy all nodes. + * The data in the nodes (RRSets) remain the same though. + */ + zone_contents_t *contents_copy = NULL; + int ret = zone_contents_shallow_copy(old_contents, &contents_copy); + if (ret != KNOT_EOK) { + return ret; + } + + *new_contents = contents_copy; + + return KNOT_EOK; +} + +int apply_add_rr(apply_ctx_t *ctx, const knot_rrset_t *rr) +{ + zone_contents_t *contents = ctx->contents; + + // Get or create node with this owner + zone_node_t *node = zone_contents_get_node_for_rr(contents, rr); + if (node == NULL) { + return KNOT_ENOMEM; + } + + knot_rrset_t changed_rrset = node_rrset(node, rr->type); + if (!knot_rrset_empty(&changed_rrset)) { + // Modifying existing RRSet. + knot_rdata_t *old_data = changed_rrset.rrs.rdata; + int ret = replace_rdataset_with_copy(node, rr->type); + if (ret != KNOT_EOK) { + return ret; + } + + // Store old RRS for cleanup. + ret = add_old_data(ctx, old_data); + if (ret != KNOT_EOK) { + clear_new_rrs(node, rr->type); + return ret; + } + } + + // Insert new RR to RRSet, data will be copied. + int ret = node_add_rrset(node, rr, NULL); + if (ret == KNOT_EOK || ret == KNOT_ETTL) { + // RR added, store for possible rollback. + knot_rdataset_t *rrs = node_rdataset(node, rr->type); + int data_ret = add_new_data(ctx, rrs->rdata); + if (data_ret != KNOT_EOK) { + knot_rdataset_clear(rrs, NULL); + return data_ret; + } + + if (ret == KNOT_ETTL) { + char buff[KNOT_DNAME_TXT_MAXLEN + 1]; + char *owner = knot_dname_to_str(buff, rr->owner, sizeof(buff)); + if (owner == NULL) { + owner = ""; + } + char type[16] = { '\0' }; + knot_rrtype_to_string(rr->type, type, sizeof(type)); + log_zone_notice(contents->apex->owner, + "TTL mismatch, owner %s, type %s, " + "TTL set to %u", owner, type, rr->ttl); + return KNOT_EOK; + } + } + + return ret; +} + +int apply_remove_rr(apply_ctx_t *ctx, const knot_rrset_t *rr) +{ + zone_contents_t *contents = ctx->contents; + + // Find node for this owner + zone_node_t *node = zone_contents_find_node_for_rr(contents, rr); + if (!can_remove(node, rr)) { + // Cannot be removed, either no node or nonexistent RR + if (ctx->flags & APPLY_STRICT) { + // Don't ignore missing RR if strict. Required for IXFR. + return KNOT_ENORECORD; + } + return KNOT_EOK; + } + + zone_tree_t *tree = knot_rrset_is_nsec3rel(rr) ? + contents->nsec3_nodes : contents->nodes; + + knot_rrset_t removed_rrset = node_rrset(node, rr->type); + knot_rdata_t *old_data = removed_rrset.rrs.rdata; + int ret = replace_rdataset_with_copy(node, rr->type); + if (ret != KNOT_EOK) { + return ret; + } + + // Store old data for cleanup. + ret = add_old_data(ctx, old_data); + if (ret != KNOT_EOK) { + clear_new_rrs(node, rr->type); + return ret; + } + + knot_rdataset_t *changed_rrs = node_rdataset(node, rr->type); + // Subtract changeset RRS from node RRS. + ret = knot_rdataset_subtract(changed_rrs, &rr->rrs, NULL); + if (ret != KNOT_EOK) { + clear_new_rrs(node, rr->type); + return ret; + } + + if (changed_rrs->count > 0) { + // Subtraction left some data in RRSet, store it for rollback. + ret = add_new_data(ctx, changed_rrs->rdata); + if (ret != KNOT_EOK) { + knot_rdataset_clear(changed_rrs, NULL); + return ret; + } + } else { + // RRSet is empty now, remove it from node, all data freed, except additionals. + node_remove_rdataset(node, rr->type); + // If node is empty now, delete it from zone tree. + if (node->rrset_count == 0 && node != contents->apex) { + zone_tree_delete_empty(tree, node); + } + } + + return KNOT_EOK; +} + +int apply_replace_soa(apply_ctx_t *ctx, const changeset_t *chset) +{ + zone_contents_t *contents = ctx->contents; + + if (!knot_dname_is_equal(chset->soa_to->owner, contents->apex->owner)) { + return KNOT_EDENIED; + } + + assert(chset->soa_from && chset->soa_to); + int ret = apply_remove_rr(ctx, chset->soa_from); + if (ret != KNOT_EOK) { + return ret; + } + + // Check for SOA with proper serial but different rdata. + if (node_rrtype_exists(contents->apex, KNOT_RRTYPE_SOA)) { + return KNOT_ESOAINVAL; + } + + return apply_add_rr(ctx, chset->soa_to); +} + +int apply_prepare_to_sign(apply_ctx_t *ctx) +{ + return zone_contents_adjust_pointers(ctx->contents); +} + +int apply_changesets_directly(apply_ctx_t *ctx, list_t *chsets) +{ + if (ctx == NULL || ctx->contents == NULL || chsets == NULL) { + return KNOT_EINVAL; + } + + changeset_t *set = NULL; + WALK_LIST(set, *chsets) { + int ret = apply_single(ctx, set); + if (ret != KNOT_EOK) { + return ret; + } + } + + return zone_contents_adjust_full(ctx->contents); +} + +int apply_changeset_directly(apply_ctx_t *ctx, const changeset_t *ch) +{ + if (ctx == NULL || ctx->contents == NULL || ch == NULL) { + return KNOT_EINVAL; + } + + int ret = apply_single(ctx, ch); + if (ret != KNOT_EOK) { + update_rollback(ctx); + return ret; + } + + ret = zone_contents_adjust_full(ctx->contents); + if (ret != KNOT_EOK) { + update_rollback(ctx); + return ret; + } + + return KNOT_EOK; +} + +int apply_finalize(apply_ctx_t *ctx) +{ + return zone_contents_adjust_full(ctx->contents); +} + +void update_cleanup(apply_ctx_t *ctx) +{ + if (ctx == NULL) { + return; + } + + // Delete old RR data + rrs_list_clear(&ctx->old_data, NULL); + init_list(&ctx->old_data); + // Keep new RR data + ptrlist_free(&ctx->new_data, NULL); + init_list(&ctx->new_data); +} + +void update_rollback(apply_ctx_t *ctx) +{ + if (ctx == NULL) { + return; + } + + // Delete new RR data + rrs_list_clear(&ctx->new_data, NULL); + init_list(&ctx->new_data); + // Keep old RR data + ptrlist_free(&ctx->old_data, NULL); + init_list(&ctx->old_data); +} + +void update_free_zone(zone_contents_t *contents) +{ + if (contents == NULL) { + return; + } + + (void)zone_tree_apply(contents->nodes, free_additional, NULL); + zone_tree_deep_free(&contents->nodes); + zone_tree_deep_free(&contents->nsec3_nodes); + + dnssec_nsec3_params_free(&contents->nsec3_params); + + free(contents); +} diff --git a/src/knot/updates/apply.h b/src/knot/updates/apply.h new file mode 100644 index 0000000..7908ca4 --- /dev/null +++ b/src/knot/updates/apply.h @@ -0,0 +1,150 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "knot/zone/contents.h" +#include "knot/updates/changesets.h" +#include "contrib/ucw/lists.h" + +enum { + APPLY_STRICT = 1 << 0, /* Apply strictly, don't ignore removing non-existent RRs. */ +}; + +struct apply_ctx { + zone_contents_t *contents; + list_t old_data; /*!< Old data, to be freed after successful update. */ + list_t new_data; /*!< New data, to be freed after failed update. */ + uint32_t flags; +}; + +typedef struct apply_ctx apply_ctx_t; + +/*! + * \brief Initialize a new context structure. + * + * \param ctx Context to be initialized. + * \param contents Zone contents to apply changes onto. + * \param flags Flags to control the application process. + */ +void apply_init_ctx(apply_ctx_t *ctx, zone_contents_t *contents, uint32_t flags); + +/*! + * \brief Creates a shallow zone contents copy. + * + * \param old_contents Source. + * \param new_contents Target. + * + * \return KNOT_E* + */ +int apply_prepare_zone_copy(zone_contents_t *old_contents, + zone_contents_t **new_contents); + +/*! + * \brief Adds a single RR into zone contents. + * + * \param ctx Apply context. + * \param rr RRSet to add. + * + * \return KNOT_E* + */ +int apply_add_rr(apply_ctx_t *ctx, const knot_rrset_t *rr); + +/*! + * \brief Removes single RR from zone contents. + * + * \param ctx Apply context. + * \param rr RRSet to remove. + * + * \return KNOT_E* + */ +int apply_remove_rr(apply_ctx_t *ctx, const knot_rrset_t *rr); + +/*! + * \brief Adds a single RR into zone contents. + * + * \param ctx Apply context. + * \param ch Changeset to be applied to the zone. + * + * \return KNOT_E* + */ +int apply_replace_soa(apply_ctx_t *ctx, const changeset_t *ch); + +/*! + * \brief Prepares the new zone contents for signing. + * + * Adjusted pointers are required for DNSSEC. + * + * \param ctx Apply context. + * + * \return KNOT_E* + */ +int apply_prepare_to_sign(apply_ctx_t *ctx); + +/*! + * \brief Applies changesets directly to the zone, without copying it. + * + * \warning Modified zone is in inconsistent state after error and should be freed. + * + * \param ctx Apply context. + * \param chsets List of changesets to be applied to the zone. + * + * \return KNOT_E* + */ +int apply_changesets_directly(apply_ctx_t *ctx, list_t *chsets); + +/*! + * \brief Applies changeset directly to the zone, without copying it. + * + * \param ctx Apply context. + * \param ch Changeset to be applied to the zone. + * + * \return KNOT_E* + */ +int apply_changeset_directly(apply_ctx_t *ctx, const changeset_t *ch); + +/*! + * \brief Finalizes the zone contents for publishing. + * + * Fully adjusts the zone. + * + * \param ctx Apply context. + * + * \return KNOT_E* + */ +int apply_finalize(apply_ctx_t *ctx); + +/*! + * \brief Cleanups successful zone update. + * + * \param ctx Context used to create the update. + */ +void update_cleanup(apply_ctx_t *ctx); + +/*! + * \brief Rollbacks failed zone update. + * + * \param ctx Context used to create the update. + */ +void update_rollback(apply_ctx_t *ctx); + +/*! + * \brief Shallow frees zone contents - either shallow copy after failed update + * or original zone contents after successful update. + * + * \param contents Contents to free. + */ +void update_free_zone(zone_contents_t *contents); diff --git a/src/knot/updates/changesets.c b/src/knot/updates/changesets.c new file mode 100644 index 0000000..3c0c9dc --- /dev/null +++ b/src/knot/updates/changesets.c @@ -0,0 +1,822 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> +#include <stdlib.h> +#include <stdarg.h> + +#include "knot/updates/changesets.h" +#include "knot/updates/apply.h" +#include "libknot/libknot.h" +#include "knot/zone/zone-dump.h" +#include "contrib/macros.h" +#include "contrib/mempattern.h" + +static int handle_soa(knot_rrset_t **soa, const knot_rrset_t *rrset) +{ + assert(soa); + assert(rrset); + + if (*soa != NULL) { + knot_rrset_free(*soa, NULL); + } + + *soa = knot_rrset_copy(rrset, NULL); + if (*soa == NULL) { + return KNOT_ENOMEM; + } + + return KNOT_EOK; +} + +/*! \brief Adds RRSet to given zone. */ +static int add_rr_to_contents(zone_contents_t *z, const knot_rrset_t *rrset) +{ + zone_node_t *n = NULL; + int ret = zone_contents_add_rr(z, rrset, &n); + UNUSED(n); + + // We don't care of TTLs. + return ret == KNOT_ETTL ? KNOT_EOK : ret; +} + +/*! \brief Cleans up trie iterations. */ +static void cleanup_iter_list(list_t *l) +{ + ptrnode_t *n, *nxt; + WALK_LIST_DELSAFE(n, nxt, *l) { + trie_it_t *it = (trie_it_t *)n->d; + trie_it_free(it); + rem_node(&n->n); + free(n); + } + init_list(l); +} + +/*! \brief Inits changeset iterator with given tries. */ +static int changeset_iter_init(changeset_iter_t *ch_it, size_t tries, ...) +{ + memset(ch_it, 0, sizeof(*ch_it)); + init_list(&ch_it->iters); + + va_list args; + va_start(args, tries); + + for (size_t i = 0; i < tries; ++i) { + trie_t *t = va_arg(args, trie_t *); + if (t == NULL) { + continue; + } + + trie_it_t *it = trie_it_begin(t); + if (it == NULL) { + cleanup_iter_list(&ch_it->iters); + va_end(args); + return KNOT_ENOMEM; + } + + if (ptrlist_add(&ch_it->iters, it, NULL) == NULL) { + cleanup_iter_list(&ch_it->iters); + va_end(args); + return KNOT_ENOMEM; + } + } + + va_end(args); + + return KNOT_EOK; +} + +/*! \brief Gets next node from trie iterators. */ +static void iter_next_node(changeset_iter_t *ch_it, trie_it_t *t_it) +{ + assert(!trie_it_finished(t_it)); + // Get next node, but not for the very first call. + if (ch_it->node) { + trie_it_next(t_it); + } + if (trie_it_finished(t_it)) { + ch_it->node = NULL; + return; + } + + ch_it->node = (zone_node_t *)*trie_it_val(t_it); + assert(ch_it->node); + while (ch_it->node && ch_it->node->rrset_count == 0) { + // Skip empty non-terminals. + trie_it_next(t_it); + if (trie_it_finished(t_it)) { + ch_it->node = NULL; + } else { + ch_it->node = (zone_node_t *)*trie_it_val(t_it); + assert(ch_it->node); + } + } + + ch_it->node_pos = 0; +} + +/*! \brief Gets next RRSet from trie iterators. */ +static knot_rrset_t get_next_rr(changeset_iter_t *ch_it, trie_it_t *t_it) +{ + if (ch_it->node == NULL || ch_it->node_pos == ch_it->node->rrset_count) { + iter_next_node(ch_it, t_it); + if (ch_it->node == NULL) { + assert(trie_it_finished(t_it)); + knot_rrset_t rr; + knot_rrset_init_empty(&rr); + return rr; + } + } + + return node_rrset_at(ch_it->node, ch_it->node_pos++); +} + +// removes from counterpart what is in rr. +// fixed_rr is an output parameter, holding a copy of rr without what has been removed from counterpart +static void check_redundancy(zone_contents_t *counterpart, const knot_rrset_t *rr, knot_rrset_t **fixed_rr) +{ + if (fixed_rr != NULL) { + *fixed_rr = knot_rrset_copy(rr, NULL); + } + + zone_node_t *node = zone_contents_find_node_for_rr(counterpart, rr); + if (node == NULL) { + return; + } + + if (!node_rrtype_exists(node, rr->type)) { + return; + } + + // Subtract the data from node's RRSet. + knot_rdataset_t *rrs = node_rdataset(node, rr->type); + uint32_t rrs_ttl = node_rrset(node, rr->type).ttl; + + if (fixed_rr != NULL && *fixed_rr != NULL && (*fixed_rr)->ttl == rrs_ttl) { + int ret = knot_rdataset_subtract(&(*fixed_rr)->rrs, rrs, NULL); + if (ret != KNOT_EOK) { + return; + } + } + + if (rr->ttl == rrs_ttl) { + int ret = knot_rdataset_subtract(rrs, &rr->rrs, NULL); + if (ret != KNOT_EOK) { + return; + } + } + + if (knot_rdataset_size(rrs) == 0) { + // Remove empty type. + node_remove_rdataset(node, rr->type); + + if (node->rrset_count == 0 && node != counterpart->apex) { + // Remove empty node. + zone_tree_t *t = knot_rrset_is_nsec3rel(rr) ? + counterpart->nsec3_nodes : counterpart->nodes; + zone_tree_delete_empty(t, node); + } + } + + return; +} + +int changeset_init(changeset_t *ch, const knot_dname_t *apex) +{ + memset(ch, 0, sizeof(changeset_t)); + + // Init local changes + ch->add = zone_contents_new(apex); + if (ch->add == NULL) { + return KNOT_ENOMEM; + } + ch->remove = zone_contents_new(apex); + if (ch->remove == NULL) { + zone_contents_free(ch->add); + return KNOT_ENOMEM; + } + + return KNOT_EOK; +} + +changeset_t *changeset_new(const knot_dname_t *apex) +{ + changeset_t *ret = malloc(sizeof(changeset_t)); + if (ret == NULL) { + return NULL; + } + + if (changeset_init(ret, apex) == KNOT_EOK) { + return ret; + } else { + free(ret); + return NULL; + } +} + +bool changeset_empty(const changeset_t *ch) +{ + if (ch == NULL || ch->add == NULL || ch->remove == NULL) { + return true; + } + + if (ch->soa_to) { + return false; + } + + changeset_iter_t itt; + changeset_iter_all(&itt, ch); + + knot_rrset_t rr = changeset_iter_next(&itt); + changeset_iter_clear(&itt); + + return knot_rrset_empty(&rr); +} + +size_t changeset_size(const changeset_t *ch) +{ + if (ch == NULL) { + return 0; + } + + changeset_iter_t itt; + changeset_iter_all(&itt, ch); + + size_t size = 0; + knot_rrset_t rr = changeset_iter_next(&itt); + while(!knot_rrset_empty(&rr)) { + ++size; + rr = changeset_iter_next(&itt); + } + changeset_iter_clear(&itt); + + if (!knot_rrset_empty(ch->soa_from)) { + size += 1; + } + if (!knot_rrset_empty(ch->soa_to)) { + size += 1; + } + + return size; +} + +int changeset_add_addition(changeset_t *ch, const knot_rrset_t *rrset, changeset_flag_t flags) +{ + if (!ch || !rrset) { + return KNOT_EINVAL; + } + + if (rrset->type == KNOT_RRTYPE_SOA) { + /* Do not add SOAs into actual contents. */ + return handle_soa(&ch->soa_to, rrset); + } + + knot_rrset_t *rrset_cancelout = NULL; + + /* Check if there's any removal and remove that, then add this + * addition anyway. Required to change TTLs. */ + if (flags & CHANGESET_CHECK) { + /* If we delete the rrset, we need to hold a copy to add it later */ + rrset = knot_rrset_copy(rrset, NULL); + if (rrset == NULL) { + return KNOT_ENOMEM; + } + + check_redundancy(ch->remove, rrset, + ((flags & CHANGESET_CHECK_CANCELOUT) ? &rrset_cancelout : NULL)); + } + + const knot_rrset_t *to_add = (rrset_cancelout == NULL ? rrset : rrset_cancelout); + int ret = knot_rrset_empty(to_add) ? KNOT_EOK : add_rr_to_contents(ch->add, to_add); + + if (flags & CHANGESET_CHECK) { + knot_rrset_free((knot_rrset_t *)rrset, NULL); + } + knot_rrset_free(rrset_cancelout, NULL); + + return ret; +} + +int changeset_add_removal(changeset_t *ch, const knot_rrset_t *rrset, changeset_flag_t flags) +{ + if (!ch || !rrset) { + return KNOT_EINVAL; + } + + if (rrset->type == KNOT_RRTYPE_SOA) { + /* Do not add SOAs into actual contents. */ + return handle_soa(&ch->soa_from, rrset); + } + + knot_rrset_t *rrset_cancelout = NULL; + + /* Check if there's any addition and remove that, then add this + * removal anyway. */ + if (flags & CHANGESET_CHECK) { + /* If we delete the rrset, we need to hold a copy to add it later */ + rrset = knot_rrset_copy(rrset, NULL); + if (rrset == NULL) { + return KNOT_ENOMEM; + } + + check_redundancy(ch->add, rrset, + ((flags & CHANGESET_CHECK_CANCELOUT) ? &rrset_cancelout : NULL)); + } + + const knot_rrset_t *to_remove = (rrset_cancelout == NULL ? rrset : rrset_cancelout); + int ret = knot_rrset_empty(to_remove) ? KNOT_EOK : add_rr_to_contents(ch->remove, to_remove); + + if (flags & CHANGESET_CHECK) { + knot_rrset_free((knot_rrset_t *)rrset, NULL); + } + knot_rrset_free(rrset_cancelout, NULL); + + return ret; +} + +int changeset_remove_addition(changeset_t *ch, const knot_rrset_t *rrset) +{ + if (rrset->type == KNOT_RRTYPE_SOA) { + /* Do not add SOAs into actual contents. */ + if (ch->soa_to != NULL) { + knot_rrset_free(ch->soa_to, NULL); + ch->soa_to = NULL; + } + return KNOT_EOK; + } + + zone_node_t *n = NULL; + return zone_contents_remove_rr(ch->add, rrset, &n); +} + +int changeset_remove_removal(changeset_t *ch, const knot_rrset_t *rrset) +{ + if (rrset->type == KNOT_RRTYPE_SOA) { + /* Do not add SOAs into actual contents. */ + if (ch->soa_from != NULL) { + knot_rrset_free(ch->soa_from, NULL); + ch->soa_from = NULL; + } + return KNOT_EOK; + } + + zone_node_t *n = NULL; + return zone_contents_remove_rr(ch->remove, rrset, &n); +} + +int changeset_merge(changeset_t *ch1, const changeset_t *ch2, int flags) +{ + changeset_iter_t itt; + changeset_iter_rem(&itt, ch2); + + knot_rrset_t rrset = changeset_iter_next(&itt); + while (!knot_rrset_empty(&rrset)) { + int ret = changeset_add_removal(ch1, &rrset, CHANGESET_CHECK | flags); + if (ret != KNOT_EOK) { + changeset_iter_clear(&itt); + return ret; + } + rrset = changeset_iter_next(&itt); + } + changeset_iter_clear(&itt); + + changeset_iter_add(&itt, ch2); + + rrset = changeset_iter_next(&itt); + while (!knot_rrset_empty(&rrset)) { + int ret = changeset_add_addition(ch1, &rrset, CHANGESET_CHECK | flags); + if (ret != KNOT_EOK) { + changeset_iter_clear(&itt); + return ret; + } + rrset = changeset_iter_next(&itt); + } + changeset_iter_clear(&itt); + + // Use soa_to and serial from the second changeset + // soa_to from the first changeset is redundant, delete it + if (ch2->soa_to == NULL && ch2->soa_from == NULL) { + // but not if ch2 has no soa change + return KNOT_EOK; + } + knot_rrset_t *soa_copy = knot_rrset_copy(ch2->soa_to, NULL); + if (soa_copy == NULL && ch2->soa_to) { + return KNOT_ENOMEM; + } + knot_rrset_free(ch1->soa_to, NULL); + ch1->soa_to = soa_copy; + + return KNOT_EOK; +} + +typedef struct { + const zone_contents_t *zone; + changeset_t *fixing; + knot_mm_t *mm; +} preapply_fix_ctx; + +static int preapply_fix_rrset(const knot_rrset_t *apply, bool adding, void *data) +{ + preapply_fix_ctx *ctx = (preapply_fix_ctx *)data; + const zone_node_t *znode = zone_contents_find_node(ctx->zone, apply->owner); + const knot_rdataset_t *zrdataset = node_rdataset(znode, apply->type); + if (adding && zrdataset == NULL) { + return KNOT_EOK; + } + + knot_rrset_t *fixrrset; + if (adding) { + fixrrset = knot_rrset_new(apply->owner, apply->type, apply->rclass, + apply->ttl, ctx->mm); + } else { + fixrrset = knot_rrset_copy(apply, ctx->mm); + } + if (fixrrset == NULL) { + return KNOT_ENOMEM; + } + + int ret = KNOT_EOK; + if (adding) { + ret = knot_rdataset_intersect(zrdataset, &apply->rrs, &fixrrset->rrs, ctx->mm); + } else { + uint32_t zrrset_ttl = node_rrset(znode, apply->type).ttl; + if (zrdataset != NULL && fixrrset->ttl == zrrset_ttl) { + ret = knot_rdataset_subtract(&fixrrset->rrs, zrdataset, ctx->mm); + } + } + if (ret == KNOT_EOK && !knot_rrset_empty(fixrrset)) { + if (adding) { + ret = changeset_add_removal(ctx->fixing, fixrrset, 0); + } else { + ret = changeset_add_addition(ctx->fixing, fixrrset, 0); + } + } + + knot_rrset_free(fixrrset, ctx->mm); + return ret; +} + +static int subtract_callback(const knot_rrset_t *rrset, bool addition, void *subtractfrom) +{ + changeset_t *chsf = (changeset_t *)subtractfrom; + if (addition) { + return changeset_remove_removal(chsf, rrset); + } else { + return changeset_remove_addition(chsf, rrset); + } +} + +static int subtract(changeset_t *from, const changeset_t *what) +{ + return changeset_walk(what, subtract_callback, (void *)from); +} + +int changeset_preapply_fix(const zone_contents_t *zone, changeset_t *ch) +{ + if (zone == NULL || ch == NULL) { + return KNOT_EINVAL; + } + + changeset_t fixing; + int ret = changeset_init(&fixing, zone->apex->owner); + if (ret != KNOT_EOK) { + return ret; + } + + preapply_fix_ctx ctx = { .zone = zone, .fixing = &fixing, .mm = NULL }; + ret = changeset_walk(ch, preapply_fix_rrset, (void *)&ctx); + if (ret == KNOT_EOK) { + ret = subtract(ch, &fixing); + } + changeset_clear(&fixing); + return ret; +} + +int changeset_cancelout(changeset_t *ch) +{ + if (ch == NULL) { + return KNOT_EINVAL; + } + + changeset_t fixing; + int ret = changeset_init(&fixing, ch->add->apex->owner); + if (ret != KNOT_EOK) { + return ret; + } + + preapply_fix_ctx ctx = { .zone = ch->remove, .fixing = &fixing, .mm = NULL }; + ret = changeset_walk(ch, preapply_fix_rrset, (void *)&ctx); + if (ret == KNOT_EOK) { + assert(zone_contents_is_empty(fixing.add)); + zone_contents_t *fixing_add_bck = fixing.add; + fixing.add = fixing.remove; + ret = subtract(ch, &fixing); + fixing.add = fixing_add_bck; + } + changeset_clear(&fixing); + return ret; +} + +bool changeset_differs_just_serial(const changeset_t *ch) +{ + if (ch == NULL || ch->soa_from == NULL || ch->soa_to == NULL) { + return false; + } + + knot_rrset_t *soa_to_cpy = knot_rrset_copy(ch->soa_to, NULL); + knot_soa_serial_set(soa_to_cpy->rrs.rdata, knot_soa_serial(ch->soa_from->rrs.rdata)); + + bool ret = knot_rrset_equal(ch->soa_from, soa_to_cpy, KNOT_RRSET_COMPARE_WHOLE); + knot_rrset_free(soa_to_cpy, NULL); + + changeset_iter_t itt; + changeset_iter_all(&itt, ch); + + knot_rrset_t rrset = changeset_iter_next(&itt); + while (!knot_rrset_empty(&rrset) && ret) { + if (rrset.type != KNOT_RRTYPE_RRSIG || rrset.rrs.count != 1 || + knot_rrsig_type_covered(rrset.rrs.rdata) != KNOT_RRTYPE_SOA) { + ret = false; + } + rrset = changeset_iter_next(&itt); + } + changeset_iter_clear(&itt); + + return ret; +} + +int changeset_to_contents(changeset_t *ch, zone_contents_t **out) +{ + assert(ch->soa_from == NULL); + assert(zone_contents_is_empty(ch->remove)); + assert(out != NULL); + + *out = ch->add; + int ret = add_rr_to_contents(*out, ch->soa_to); + knot_rrset_free(ch->soa_to, NULL); + if (ret != KNOT_EOK) { + zone_contents_deep_free(*out); + } + + zone_contents_deep_free(ch->remove); + free(ch->data); + free(ch); + return ret; +} + +changeset_t *changeset_from_contents(const zone_contents_t *contents) +{ + zone_contents_t *copy = NULL; + if (zone_contents_shallow_copy(contents, ©) != KNOT_EOK) { + return NULL; + } + + changeset_t *res = changeset_new(copy->apex->owner); + + knot_rrset_t soa_rr = node_rrset(copy->apex, KNOT_RRTYPE_SOA);; + res->soa_to = knot_rrset_copy(&soa_rr, NULL); + + node_remove_rdataset(copy->apex, KNOT_RRTYPE_SOA); + + zone_contents_deep_free(res->add); + res->add = copy; + return res; +} + +void changeset_from_contents_free(changeset_t *ch) +{ + assert(ch); + assert(ch->soa_from == NULL); + assert(zone_contents_is_empty(ch->remove)); + + update_free_zone(ch->add); + + zone_contents_deep_free(ch->remove); + knot_rrset_free(ch->soa_from, NULL); + knot_rrset_free(ch->soa_to, NULL); + free(ch->data); + free(ch); +} + +void changesets_clear(list_t *chgs) +{ + if (chgs) { + changeset_t *chg, *nxt; + WALK_LIST_DELSAFE(chg, nxt, *chgs) { + changeset_clear(chg); + rem_node(&chg->n); + } + init_list(chgs); + } +} + +void changesets_free(list_t *chgs) +{ + if (chgs) { + changeset_t *chg, *nxt; + WALK_LIST_DELSAFE(chg, nxt, *chgs) { + rem_node(&chg->n); + changeset_free(chg); + } + init_list(chgs); + } +} + +void changeset_clear(changeset_t *ch) +{ + if (ch == NULL) { + return; + } + + // Delete RRSets in lists, in case there are any left + zone_contents_deep_free(ch->add); + zone_contents_deep_free(ch->remove); + ch->add = NULL; + ch->remove = NULL; + + knot_rrset_free(ch->soa_from, NULL); + knot_rrset_free(ch->soa_to, NULL); + ch->soa_from = NULL; + ch->soa_to = NULL; + + // Delete binary data + free(ch->data); +} + +changeset_t *changeset_clone(const changeset_t *ch) +{ + if (ch == NULL) { + return NULL; + } + + changeset_t *res = changeset_new(ch->add->apex->owner); + if (res == NULL) { + return NULL; + } + + res->soa_from = knot_rrset_copy(ch->soa_from, NULL); + res->soa_to = knot_rrset_copy(ch->soa_to, NULL); + + int ret = KNOT_EOK; + changeset_iter_t itt; + + changeset_iter_rem(&itt, ch); + knot_rrset_t rr = changeset_iter_next(&itt); + while (!knot_rrset_empty(&rr) && ret == KNOT_EOK) { + ret = changeset_add_removal(res, &rr, 0); + rr = changeset_iter_next(&itt); + } + changeset_iter_clear(&itt); + + changeset_iter_add(&itt, ch); + rr = changeset_iter_next(&itt); + while (!knot_rrset_empty(&rr) && ret == KNOT_EOK) { + ret = changeset_add_addition(res, &rr, 0); + rr = changeset_iter_next(&itt); + } + changeset_iter_clear(&itt); + + if ((ch->soa_from != NULL && res->soa_from == NULL) || + (ch->soa_to != NULL && res->soa_to == NULL) || + ret != KNOT_EOK) { + changeset_free(res); + return NULL; + } + + return res; +} + +void changeset_free(changeset_t *ch) +{ + changeset_clear(ch); + free(ch); +} + +int changeset_iter_add(changeset_iter_t *itt, const changeset_t *ch) +{ + return changeset_iter_init(itt, 2, ch->add->nodes, ch->add->nsec3_nodes); +} + +int changeset_iter_rem(changeset_iter_t *itt, const changeset_t *ch) +{ + return changeset_iter_init(itt, 2, ch->remove->nodes, ch->remove->nsec3_nodes); +} + +int changeset_iter_all(changeset_iter_t *itt, const changeset_t *ch) +{ + return changeset_iter_init(itt, 4, ch->add->nodes, ch->add->nsec3_nodes, + ch->remove->nodes, ch->remove->nsec3_nodes); +} + +knot_rrset_t changeset_iter_next(changeset_iter_t *it) +{ + assert(it); + ptrnode_t *n = NULL; + knot_rrset_t rr; + knot_rrset_init_empty(&rr); + WALK_LIST(n, it->iters) { + trie_it_t *t_it = (trie_it_t *)n->d; + if (trie_it_finished(t_it)) { + continue; + } + + rr = get_next_rr(it, t_it); + if (!knot_rrset_empty(&rr)) { + // Got valid RRSet. + return rr; + } + } + + return rr; +} + +void changeset_iter_clear(changeset_iter_t *it) +{ + if (it) { + cleanup_iter_list(&it->iters); + it->node = NULL; + it->node_pos = 0; + } +} + +int changeset_walk(const changeset_t *changeset, changeset_walk_callback callback, void *ctx) +{ + changeset_iter_t it; + int ret = changeset_iter_rem(&it, changeset); + if (ret != KNOT_EOK) { + return ret; + } + + knot_rrset_t rrset = changeset_iter_next(&it); + while (!knot_rrset_empty(&rrset)) { + ret = callback(&rrset, false, ctx); + if (ret != KNOT_EOK) { + changeset_iter_clear(&it); + return ret; + } + rrset = changeset_iter_next(&it); + } + changeset_iter_clear(&it); + + ret = changeset_iter_add(&it, changeset); + if (ret != KNOT_EOK) { + return ret; + } + + rrset = changeset_iter_next(&it); + while (!knot_rrset_empty(&rrset)) { + ret = callback(&rrset, true, ctx); + if (ret != KNOT_EOK) { + changeset_iter_clear(&it); + return ret; + } + rrset = changeset_iter_next(&it); + } + changeset_iter_clear(&it); + + return KNOT_EOK; +} + +void changeset_print(const changeset_t *changeset, FILE *outfile, bool color) +{ + const char * RED = "\x1B[31m", * GRN = "\x1B[32m", * RESET = "\x1B[0m"; + size_t buflen = 1024; + char *buff = malloc(buflen); + + if (changeset->soa_from != NULL || !zone_contents_is_empty(changeset->remove)) { + fprintf(outfile, "%s;;Removed\n", color ? RED : ""); + } + if (changeset->soa_from != NULL && buff != NULL) { + (void)knot_rrset_txt_dump(changeset->soa_from, &buff, &buflen, &KNOT_DUMP_STYLE_DEFAULT); + fprintf(outfile, "%s", buff); + } + (void)zone_dump_text(changeset->remove, outfile, false); + + if (changeset->soa_to != NULL || !zone_contents_is_empty(changeset->add)) { + fprintf(outfile, "%s;;Added\n", color ? GRN : ""); + } + if (changeset->soa_to != NULL && buff != NULL) { + (void)knot_rrset_txt_dump(changeset->soa_to, &buff, &buflen, &KNOT_DUMP_STYLE_DEFAULT); + fprintf(outfile, "%s", buff); + } + (void)zone_dump_text(changeset->add, outfile, false); + + if (color) { + printf("%s", RESET); + } + free(buff); +} diff --git a/src/knot/updates/changesets.h b/src/knot/updates/changesets.h new file mode 100644 index 0000000..8002712 --- /dev/null +++ b/src/knot/updates/changesets.h @@ -0,0 +1,314 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <stdio.h> + +#include "libknot/rrset.h" +#include "knot/zone/contents.h" +#include "contrib/ucw/lists.h" + +/*! \brief Changeset addition/removal flags */ +typedef enum { + CHANGESET_NONE = 0, + CHANGESET_CHECK = 1 << 0, /*! Perform redundancy check on additions/removals */ + CHANGESET_CHECK_CANCELOUT = 1 << 1, /*! Do the complete cancelout on addition/removal/merge (depends on CHANGESET_CHECK */ +} changeset_flag_t; + +/*! \brief One zone change, from 'soa_from' to 'soa_to'. */ +typedef struct { + node_t n; /*!< List node. */ + knot_rrset_t *soa_from; /*!< Start SOA. */ + knot_rrset_t *soa_to; /*!< Destination SOA. */ + zone_contents_t *add; /*!< Change additions. */ + zone_contents_t *remove; /*!< Change removals. */ + size_t size; /*!< Size of serialized changeset. \todo Remove after old_journal removal! */ + uint8_t *data; /*!< Serialized changeset. */ +} changeset_t; + +/*! \brief Changeset iteration structure. */ +typedef struct { + list_t iters; /*!< List of pending zone iterators. */ + const zone_node_t *node; /*!< Current zone node. */ + uint16_t node_pos; /*!< Position in node. */ +} changeset_iter_t; + +/*! + * \brief Inits changeset structure. + * + * \param ch Changeset to init. + * \param apex Zone apex DNAME. + * + * \return KNOT_E* + */ +int changeset_init(changeset_t *ch, const knot_dname_t *apex); + +/*! + * \brief Creates new changeset structure and inits it. + * + * \param apex Zone apex DNAME. + * + * \return Changeset structure on success, NULL on errors. + */ +changeset_t *changeset_new(const knot_dname_t *apex); + +/*! + * \brief Checks whether changeset is empty, i.e. no change will happen after its application. + * + * \param ch Changeset to be checked. + * + * \retval true if changeset is empty. + * \retval false if changeset is not empty. + */ +bool changeset_empty(const changeset_t *ch); + +/*! + * \brief Get number of changes (additions and removals) in the changeset. + * + * \param ch Changeset to be checked. + * + * \return Number of changes in the changeset. + */ +size_t changeset_size(const changeset_t *ch); + +/*! + * \brief Add RRSet to 'add' part of changeset. + * + * \param ch Changeset to add RRSet into. + * \param rrset RRSet to be added. + * \param flags Changeset flags. + * + * \return KNOT_E* + */ +int changeset_add_addition(changeset_t *ch, const knot_rrset_t *rrset, changeset_flag_t flags); + +/*! + * \brief Add RRSet to 'remove' part of changeset. + * + * \param ch Changeset to add RRSet into. + * \param rrset RRSet to be added. + * \param flags Changeset flags. + * + * \return KNOT_E* + */ +int changeset_add_removal(changeset_t *ch, const knot_rrset_t *rrset, changeset_flag_t flags); + + +/*! + * \brief Remove an RRSet from the 'add' part of changeset. + * + * \param ch Changeset to add RRSet into. + * \param rrset RRSet to be added. + * + * \return KNOT_E* + */ +int changeset_remove_addition(changeset_t *ch, const knot_rrset_t *rrset); + +/*! + * \brief Remove an RRSet from the 'remove' part of changeset. + * + * \param ch Changeset to add RRSet into. + * \param rrset RRSet to be added. + * + * \return KNOT_E* + */ +int changeset_remove_removal(changeset_t *ch, const knot_rrset_t *rrset); + +/*! + * \brief Merges two changesets together. + * + * \param ch1 Merge into this changeset. + * \param ch2 Merge this changeset. + * \param flags Flags how to handle rendundancies. + * + * \return KNOT_E* + */ +int changeset_merge(changeset_t *ch1, const changeset_t *ch2, int flags); + +/*! + * \brief Remove from changeset those rdata which won't be added/removed from zone. + * + * \param zone The zone the changeset is going to be applied on. + * \param ch The cheangeset to be fixed. + * + * \return KNOT_E* + */ +int changeset_preapply_fix(const zone_contents_t *zone, changeset_t *ch); + +/*! + * \brief Remove from changeset records which are removed and added the same. + * + * \param ch Changeset to be fixed. + * + * \return KNOT_E* + */ +int changeset_cancelout(changeset_t *ch); + +/*! + * \brief Check the changes and SOA, ignoring possibly updated SOA serial. + * + * \note Also tolerates changed RRSIG of SOA. + * + * \param ch Changeset in question. + * + * \retval false If the changeset changes other records than SOA, or some SOA field + * other than serial changed. + * \retval true Otherwise. + */ +bool changeset_differs_just_serial(const changeset_t *ch); + +/*! + * \brief Loads zone contents from botstrap changeset. + * + * \param ch Changeset to load from, will be freed! + * \param out Zone contents. + * + * \return KNOT_E* + */ +int changeset_to_contents(changeset_t *ch, zone_contents_t **out); + +/*! + * \brief Creates a bootstrap changeset from zone. + * + * \param contents Contents to include, will be freed! + * + * \return Changeset, which shall be freed with changeset_from_contents_free() + */ +changeset_t *changeset_from_contents(const zone_contents_t *contents); + +/*! + * \brief Frees single changeset. + * + * \param ch Changeset from changeset_from_contents() to free. + */ +void changeset_from_contents_free(changeset_t *ch); + +/*! + * \brief Clears changesets in list. Changesets are not free'd. Legacy. + * + * \param chgs Changeset list to clear. + */ +void changesets_clear(list_t *chgs); + +/*! + * \brief Free changesets in list. Legacy. + * + * \param chgs Changeset list to free. + */ +void changesets_free(list_t *chgs); + +/*! + * \brief Clear single changeset. + * + * \param ch Changeset to clear. + */ +void changeset_clear(changeset_t *ch); + +/*! + * \brief Copy changeset to newly allocated space, all rrsigs are copied. + * + * \param ch Changeset to be copied. + * + * \return a copy, or NULL if error. + */ +changeset_t *changeset_clone(const changeset_t *ch); + +/*! + * \brief Frees single changeset. + * + * \param ch Changeset to free. + */ +void changeset_free(changeset_t *ch); + +/*! + * \brief Inits changeset iteration structure with changeset additions. + * + * \param itt Iterator to init. + * \param ch Changeset to use. + * + * \return KNOT_E* + */ +int changeset_iter_add(changeset_iter_t *itt, const changeset_t *ch); + +/*! + * \brief Inits changeset iteration structure with changeset removals. + * + * \param itt Iterator to init. + * \param ch Changeset to use. + * + * \return KNOT_E* + */ +int changeset_iter_rem(changeset_iter_t *itt, const changeset_t *ch); + +/*! + * \brief Inits changeset iteration structure with changeset additions and removals. + * + * \param itt Iterator to init. + * \param ch Changeset to use. + * + * \return KNOT_E* + */ +int changeset_iter_all(changeset_iter_t *itt, const changeset_t *ch); + +/*! + * \brief Gets next RRSet from changeset iterator. + * + * \param it Changeset iterator. + * + * \return Next RRSet in iterator, empty RRSet if iteration done. + */ +knot_rrset_t changeset_iter_next(changeset_iter_t *it); + +/*! + * \brief Free resources allocated by changeset iterator. + * + * \param it Iterator to clear. + */ +void changeset_iter_clear(changeset_iter_t *it); + +/*! + * \brief A pointer type for callback for changeset_walk() function. + * + * \param rrset An actual removal/addition inside the changeset. + * \param addition Indicates addition against removal. + * \param ctx A context passed to the changeset_walk() function. + * + * \retval KNOT_EOK if all ok, iteration will continue + * \return KNOT_E* if error, iteration will stop immediately and changeset_walk() returns this error. + */ +typedef int (*changeset_walk_callback)(const knot_rrset_t *rrset, bool addition, void *ctx); + +/*! + * \brief Calls a callback for each removal/addition in the changeset. + * + * \param changeset Changeset. + * \param callback Callback. + * \param ctx Arbitrary context passed to the callback. + * + * \return KNOT_E* + */ +int changeset_walk(const changeset_t *changeset, changeset_walk_callback callback, void *ctx); + +/*! + * + * \brief Dumps the changeset into text file. + * + * \param changeset Changeset. + * \param outfile File to write into. + * \param color Use unix tty color metacharacters. + */ +void changeset_print(const changeset_t *changeset, FILE *outfile, bool color); diff --git a/src/knot/updates/ddns.c b/src/knot/updates/ddns.c new file mode 100644 index 0000000..cd2952e --- /dev/null +++ b/src/knot/updates/ddns.c @@ -0,0 +1,769 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> + +#include "knot/common/log.h" +#include "knot/updates/ddns.h" +#include "knot/updates/changesets.h" +#include "knot/updates/zone-update.h" +#include "knot/zone/serial.h" +#include "libknot/libknot.h" +#include "contrib/ucw/lists.h" + +/* ----------------------------- prereq check ------------------------------- */ + +/*!< \brief Clears prereq RRSet list. */ +static void rrset_list_clear(list_t *l) +{ + node_t *n, *nxt; + WALK_LIST_DELSAFE(n, nxt, *l) { + ptrnode_t *ptr_n = (ptrnode_t *)n; + knot_rrset_t *rrset = (knot_rrset_t *)ptr_n->d; + knot_rrset_free(rrset, NULL); + free(n); + }; +} + +/*!< \brief Adds RR to prereq RRSet list, merges RRs into RRSets. */ +static int add_rr_to_list(list_t *l, const knot_rrset_t *rr) +{ + node_t *n; + WALK_LIST(n, *l) { + ptrnode_t *ptr_n = (ptrnode_t *)n; + knot_rrset_t *rrset = (knot_rrset_t *)ptr_n->d; + if (knot_rrset_equal(rr, rrset, KNOT_RRSET_COMPARE_HEADER)) { + return knot_rdataset_merge(&rrset->rrs, &rr->rrs, NULL); + } + }; + + knot_rrset_t *rr_copy = knot_rrset_copy(rr, NULL); + if (rr_copy == NULL) { + return KNOT_ENOMEM; + } + return ptrlist_add(l, rr_copy, NULL) != NULL ? KNOT_EOK : KNOT_ENOMEM; +} + +/*!< \brief Checks whether RRSet exists in the zone. */ +static int check_rrset_exists(zone_update_t *update, const knot_rrset_t *rrset, + uint16_t *rcode) +{ + assert(rrset->type != KNOT_RRTYPE_ANY); + + const zone_node_t *node = zone_update_get_node(update, rrset->owner); + if (node == NULL || !node_rrtype_exists(node, rrset->type)) { + *rcode = KNOT_RCODE_NXRRSET; + return KNOT_EPREREQ; + } else { + knot_rrset_t found = node_rrset(node, rrset->type); + assert(!knot_rrset_empty(&found)); + if (knot_rrset_equal(&found, rrset, KNOT_RRSET_COMPARE_WHOLE)) { + return KNOT_EOK; + } else { + *rcode = KNOT_RCODE_NXRRSET; + return KNOT_EPREREQ; + } + } +} + +/*!< \brief Checks whether RRSets in the list exist in the zone. */ +static int check_stored_rrsets(list_t *l, zone_update_t *update, + uint16_t *rcode) +{ + node_t *n; + WALK_LIST(n, *l) { + ptrnode_t *ptr_n = (ptrnode_t *)n; + knot_rrset_t *rrset = (knot_rrset_t *)ptr_n->d; + int ret = check_rrset_exists(update, rrset, rcode); + if (ret != KNOT_EOK) { + return ret; + } + }; + + return KNOT_EOK; +} + +/*!< \brief Checks whether node of given owner, with given type exists. */ +static bool check_type(zone_update_t *update, const knot_rrset_t *rrset) +{ + assert(rrset->type != KNOT_RRTYPE_ANY); + const zone_node_t *node = zone_update_get_node(update, rrset->owner); + if (node == NULL || !node_rrtype_exists(node, rrset->type)) { + return false; + } + + return true; +} + +/*!< \brief Checks whether RR type exists in the zone. */ +static int check_type_exist(zone_update_t *update, + const knot_rrset_t *rrset, uint16_t *rcode) +{ + assert(rrset->rclass == KNOT_CLASS_ANY); + if (check_type(update, rrset)) { + return KNOT_EOK; + } else { + *rcode = KNOT_RCODE_NXRRSET; + return KNOT_EPREREQ; + } +} + +/*!< \brief Checks whether RR type is not in the zone. */ +static int check_type_not_exist(zone_update_t *update, + const knot_rrset_t *rrset, uint16_t *rcode) +{ + assert(rrset->rclass == KNOT_CLASS_NONE); + if (check_type(update, rrset)) { + *rcode = KNOT_RCODE_YXRRSET; + return KNOT_EPREREQ; + } else { + return KNOT_EOK; + } +} + +/*!< \brief Checks whether DNAME is in the zone. */ +static int check_in_use(zone_update_t *update, + const knot_dname_t *dname, uint16_t *rcode) +{ + const zone_node_t *node = zone_update_get_node(update, dname); + if (node == NULL || node->rrset_count == 0) { + *rcode = KNOT_RCODE_NXDOMAIN; + return KNOT_EPREREQ; + } else { + return KNOT_EOK; + } +} + +/*!< \brief Checks whether DNAME is not in the zone. */ +static int check_not_in_use(zone_update_t *update, + const knot_dname_t *dname, uint16_t *rcode) +{ + const zone_node_t *node = zone_update_get_node(update, dname); + if (node == NULL || node->rrset_count == 0) { + return KNOT_EOK; + } else { + *rcode = KNOT_RCODE_YXDOMAIN; + return KNOT_EPREREQ; + } +} + +/*!< \brief Returns true if rrset has 0 data or RDATA of size 0 (we need TTL). */ +static bool rrset_empty(const knot_rrset_t *rrset) +{ + switch (rrset->rrs.count) { + case 0: + return true; + case 1: + return rrset->rrs.rdata->len == 0; + default: + return false; + } +} + +/*< \brief Returns true if DDNS should deny updating DNSSEC-related record. */ +static bool is_dnssec_protected(uint16_t type, bool is_apex) +{ + switch (type) { + case KNOT_RRTYPE_RRSIG: + case KNOT_RRTYPE_NSEC: + case KNOT_RRTYPE_NSEC3: + case KNOT_RRTYPE_CDNSKEY: + case KNOT_RRTYPE_CDS: + return true; + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_NSEC3PARAM: + return is_apex; + default: + return false; + } +} + +/*!< \brief Checks prereq for given packet RR. */ +static int process_prereq(const knot_rrset_t *rrset, uint16_t qclass, + zone_update_t *update, uint16_t *rcode, + list_t *rrset_list) +{ + if (rrset->ttl != 0) { + *rcode = KNOT_RCODE_FORMERR; + return KNOT_EMALF; + } + + if (knot_dname_in_bailiwick(rrset->owner, update->zone->name) < 0) { + *rcode = KNOT_RCODE_NOTZONE; + return KNOT_EOUTOFZONE; + } + + if (rrset->rclass == KNOT_CLASS_ANY) { + if (!rrset_empty(rrset)) { + *rcode = KNOT_RCODE_FORMERR; + return KNOT_EMALF; + } + if (rrset->type == KNOT_RRTYPE_ANY) { + return check_in_use(update, rrset->owner, rcode); + } else { + return check_type_exist(update, rrset, rcode); + } + } else if (rrset->rclass == KNOT_CLASS_NONE) { + if (!rrset_empty(rrset)) { + *rcode = KNOT_RCODE_FORMERR; + return KNOT_EMALF; + } + if (rrset->type == KNOT_RRTYPE_ANY) { + return check_not_in_use(update, rrset->owner, rcode); + } else { + return check_type_not_exist(update, rrset, rcode); + } + } else if (rrset->rclass == qclass) { + // Store RRs for full check into list + int ret = add_rr_to_list(rrset_list, rrset); + if (ret != KNOT_EOK) { + *rcode = KNOT_RCODE_SERVFAIL; + } + return ret; + } else { + *rcode = KNOT_RCODE_FORMERR; + return KNOT_EMALF; + } +} + +/* --------------------------- DDNS processing ------------------------------ */ + +/* --------------------- true/false helper functions ------------------------ */ + +static inline bool is_addition(const knot_rrset_t *rr) +{ + return rr->rclass == KNOT_CLASS_IN; +} + +static inline bool is_removal(const knot_rrset_t *rr) +{ + return rr->rclass == KNOT_CLASS_NONE || rr->rclass == KNOT_CLASS_ANY; +} + +static inline bool is_rr_removal(const knot_rrset_t *rr) +{ + return rr->rclass == KNOT_CLASS_NONE; +} + +static inline bool is_rrset_removal(const knot_rrset_t *rr) +{ + return rr->rclass == KNOT_CLASS_ANY && rr->type != KNOT_RRTYPE_ANY; +} + +static inline bool is_node_removal(const knot_rrset_t *rr) +{ + return rr->rclass == KNOT_CLASS_ANY && rr->type == KNOT_RRTYPE_ANY; +} + +/*!< \brief Returns true if last addition of certain types is to be replaced. */ +static bool should_replace(const knot_rrset_t *rrset) +{ + return rrset->type == KNOT_RRTYPE_CNAME || + rrset->type == KNOT_RRTYPE_NSEC3PARAM; +} + +/*!< \brief Returns true if node contains given RR in its RRSets. */ +static bool node_contains_rr(const zone_node_t *node, + const knot_rrset_t *rrset) +{ + const knot_rdataset_t *zone_rrs = node_rdataset(node, rrset->type); + if (zone_rrs != NULL) { + assert(rrset->rrs.count == 1); + return knot_rdataset_member(zone_rrs, rrset->rrs.rdata); + } else { + return false; + } +} + +/*!< \brief Returns true if CNAME is in this node. */ +static bool adding_to_cname(const knot_dname_t *owner, + const zone_node_t *node) +{ + if (node == NULL) { + // Node did not exist before update. + return false; + } + + knot_rrset_t cname = node_rrset(node, KNOT_RRTYPE_CNAME); + if (knot_rrset_empty(&cname)) { + // Node did not contain CNAME before update. + return false; + } + + // CNAME present + return true; +} + +/*!< \brief Used to ignore SOA deletions and SOAs with lower serial than zone. */ +static bool skip_soa(const knot_rrset_t *rr, int64_t sn) +{ + if (rr->type == KNOT_RRTYPE_SOA && + (rr->rclass == KNOT_CLASS_NONE || rr->rclass == KNOT_CLASS_ANY || + (serial_compare(knot_soa_serial(rr->rrs.rdata), sn) != SERIAL_GREATER))) { + return true; + } + + return false; +} + +/* ---------------------- changeset manipulation ---------------------------- */ + +/*!< \brief Replaces possible singleton RR type in changeset. */ +static bool singleton_replaced(changeset_t *changeset, + const knot_rrset_t *rr) +{ + if (!should_replace(rr)) { + return false; + } + + zone_node_t *n = zone_contents_find_node_for_rr(changeset->add, rr); + if (n == NULL) { + return false; + } + + knot_rdataset_t *rrs = node_rdataset(n, rr->type); + if (rrs == NULL) { + return false; + } + + // Replace singleton RR. + knot_rdataset_clear(rrs, NULL); + node_remove_rdataset(n, rr->type); + node_add_rrset(n, rr, NULL); + + return true; +} + +/*!< \brief Adds RR into add section of changeset if it is deemed worthy. */ +static int add_rr_to_chgset(const knot_rrset_t *rr, + zone_update_t *update) +{ + if (singleton_replaced(&update->change, rr)) { + return KNOT_EOK; + } + + return zone_update_add(update, rr); +} + +/* ------------------------ RR processing logic ----------------------------- */ + +/* --------------------------- RR additions --------------------------------- */ + +/*!< \brief Processes CNAME addition (replace or ignore) */ +static int process_add_cname(const zone_node_t *node, + const knot_rrset_t *rr, + zone_update_t *update) +{ + knot_rrset_t cname = node_rrset(node, KNOT_RRTYPE_CNAME); + if (!knot_rrset_empty(&cname)) { + // If they are identical, ignore. + if (knot_rrset_equal(&cname, rr, KNOT_RRSET_COMPARE_WHOLE)) { + return KNOT_EOK; + } + + int ret = zone_update_remove(update, &cname); + if (ret != KNOT_EOK) { + return ret; + } + + return add_rr_to_chgset(rr, update); + } else if (!node_empty(node)) { + // Other occupied node => ignore. + return KNOT_EOK; + } else { + // Can add. + return add_rr_to_chgset(rr, update); + } +} + +/*!< \brief Processes NSEC3PARAM addition (ignore when not removed, or non-apex) */ +static int process_add_nsec3param(const zone_node_t *node, + const knot_rrset_t *rr, + zone_update_t *update) +{ + if (node == NULL || !node_rrtype_exists(node, KNOT_RRTYPE_SOA)) { + // Ignore non-apex additions + char *owner = knot_dname_to_str_alloc(rr->owner); + log_warning("DDNS, refusing to add NSEC3PARAM to non-apex " + "node '%s'", owner); + free(owner); + return KNOT_EDENIED; + } + knot_rrset_t param = node_rrset(node, KNOT_RRTYPE_NSEC3PARAM); + if (knot_rrset_empty(¶m)) { + return add_rr_to_chgset(rr, update); + } + + char *owner = knot_dname_to_str_alloc(rr->owner); + log_warning("DDNS, refusing to add second NSEC3PARAM to node '%s'", owner); + free(owner); + + return KNOT_EOK; +} + +/*! + * \brief Processes SOA addition (ignore when non-apex), lower serials + * dropped before. + */ +static int process_add_soa(const zone_node_t *node, + const knot_rrset_t *rr, + zone_update_t *update) +{ + if (node == NULL || !node_rrtype_exists(node, KNOT_RRTYPE_SOA)) { + // Adding SOA to non-apex node, ignore. + return KNOT_EOK; + } + + // Get current SOA RR. + knot_rrset_t removed = node_rrset(node, KNOT_RRTYPE_SOA); + if (knot_rrset_equal(&removed, rr, KNOT_RRSET_COMPARE_WHOLE)) { + // If they are identical, ignore. + return KNOT_EOK; + } + + return add_rr_to_chgset(rr, update); +} + +/*!< \brief Adds normal RR, ignores when CNAME exists in node. */ +static int process_add_normal(const zone_node_t *node, + const knot_rrset_t *rr, + zone_update_t *update) +{ + if (adding_to_cname(rr->owner, node)) { + // Adding RR to CNAME node, ignore. + return KNOT_EOK; + } + + if (node && node_contains_rr(node, rr)) { + // Adding existing RR, ignore. + return KNOT_EOK; + } + + return add_rr_to_chgset(rr, update); +} + +/*!< \brief Decides what to do with RR addition. */ +static int process_add(const knot_rrset_t *rr, + const zone_node_t *node, + zone_update_t *update) +{ + switch(rr->type) { + case KNOT_RRTYPE_CNAME: + return process_add_cname(node, rr, update); + case KNOT_RRTYPE_SOA: + return process_add_soa(node, rr, update); + case KNOT_RRTYPE_NSEC3PARAM: + return process_add_nsec3param(node, rr, update); + default: + return process_add_normal(node, rr, update); + } +} + +/* --------------------------- RR deletions --------------------------------- */ + +/*!< \brief Removes single RR from zone. */ +static int process_rem_rr(const knot_rrset_t *rr, + const zone_node_t *node, + zone_update_t *update) +{ + if (node == NULL) { + // Removing from node that does not exist + return KNOT_EOK; + } + + const bool apex_ns = node_rrtype_exists(node, KNOT_RRTYPE_SOA) && + rr->type == KNOT_RRTYPE_NS; + if (apex_ns) { + const knot_rdataset_t *ns_rrs = + node_rdataset(node, KNOT_RRTYPE_NS); + if (ns_rrs == NULL) { + // Zone without apex NS. + return KNOT_EOK; + } + if (ns_rrs->count == 1) { + // Cannot remove last apex NS RR. + return KNOT_EOK; + } + } + + knot_rrset_t to_modify = node_rrset(node, rr->type); + if (knot_rrset_empty(&to_modify)) { + // No such RRSet + return KNOT_EOK; + } + + knot_rdataset_t *rrs = node_rdataset(node, rr->type); + if (!knot_rdataset_member(rrs, rr->rrs.rdata)) { + // Node does not contain this RR + return KNOT_EOK; + } + + return zone_update_remove(update, rr); +} + +/*!< \brief Removes RRSet from zone. */ +static int process_rem_rrset(const knot_rrset_t *rrset, + const zone_node_t *node, + zone_update_t *update) +{ + bool is_apex = node_rrtype_exists(node, KNOT_RRTYPE_SOA); + + if (rrset->type == KNOT_RRTYPE_SOA || is_dnssec_protected(rrset->type, is_apex)) { + // Ignore SOA and DNSSEC removals. + return KNOT_EOK; + } + + if (is_apex && rrset->type == KNOT_RRTYPE_NS) { + // Ignore NS apex RRSet removals. + return KNOT_EOK; + } + + if (node == NULL) { + // no such node in zone, ignore + return KNOT_EOK; + } + + if (!node_rrtype_exists(node, rrset->type)) { + // no such RR, ignore + return KNOT_EOK; + } + + knot_rrset_t to_remove = node_rrset(node, rrset->type); + return zone_update_remove(update, &to_remove); +} + +/*!< \brief Removes node from zone. */ +static int process_rem_node(const knot_rrset_t *rr, + const zone_node_t *node, zone_update_t *update) +{ + if (node == NULL) { + return KNOT_EOK; + } + + zone_node_t *node_copy = node_shallow_copy(node, NULL); + if (node_copy == NULL) { + return KNOT_ENOMEM; + } + + // Remove all RRSets from node + size_t rrset_count = node_copy->rrset_count; + for (int i = 0; i < rrset_count; ++i) { + knot_rrset_t rrset = node_rrset_at(node_copy, rrset_count - i - 1); + int ret = process_rem_rrset(&rrset, node_copy, update); + if (ret != KNOT_EOK) { + node_free(node_copy, NULL); + return ret; + } + } + + node_free(node_copy, NULL); + + return KNOT_EOK; +} + +/*!< \brief Decides what to with removal. */ +static int process_remove(const knot_rrset_t *rr, + const zone_node_t *node, + zone_update_t *update) +{ + if (is_rr_removal(rr)) { + return process_rem_rr(rr, node, update); + } else if (is_rrset_removal(rr)) { + return process_rem_rrset(rr, node, update); + } else if (is_node_removal(rr)) { + return process_rem_node(rr, node, update); + } else { + return KNOT_EINVAL; + } +} + +/* --------------------------- validity checks ------------------------------ */ + +/*!< \brief Checks whether addition has not violated DNAME rules. */ +static bool sem_check(const knot_rrset_t *rr, const zone_node_t *zone_node, + zone_update_t *update) +{ + // Check that we have not added DNAME child + const knot_dname_t *parent_dname = knot_wire_next_label(rr->owner, NULL); + const zone_node_t *parent = zone_update_get_node(update, parent_dname); + if (parent == NULL) { + return true; + } + + if (node_rrtype_exists(parent, KNOT_RRTYPE_DNAME)) { + // Parent has DNAME RRSet, refuse update + return false; + } + + if (rr->type != KNOT_RRTYPE_DNAME || zone_node == NULL) { + return true; + } + + // Check that we have not created node with DNAME children. + if (zone_node->children > 0) { + // Updated node has children and DNAME was added, refuse update + return false; + } + + return true; +} + +/*!< \brief Checks whether we can accept this RR. */ +static int check_update(const knot_rrset_t *rrset, const knot_pkt_t *query, + uint16_t *rcode) +{ + /* Accept both subdomain and dname match. */ + const knot_dname_t *owner = rrset->owner; + const knot_dname_t *qname = knot_pkt_qname(query); + const int in_bailiwick = knot_dname_in_bailiwick(owner, qname); + if (in_bailiwick < 0) { + *rcode = KNOT_RCODE_NOTZONE; + return KNOT_EOUTOFZONE; + } + const bool is_apex = in_bailiwick == 0; + + if (is_dnssec_protected(rrset->type, is_apex)) { + *rcode = KNOT_RCODE_REFUSED; + log_warning("DDNS, refusing to update DNSSEC-related record"); + return KNOT_EDENIED; + } + + if (rrset->rclass == knot_pkt_qclass(query)) { + if (knot_rrtype_is_metatype(rrset->type)) { + *rcode = KNOT_RCODE_FORMERR; + return KNOT_EMALF; + } + } else if (rrset->rclass == KNOT_CLASS_ANY) { + if (!rrset_empty(rrset) || + (knot_rrtype_is_metatype(rrset->type) && + rrset->type != KNOT_RRTYPE_ANY)) { + *rcode = KNOT_RCODE_FORMERR; + return KNOT_EMALF; + } + } else if (rrset->rclass == KNOT_CLASS_NONE) { + if (rrset->ttl != 0 || knot_rrtype_is_metatype(rrset->type)) { + *rcode = KNOT_RCODE_FORMERR; + return KNOT_EMALF; + } + } else { + *rcode = KNOT_RCODE_FORMERR; + return KNOT_EMALF; + } + + return KNOT_EOK; +} + +/*!< \brief Checks RR and decides what to do with it. */ +static int process_rr(const knot_rrset_t *rr, zone_update_t *update) +{ + const zone_node_t *node = zone_update_get_node(update, rr->owner); + + if (is_addition(rr)) { + int ret = process_add(rr, node, update); + if (ret == KNOT_EOK) { + if (!sem_check(rr, node, update)) { + return KNOT_EDENIED; + } + } + return ret; + } else if (is_removal(rr)) { + return process_remove(rr, node, update); + } else { + return KNOT_EMALF; + } +} + +/*!< \brief Maps Knot return code to RCODE. */ +static uint16_t ret_to_rcode(int ret) +{ + if (ret == KNOT_EMALF) { + return KNOT_RCODE_FORMERR; + } else if (ret == KNOT_EDENIED) { + return KNOT_RCODE_REFUSED; + } else { + return KNOT_RCODE_SERVFAIL; + } +} + +/* ---------------------------------- API ----------------------------------- */ + +int ddns_process_prereqs(const knot_pkt_t *query, zone_update_t *update, + uint16_t *rcode) +{ + if (query == NULL || rcode == NULL || update == NULL) { + return KNOT_EINVAL; + } + + int ret = KNOT_EOK; + list_t rrset_list; // List used to store merged RRSets + init_list(&rrset_list); + + const knot_pktsection_t *answer = knot_pkt_section(query, KNOT_ANSWER); + const knot_rrset_t *answer_rr = knot_pkt_rr(answer, 0); + for (int i = 0; i < answer->count; ++i) { + // Check what can be checked, store full RRs into list + ret = process_prereq(&answer_rr[i], knot_pkt_qclass(query), + update, rcode, &rrset_list); + if (ret != KNOT_EOK) { + rrset_list_clear(&rrset_list); + return ret; + } + } + + // Check stored RRSets + ret = check_stored_rrsets(&rrset_list, update, rcode); + rrset_list_clear(&rrset_list); + return ret; +} + +int ddns_process_update(const zone_t *zone, const knot_pkt_t *query, + zone_update_t *update, uint16_t *rcode) +{ + if (zone == NULL || query == NULL || update == NULL || rcode == NULL) { + if (rcode) { + *rcode = ret_to_rcode(KNOT_EINVAL); + } + return KNOT_EINVAL; + } + + uint32_t sn_old = knot_soa_serial(zone_update_from(update)->rdata); + + // Process all RRs in the authority section. + const knot_pktsection_t *authority = knot_pkt_section(query, KNOT_AUTHORITY); + const knot_rrset_t *authority_rr = knot_pkt_rr(authority, 0); + for (uint16_t i = 0; i < authority->count; ++i) { + const knot_rrset_t *rr = &authority_rr[i]; + // Check if RR is correct. + int ret = check_update(rr, query, rcode); + if (ret != KNOT_EOK) { + assert(*rcode != KNOT_RCODE_NOERROR); + return ret; + } + + if (skip_soa(rr, sn_old)) { + continue; + } + + ret = process_rr(rr, update); + if (ret != KNOT_EOK) { + *rcode = ret_to_rcode(ret); + return ret; + } + } + + *rcode = KNOT_RCODE_NOERROR; + return KNOT_EOK; +} diff --git a/src/knot/updates/ddns.h b/src/knot/updates/ddns.h new file mode 100644 index 0000000..171245d --- /dev/null +++ b/src/knot/updates/ddns.h @@ -0,0 +1,57 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \file + * + * \brief Dynamic updates processing. + * + * \addtogroup ddns + * @{ + */ + +#pragma once + +#include "knot/updates/zone-update.h" +#include "knot/zone/zone.h" +#include "libknot/packet/pkt.h" + +/*! + * \brief Checks update prerequisite section. + * + * \param query DNS message containing the update. + * \param update Zone to be checked. + * \param rcode Returned DNS RCODE. + * + * \return KNOT_E* + */ +int ddns_process_prereqs(const knot_pkt_t *query, zone_update_t *update, + uint16_t *rcode); + +/*! + * \brief Processes DNS update and creates a changeset out of it. Zone is left + * intact. + * + * \param zone Zone to be updated. + * \param query DNS message containing the update. + * \param update Output changeset. + * \param rcode Output DNS RCODE. + * + * \return KNOT_E* + */ +int ddns_process_update(const zone_t *zone, const knot_pkt_t *query, + zone_update_t *update, uint16_t *rcode); + +/*! @} */ diff --git a/src/knot/updates/zone-update.c b/src/knot/updates/zone-update.c new file mode 100644 index 0000000..7905a33 --- /dev/null +++ b/src/knot/updates/zone-update.c @@ -0,0 +1,895 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "knot/common/log.h" +#include "knot/dnssec/zone-events.h" +#include "knot/updates/zone-update.h" +#include "knot/zone/serial.h" +#include "knot/zone/zone-diff.h" +#include "contrib/mempattern.h" +#include "contrib/trim.h" +#include "contrib/ucw/lists.h" +#include "contrib/ucw/mempool.h" + +#include <urcu.h> + +static int init_incremental(zone_update_t *update, zone_t *zone, zone_contents_t *old_contents, bool deep_copy) +{ + if (old_contents == NULL) { + return KNOT_EINVAL; + } + + int ret = changeset_init(&update->change, zone->name); + if (ret != KNOT_EOK) { + return ret; + } + + if (deep_copy) { + update->new_cont_deep_copy = true; + update->new_cont = old_contents; + } else { + update->new_cont_deep_copy = false; + ret = apply_prepare_zone_copy(old_contents, &update->new_cont); + if (ret != KNOT_EOK) { + changeset_clear(&update->change); + return ret; + } + } + + uint32_t apply_flags = update->flags & UPDATE_STRICT ? APPLY_STRICT : 0; + apply_init_ctx(update->a_ctx, update->new_cont, apply_flags); + + /* Copy base SOA RR. */ + update->change.soa_from = + node_create_rrset(old_contents->apex, KNOT_RRTYPE_SOA); + if (update->change.soa_from == NULL) { + zone_contents_free(update->new_cont); + changeset_clear(&update->change); + return KNOT_ENOMEM; + } + + return KNOT_EOK; +} + +static int init_full(zone_update_t *update, zone_t *zone) +{ + update->new_cont = zone_contents_new(zone->name); + if (update->new_cont == NULL) { + return KNOT_ENOMEM; + } + + update->new_cont_deep_copy = true; + + apply_init_ctx(update->a_ctx, update->new_cont, 0); + + return KNOT_EOK; +} + +static int replace_soa(zone_contents_t *contents, const knot_rrset_t *rr) +{ + /* SOA possible only within apex. */ + if (!knot_dname_is_equal(rr->owner, contents->apex->owner)) { + return KNOT_EDENIED; + } + + knot_rrset_t old_soa = node_rrset(contents->apex, KNOT_RRTYPE_SOA); + zone_node_t *n = contents->apex; + int ret = zone_contents_remove_rr(contents, &old_soa, &n); + if (ret != KNOT_EOK && ret != KNOT_EINVAL) { + return ret; + } + + ret = zone_contents_add_rr(contents, rr, &n); + if (ret == KNOT_ETTL) { + return KNOT_EOK; + } + + return ret; +} + +int init_base(zone_update_t *update, zone_t *zone, zone_contents_t *old_contents, + zone_update_flags_t flags) +{ + if (update == NULL || zone == NULL || (old_contents == NULL && (flags & UPDATE_INCREMENTAL))) { + return KNOT_EINVAL; + } + + memset(update, 0, sizeof(*update)); + update->zone = zone; + + mm_ctx_mempool(&update->mm, MM_DEFAULT_BLKSIZE); + update->flags = flags; + + update->a_ctx = calloc(1, sizeof(*update->a_ctx)); + if (update->a_ctx == NULL) { + return KNOT_ENOMEM; + } + + int ret = KNOT_EINVAL; + if (flags & UPDATE_INCREMENTAL) { + ret = init_incremental(update, zone, old_contents, flags & UPDATE_JOURNAL); + } else if (flags & UPDATE_FULL) { + ret = init_full(update, zone); + } + if (ret != KNOT_EOK) { + free(update->a_ctx); + } + + return ret; +} + +/* ------------------------------- API -------------------------------------- */ + +int zone_update_init(zone_update_t *update, zone_t *zone, zone_update_flags_t flags) +{ + return init_base(update, zone, zone->contents, flags); +} + +int zone_update_from_differences(zone_update_t *update, zone_t *zone, zone_contents_t *old_cont, + zone_contents_t *new_cont, zone_update_flags_t flags, bool ignore_dnssec) +{ + if (update == NULL || zone == NULL || new_cont == NULL || + !(flags & UPDATE_INCREMENTAL) || (flags & UPDATE_FULL)) { + return KNOT_EINVAL; + } + + changeset_t diff; + int ret = changeset_init(&diff, zone->name); + if (ret != KNOT_EOK) { + return ret; + } + + ret = zone_contents_diff(old_cont, new_cont, &diff, ignore_dnssec); + if (ret != KNOT_EOK && ret != KNOT_ENODIFF && ret != KNOT_ESEMCHECK) { + changeset_clear(&diff); + return ret; + } + + // True if nonempty changes were made but the serial + // remained the same and has to be incremented. + bool diff_semcheck = (ret == KNOT_ESEMCHECK); + + ret = init_base(update, zone, old_cont, flags); + if (ret != KNOT_EOK) { + changeset_clear(&diff); + return ret; + } + + ret = zone_update_apply_changeset(update, &diff); + changeset_clear(&diff); + if (ret != KNOT_EOK) { + zone_update_clear(update); + return ret; + } + + if (diff_semcheck) { + ret = zone_update_increment_soa(update, conf()); + if (ret != KNOT_EOK) { + zone_update_clear(update); + return ret; + } + log_zone_info(zone->name, "automatic SOA serial increment"); + } + + return KNOT_EOK; +} + +int zone_update_from_contents(zone_update_t *update, zone_t *zone_without_contents, + zone_contents_t *new_cont, zone_update_flags_t flags) +{ + if (update == NULL || zone_without_contents == NULL || new_cont == NULL) { + return KNOT_EINVAL; + } + + memset(update, 0, sizeof(*update)); + update->zone = zone_without_contents; + + mm_ctx_mempool(&update->mm, MM_DEFAULT_BLKSIZE); + update->flags = flags; + + update->new_cont = new_cont; + update->new_cont_deep_copy = true; + + update->a_ctx = calloc(1, sizeof(*update->a_ctx)); + if (update->a_ctx == NULL) { + return KNOT_ENOMEM; + } + + if (flags & UPDATE_INCREMENTAL) { + int ret = changeset_init(&update->change, zone_without_contents->name); + if (ret != KNOT_EOK) { + free(update->a_ctx); + return ret; + } + + update->change.soa_from = node_create_rrset(new_cont->apex, KNOT_RRTYPE_SOA); + if (update->change.soa_from == NULL) { + changeset_clear(&update->change); + free(update->a_ctx); + return KNOT_ENOMEM; + } + } + + uint32_t apply_flags = update->flags & UPDATE_STRICT ? APPLY_STRICT : 0; + apply_init_ctx(update->a_ctx, update->new_cont, apply_flags); + + return KNOT_EOK; +} + +const zone_node_t *zone_update_get_node(zone_update_t *update, const knot_dname_t *dname) +{ + if (update == NULL || dname == NULL) { + return NULL; + } + + return zone_contents_find_node(update->new_cont, dname); +} + +const zone_node_t *zone_update_get_apex(zone_update_t *update) +{ + if (update == NULL) { + return NULL; + } + + return zone_update_get_node(update, update->zone->name); +} + +uint32_t zone_update_current_serial(zone_update_t *update) +{ + const zone_node_t *apex = zone_update_get_apex(update); + if (apex != NULL) { + return knot_soa_serial(node_rdataset(apex, KNOT_RRTYPE_SOA)->rdata); + } else { + return 0; + } +} + +const knot_rdataset_t *zone_update_from(zone_update_t *update) +{ + if (update == NULL) { + return NULL; + } + + if (update->flags & UPDATE_INCREMENTAL) { + const zone_node_t *apex = update->zone->contents->apex; + return node_rdataset(apex, KNOT_RRTYPE_SOA); + } + + return NULL; +} + +const knot_rdataset_t *zone_update_to(zone_update_t *update) +{ + if (update == NULL) { + return NULL; + } + + if (update->flags & UPDATE_FULL) { + const zone_node_t *apex = update->new_cont->apex; + return node_rdataset(apex, KNOT_RRTYPE_SOA); + } else if (update->flags & UPDATE_INCREMENTAL) { + if (update->change.soa_to == NULL) { + return NULL; + } + return &update->change.soa_to->rrs; + } + + return NULL; +} + +void zone_update_clear(zone_update_t *update) +{ + if (update == NULL) { + return; + } + + if (update->flags & UPDATE_INCREMENTAL) { + /* Revert any changes on error, do nothing on success. */ + if (update->new_cont_deep_copy) { + update_cleanup(update->a_ctx); + zone_contents_deep_free(update->new_cont); + } else { + update_rollback(update->a_ctx); + update_free_zone(update->new_cont); + } + changeset_clear(&update->change); + } else if (update->flags & UPDATE_FULL) { + assert(update->new_cont_deep_copy); + zone_contents_deep_free(update->new_cont); + } + free(update->a_ctx); + mp_delete(update->mm.ctx); + memset(update, 0, sizeof(*update)); +} + +int zone_update_add(zone_update_t *update, const knot_rrset_t *rrset) +{ + if (update == NULL || rrset == NULL) { + return KNOT_EINVAL; + } + + if (update->flags & UPDATE_INCREMENTAL) { + int ret = changeset_add_addition(&update->change, rrset, CHANGESET_CHECK); + if (ret != KNOT_EOK) { + return ret; + } + + if (rrset->type == KNOT_RRTYPE_SOA) { + /* replace previous SOA */ + ret = apply_replace_soa(update->a_ctx, &update->change); + if (ret != KNOT_EOK) { + changeset_remove_addition(&update->change, rrset); + } + return ret; + } + + ret = apply_add_rr(update->a_ctx, rrset); + if (ret != KNOT_EOK) { + changeset_remove_addition(&update->change, rrset); + return ret; + } + + return KNOT_EOK; + } else if (update->flags & UPDATE_FULL) { + if (rrset->type == KNOT_RRTYPE_SOA) { + /* replace previous SOA */ + return replace_soa(update->new_cont, rrset); + } + + zone_node_t *n = NULL; + int ret = zone_contents_add_rr(update->new_cont, rrset, &n); + if (ret == KNOT_ETTL) { + char buff[KNOT_DNAME_TXT_MAXLEN + 1]; + char *owner = knot_dname_to_str(buff, rrset->owner, sizeof(buff)); + if (owner == NULL) { + owner = ""; + } + char type[16] = { '\0' }; + knot_rrtype_to_string(rrset->type, type, sizeof(type)); + log_zone_notice(update->new_cont->apex->owner, + "TTL mismatch, owner %s, type %s, " + "TTL set to %u", owner, type, rrset->ttl); + return KNOT_EOK; + } + + return ret; + } else { + return KNOT_EINVAL; + } +} + +int zone_update_remove(zone_update_t *update, const knot_rrset_t *rrset) +{ + if (update == NULL || rrset == NULL) { + return KNOT_EINVAL; + } + + if (update->flags & UPDATE_INCREMENTAL) { + int ret = changeset_add_removal(&update->change, rrset, CHANGESET_CHECK); + if (ret != KNOT_EOK) { + return ret; + } + + if (rrset->type == KNOT_RRTYPE_SOA) { + /* SOA is replaced with addition */ + return KNOT_EOK; + } + + ret = apply_remove_rr(update->a_ctx, rrset); + if (ret != KNOT_EOK) { + changeset_remove_removal(&update->change, rrset); + return ret; + } + + return KNOT_EOK; + } else if (update->flags & UPDATE_FULL) { + zone_node_t *n = NULL; + knot_rrset_t *rrs_copy = knot_rrset_copy(rrset, &update->mm); + int ret = zone_contents_remove_rr(update->new_cont, rrs_copy, &n); + knot_rrset_free(rrs_copy, &update->mm); + return ret; + } else { + return KNOT_EINVAL; + } +} + +int zone_update_remove_rrset(zone_update_t *update, knot_dname_t *owner, uint16_t type) +{ + if (update == NULL || owner == NULL) { + return KNOT_EINVAL; + } + + if (update->flags & UPDATE_INCREMENTAL) { + /* Remove the RRSet from the original node */ + const zone_node_t *node = zone_contents_find_node(update->new_cont, owner); + if (node != NULL) { + knot_rrset_t rrset = node_rrset(node, type); + if (rrset.owner == NULL) { + return KNOT_ENOENT; + } + int ret = changeset_add_removal(&update->change, &rrset, + CHANGESET_CHECK); + if (ret != KNOT_EOK) { + return ret; + } + + if (type == KNOT_RRTYPE_SOA) { + /* SOA is replaced with addition */ + return KNOT_EOK; + } + + ret = apply_remove_rr(update->a_ctx, &rrset); + if (ret != KNOT_EOK) { + return ret; + } + } else { + return KNOT_ENONODE; + } + } else if (update->flags & UPDATE_FULL) { + /* Remove the RRSet from the non-synthesized new node */ + const zone_node_t *node = zone_contents_find_node(update->new_cont, owner); + if (node == NULL) { + return KNOT_ENONODE; + } + + knot_rrset_t rrset = node_rrset(node, type); + int ret = zone_update_remove(update, &rrset); + if (ret != KNOT_EOK) { + return ret; + } + } + + return KNOT_EOK; +} + +int zone_update_remove_node(zone_update_t *update, const knot_dname_t *owner) +{ + if (update == NULL || owner == NULL) { + return KNOT_EINVAL; + } + + if (update->flags & UPDATE_INCREMENTAL) { + /* Remove all RRSets from the new node */ + const zone_node_t *node = zone_contents_find_node(update->new_cont, owner); + if (node != NULL) { + size_t rrset_count = node->rrset_count; + for (int i = 0; i < rrset_count; ++i) { + knot_rrset_t rrset = node_rrset_at(node, rrset_count - 1 - i); + int ret = changeset_add_removal(&update->change, &rrset, + CHANGESET_CHECK); + if (ret != KNOT_EOK) { + return ret; + } + + if (rrset.type == KNOT_RRTYPE_SOA) { + /* SOA is replaced with addition */ + continue; + } + + ret = apply_remove_rr(update->a_ctx, &rrset); + if (ret != KNOT_EOK) { + return ret; + } + } + } else { + return KNOT_ENONODE; + } + } else if (update->flags & UPDATE_FULL) { + /* Remove all RRSets from the non-synthesized new node */ + const zone_node_t *node = zone_contents_find_node(update->new_cont, owner); + if (node == NULL) { + return KNOT_ENONODE; + } + + size_t rrset_count = node->rrset_count; + for (int i = 0; i < rrset_count; ++i) { + knot_rrset_t rrset = node_rrset_at(node, rrset_count - 1 - i); + int ret = zone_update_remove(update, &rrset); + if (ret != KNOT_EOK) { + return ret; + } + } + } + + return KNOT_EOK; +} + +int zone_update_apply_changeset(zone_update_t *update, const changeset_t *changes) +{ + int ret = KNOT_EOK; + if (update->flags & UPDATE_INCREMENTAL) { + ret = changeset_merge(&update->change, changes, CHANGESET_CHECK_CANCELOUT); + } + if (ret == KNOT_EOK) { + ret = apply_changeset_directly(update->a_ctx, changes); + } + return ret; +} + +int zone_update_apply_changeset_fix(zone_update_t *update, changeset_t *changes) +{ + int ret = changeset_cancelout(changes); + if (ret == KNOT_EOK) { + ret = changeset_preapply_fix(update->new_cont, changes); + } + if (ret == KNOT_EOK) { + ret = zone_update_apply_changeset(update, changes); + } + return ret; +} + +int zone_update_apply_changeset_reverse(zone_update_t *update, const changeset_t *changes) +{ + changeset_t reverse; + reverse.remove = changes->add; + reverse.add = changes->remove; + reverse.soa_from = changes->soa_to; + reverse.soa_to = changes->soa_from; + return zone_update_apply_changeset(update, &reverse); +} + +static int set_new_soa(zone_update_t *update, unsigned serial_policy) +{ + assert(update); + + knot_rrset_t *soa_cpy = node_create_rrset(zone_update_get_apex(update), + KNOT_RRTYPE_SOA); + if (soa_cpy == NULL) { + return KNOT_ENOMEM; + } + + int ret = zone_update_remove(update, soa_cpy); + if (ret != KNOT_EOK) { + knot_rrset_free(soa_cpy, NULL); + return ret; + } + + uint32_t old_serial = knot_soa_serial(soa_cpy->rrs.rdata); + uint32_t new_serial = serial_next(old_serial, serial_policy); + if (serial_compare(old_serial, new_serial) != SERIAL_LOWER) { + log_zone_warning(update->zone->name, "updated SOA serial is lower " + "than current, serial %u -> %u", + old_serial, new_serial); + ret = KNOT_ESOAINVAL; + } else { + knot_soa_serial_set(soa_cpy->rrs.rdata, new_serial); + + ret = zone_update_add(update, soa_cpy); + } + knot_rrset_free(soa_cpy, NULL); + + return ret; +} + +int zone_update_increment_soa(zone_update_t *update, conf_t *conf) +{ + if (update == NULL || conf == NULL) { + return KNOT_EINVAL; + } + + conf_val_t val = conf_zone_get(conf, C_SERIAL_POLICY, update->zone->name); + return set_new_soa(update, conf_opt(&val)); +} + +static int commit_incremental(conf_t *conf, zone_update_t *update, + zone_contents_t **contents_out) +{ + assert(update); + assert(contents_out); + + if (changeset_empty(&update->change)) { + changeset_clear(&update->change); + if (update->zone->contents == NULL || update->new_cont_deep_copy) { + *contents_out = update->new_cont; + } + return KNOT_EOK; + } + + zone_contents_t *new_contents = update->new_cont; + int ret = KNOT_EOK; + if (zone_update_to(update) == NULL) { + /* No SOA in the update, create one according to the current policy */ + ret = zone_update_increment_soa(update, conf); + if (ret != KNOT_EOK) { + zone_update_clear(update); + return ret; + } + } + + ret = apply_finalize(update->a_ctx); + if (ret != KNOT_EOK) { + zone_update_clear(update); + return ret; + } + + /* Write changes to journal if all went well. */ + conf_val_t val = conf_zone_get(conf, C_JOURNAL_CONTENT, update->zone->name); + if (conf_opt(&val) != JOURNAL_CONTENT_NONE) { + ret = zone_change_store(conf, update->zone, &update->change); + if (ret != KNOT_EOK) { + return ret; + } + } + + *contents_out = new_contents; + + return KNOT_EOK; +} + +static int commit_full(conf_t *conf, zone_update_t *update, zone_contents_t **contents_out) +{ + assert(update); + assert(contents_out); + + /* Check if we have SOA. We might consider adding full semantic check here. + * But if we wanted full sem-check I'd consider being it controlled by a flag + * - to enable/disable it on demand. */ + if (!node_rrtype_exists(update->new_cont->apex, KNOT_RRTYPE_SOA)) { + return KNOT_ESEMCHECK; + } + + int ret = zone_contents_adjust_full(update->new_cont); + if (ret != KNOT_EOK) { + zone_update_clear(update); + return ret; + } + + /* Store new zone contents in journal. */ + conf_val_t val = conf_zone_get(conf, C_JOURNAL_CONTENT, update->zone->name); + unsigned content = conf_opt(&val); + if (content == JOURNAL_CONTENT_ALL) { + ret = zone_in_journal_store(conf, update->zone, update->new_cont); + } else if (content != JOURNAL_CONTENT_NONE) { // zone_in_journal_store does this automatically + ret = zone_changes_clear(conf, update->zone); + } + + *contents_out = update->new_cont; + + return ret; +} + +/*! \brief Routine for calling call_rcu() easier way. + * + * Consider moving elsewhere, as it has no direct relation to zone-update. + */ +typedef struct { + struct rcu_head rcuhead; + void (*callback)(void *); + void *ctx; + bool free_ctx; +} callrcu_wrapper_t; + +static void callrcu_wrapper_cb(struct rcu_head *param) +{ + callrcu_wrapper_t *wrap = (callrcu_wrapper_t *)param; + wrap->callback(wrap->ctx); + if (wrap->free_ctx) { + free(wrap->ctx); + } + free(wrap); + + // Trim extra heap. + mem_trim(); +} + +/* NOTE: Does nothing if not enough memory. */ +static void callrcu_wrapper(void *ctx, void *callback, bool free_ctx) +{ + callrcu_wrapper_t *wrap = calloc(1, sizeof(callrcu_wrapper_t)); + if (wrap != NULL) { + wrap->callback = callback; + wrap->ctx = ctx; + wrap->free_ctx = free_ctx; + call_rcu((struct rcu_head *)wrap, callrcu_wrapper_cb); + } +} + +int zone_update_commit(conf_t *conf, zone_update_t *update) +{ + if (conf == NULL || update == NULL) { + return KNOT_EINVAL; + } + + int ret = KNOT_EOK; + zone_contents_t *new_contents = NULL; + if (update->flags & UPDATE_INCREMENTAL) { + ret = commit_incremental(conf, update, &new_contents); + } else { + ret = commit_full(conf, update, &new_contents); + } + if (ret != KNOT_EOK) { + return ret; + } + + /* If there is anything to change. */ + if (new_contents == NULL) { + return KNOT_EOK; + } + + /* Check the zone size. */ + conf_val_t val = conf_zone_get(conf, C_MAX_ZONE_SIZE, update->zone->name); + size_t size_limit = conf_int(&val); + + if (new_contents->size > size_limit) { + /* Recoverable error. */ + return KNOT_EZONESIZE; + } + + /* Check if the zone was re-signed upon zone load to ensure proper flush + * even if the SOA serial wasn't incremented by re-signing. */ + val = conf_zone_get(conf, C_DNSSEC_SIGNING, update->zone->name); + bool dnssec = conf_bool(&val); + if (!changeset_empty(&update->change) && dnssec) { + update->zone->zonefile.resigned = true; + } + + /* Switch zone contents. */ + zone_contents_t *old_contents; + old_contents = zone_switch_contents(update->zone, new_contents); + + /* Sync RCU. */ + if (update->flags & UPDATE_FULL) { + assert(update->new_cont_deep_copy); + callrcu_wrapper(old_contents, zone_contents_deep_free, false); + } else if (update->flags & UPDATE_INCREMENTAL) { + if (update->new_cont_deep_copy) { + callrcu_wrapper(old_contents, zone_contents_deep_free, false); + } else { + callrcu_wrapper(old_contents, update_free_zone, false); + } + changeset_clear(&update->change); + } + callrcu_wrapper(update->a_ctx, update_cleanup, true); + update->a_ctx = NULL; + update->new_cont = NULL; + + /* Sync zonefile immediately if configured. */ + val = conf_zone_get(conf, C_ZONEFILE_SYNC, update->zone->name); + if (conf_int(&val) == 0) { + zone_events_schedule_now(update->zone, ZONE_EVENT_FLUSH); + } + + return KNOT_EOK; +} + +static int iter_init_tree_iters(zone_update_iter_t *it, zone_update_t *update, + bool nsec3) +{ + /* Set zone iterator. */ + zone_contents_t *_contents = update->new_cont; + + /* Begin iteration. We can safely assume _contents is a valid pointer. */ + zone_tree_t *tree = nsec3 ? _contents->nsec3_nodes : _contents->nodes; + it->tree_it = trie_it_begin(tree); + if (it->tree_it == NULL) { + return KNOT_ENOMEM; + } + + it->cur_node = (zone_node_t *)(*trie_it_val(it->tree_it)); + + return KNOT_EOK; +} + +static int iter_get_next_node(zone_update_iter_t *it) +{ + trie_it_next(it->tree_it); + if (trie_it_finished(it->tree_it)) { + trie_it_free(it->tree_it); + it->tree_it = NULL; + it->cur_node = NULL; + return KNOT_ENOENT; + } + + it->cur_node = (zone_node_t *)(*trie_it_val(it->tree_it)); + + return KNOT_EOK; +} + +static int iter_init(zone_update_iter_t *it, zone_update_t *update, const bool nsec3) +{ + memset(it, 0, sizeof(*it)); + + it->update = update; + it->nsec3 = nsec3; + int ret = iter_init_tree_iters(it, update, nsec3); + if (ret != KNOT_EOK) { + return ret; + } + + it->cur_node = (zone_node_t *)(*trie_it_val(it->tree_it)); + + return KNOT_EOK; +} + +int zone_update_iter(zone_update_iter_t *it, zone_update_t *update) +{ + if (it == NULL || update == NULL) { + return KNOT_EINVAL; + } + + return iter_init(it, update, false); +} + +int zone_update_iter_nsec3(zone_update_iter_t *it, zone_update_t *update) +{ + if (it == NULL || update == NULL) { + return KNOT_EINVAL; + } + + if (update->flags & UPDATE_FULL) { + if (update->new_cont->nsec3_nodes == NULL) { + /* No NSEC3 tree. */ + return KNOT_ENOENT; + } + } else { + if (update->change.add->nsec3_nodes == NULL && + update->change.remove->nsec3_nodes == NULL) { + /* No NSEC3 changes. */ + return KNOT_ENOENT; + } + } + + return iter_init(it, update, true); +} + +int zone_update_iter_next(zone_update_iter_t *it) +{ + if (it == NULL) { + return KNOT_EINVAL; + } + + if (it->tree_it != NULL) { + int ret = iter_get_next_node(it); + if (ret != KNOT_EOK && ret != KNOT_ENOENT) { + return ret; + } + } + + return KNOT_EOK; +} + +const zone_node_t *zone_update_iter_val(zone_update_iter_t *it) +{ + if (it != NULL) { + return it->cur_node; + } else { + return NULL; + } +} + +void zone_update_iter_finish(zone_update_iter_t *it) +{ + if (it == NULL) { + return; + } + + trie_it_free(it->tree_it); +} + +bool zone_update_no_change(zone_update_t *update) +{ + if (update == NULL) { + return true; + } + + if (update->flags & UPDATE_INCREMENTAL) { + return changeset_empty(&update->change); + } else { + /* This branch does not make much sense and FULL update will most likely + * be a change every time anyway, just return false. */ + return false; + } +} diff --git a/src/knot/updates/zone-update.h b/src/knot/updates/zone-update.h new file mode 100644 index 0000000..b5d7a9a --- /dev/null +++ b/src/knot/updates/zone-update.h @@ -0,0 +1,321 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "knot/updates/apply.h" +#include "knot/conf/conf.h" +#include "knot/updates/changesets.h" +#include "knot/zone/contents.h" +#include "knot/zone/zone.h" +#include "libknot/mm_ctx.h" + +/*! \brief Structure for zone contents updating / querying. */ +typedef struct zone_update { + zone_t *zone; /*!< Zone being updated. */ + zone_contents_t *new_cont; /*!< New zone contents for full updates. */ + bool new_cont_deep_copy; /*!< On update_clear, perform deep free instead of shallow. */ + changeset_t change; /*!< Changes we want to apply. */ + apply_ctx_t *a_ctx; /*!< Context for applying changesets. */ + uint32_t flags; /*!< Zone update flags. */ + knot_mm_t mm; /*!< Memory context used for intermediate nodes. */ +} zone_update_t; + +typedef struct { + zone_update_t *update; /*!< The update we're iterating over. */ + trie_it_t *tree_it; /*!< Iterator for the new zone. */ + const zone_node_t *cur_node; /*!< Current node in the new zone. */ + bool nsec3; /*!< Set when we're using the NSEC3 node tree. */ +} zone_update_iter_t; + +typedef enum { + UPDATE_FULL = 1 << 0, /*!< Replace the old zone by a complete new one. */ + UPDATE_INCREMENTAL = 1 << 1, /*!< Apply changes to the old zone. */ + UPDATE_SIGN = 1 << 2, /*!< Sign the resulting zone. */ + UPDATE_JOURNAL = 1 << 3, /*!< Using zone-in-journal for a diff update. */ + UPDATE_STRICT = 1 << 4, /*!< Apply changes strictly, i.e. fail when removing nonexistent RR. */ +} zone_update_flags_t; + +/*! + * \brief Inits given zone update structure, new memory context is created. + * + * \param update Zone update structure to init. + * \param zone Init with this zone. + * \param flags Flags to control the behavior of the update. + * + * \return KNOT_E* + */ +int zone_update_init(zone_update_t *update, zone_t *zone, zone_update_flags_t flags); + +/*! + * \brief Inits update structure, the update is built like IXFR from differences. + * + * The existing zone with its own contents is taken as a base, + * the new candidate zone contents are taken as new contents, + * the diff is calculated, so that this update is INCREMENTAL. + * + * \param update Zone update structure to init. + * \param zone Init with this zone. + * \param old_cont The current zone contents the diff will be against. Probably zone->contents. + * \param new_cont New zone contents. Will be taken over (and later freed) by zone update. + * \param flags Flags for update. Must be UPDATE_INCREMENTAL. + * + * \return KNOT_E* + */ +int zone_update_from_differences(zone_update_t *update, zone_t *zone, zone_contents_t *old_cont, + zone_contents_t *new_cont, zone_update_flags_t flags, bool ignore_dnssec); + +/*! + * \brief Inits a zone update based on new zone contents. + * + * \param update Zone update structure to init. + * \param zone_without_contents Init with this zone. Its contents may be NULL. + * \param new_cont New zone contents. Will be taken over (and later freed) by zone update. + * \param flags Flags for update. + * + * \return KNOT_E* + */ +int zone_update_from_contents(zone_update_t *update, zone_t *zone_without_contents, + zone_contents_t *new_cont, zone_update_flags_t flags); + +/*! + * \brief Returns node that would be in the zone after updating it. + * + * \note Returned node is either zone original or synthesized, do *not* free + * or modify. Returned node is allocated on local mempool. + * + * \param update Zone update. + * \param dname Dname to search for. + * + * \return Node after zone update. + */ +const zone_node_t *zone_update_get_node(zone_update_t *update, + const knot_dname_t *dname); + +/*! + * \brief Returns updated zone apex. + * + * \note Returned node is either zone original or synthesized, do *not* free + * or modify. + * + * \param update Zone update. + * + * \return Returns apex after update. + */ +const zone_node_t *zone_update_get_apex(zone_update_t *update); + +/*! + * \brief Returns the serial from the current apex. + * + * \param update Zone update. + * + * \return 0 if no apex was found, its serial otherwise. + */ +uint32_t zone_update_current_serial(zone_update_t *update); + +/*! + * \brief Returns the SOA rdataset we're updating from. + * + * \param update Zone update. + * + * \return The original SOA rdataset. + */ +const knot_rdataset_t *zone_update_from(zone_update_t *update); + +/*! + * \brief Returns the SOA rdataset we're updating to. + * + * \param update Zone update. + * + * \return NULL if no new SOA has been added, new SOA otherwise. + */ +const knot_rdataset_t *zone_update_to(zone_update_t *update); + +/*! + * \brief Clear data allocated by given zone update structure. + * + * \param update Zone update to clear. + */ +void zone_update_clear(zone_update_t *update); + +/*! + * \brief Adds an RRSet to the zone. + * + * \warning Do not edit the zone_update when any iterator is active. Any + * zone_update modifications will invalidate the trie iterators + * in the zone_update iterator(s). + * + * \param update Zone update. + * \param rrset RRSet to add. + * + * \return KNOT_E* + */ +int zone_update_add(zone_update_t *update, const knot_rrset_t *rrset); + +/*! + * \brief Removes an RRSet from the zone. + * + * \warning Do not edit the zone_update when any iterator is active. Any + * zone_update modifications will invalidate the trie iterators + * in the zone_update iterator(s). + * + * \param update Zone update. + * \param rrset RRSet to remove. + * + * \return KNOT_E* + */ +int zone_update_remove(zone_update_t *update, const knot_rrset_t *rrset); + +/*! + * \brief Removes a whole RRSet of specified type from the zone. + * + * \warning Do not edit the zone_update when any iterator is active. Any + * zone_update modifications will invalidate the trie iterators + * in the zone_update iterator(s). + * + * \param update Zone update. + * \param owner Node name to remove. + * \param type RRSet type to remove. + * + * \return KNOT_E* + */ +int zone_update_remove_rrset(zone_update_t *update, knot_dname_t *owner, uint16_t type); + +/*! + * \brief Removes a whole node from the zone. + * + * \warning Do not edit the zone_update when any iterator is active. Any + * zone_update modifications will invalidate the trie iterators + * in the zone_update iterator(s). + * + * \param update Zone update. + * \param owner Node name to remove. + * + * \return KNOT_E* + */ +int zone_update_remove_node(zone_update_t *update, const knot_dname_t *owner); + +/*! + * \brief Adds and removes RRsets to/from the zone according to the changeset. + * + * \param update Zone update. + * \param changes Changes to be made in zone. + * + * \return KNOT_E* + */ +int zone_update_apply_changeset(zone_update_t *update, const changeset_t *changes); + +/*! + * \brief Applies a changeset to zone, the changeset is modified to contain only really added/removed rdata. + * + * \param update Zone update. + * \param changes In: changes to be made in zone; out: changes really made in zone. + * + * \return KNOT_E* + */ +int zone_update_apply_changeset_fix(zone_update_t *update, changeset_t *changes); + +/*! + * \brief Applies the changeset in reverse, rsets from REM section are added and from ADD section removed. + * + * \param update Zone update. + * \param changes Changes to be un-done. + * + * \return KNOT_E* + */ +int zone_update_apply_changeset_reverse(zone_update_t *update, const changeset_t *changes); + +/*! + * \brief Increment SOA serial (according to cofigured policy) in the update. + * + * \param update Update to be modified. + * \param conf Configuration. + * + * \return KNOT_E* + */ +int zone_update_increment_soa(zone_update_t *update, conf_t *conf); + +/*! + * \brief Commits all changes to the zone, signs it, saves changes to journal. + * + * \param conf Configuration. + * \param update Zone update. + * + * \return KNOT_E* + */ +int zone_update_commit(conf_t *conf, zone_update_t *update); + +/*! + * \brief Setup a zone_update iterator for both FULL and INCREMENTAL updates. + * + * \warning Do not init or use iterators when the zone is edited. Any + * zone_update modifications will invalidate the trie iterators + * in the zone_update iterator. + * + * \param it Iterator. + * \param update Zone update. + * + * \return KNOT_E* + */ +int zone_update_iter(zone_update_iter_t *it, zone_update_t *update); + +/*! + * \brief Setup a zone_update iterator for both FULL and INCREMENTAL updates. + * Version for iterating over nsec3 nodes. + * + * \warning Do not init or use iterators when the zone is edited. Any + * zone_update modifications will invalidate the trie iterators + * in the zone_update iterator. + * + * + * \param it Iterator. + * \param update Zone update. + * + * \return KNOT_E* + */ +int zone_update_iter_nsec3(zone_update_iter_t *it, zone_update_t *update); + +/*! + * \brief Move the iterator to the next item. + * + * \param it Iterator. + * + * \return KNOT_E* + */ +int zone_update_iter_next(zone_update_iter_t *it); + +/*! + * \brief Get the value of the iterator. + * + * \param it Iterator. + * + * \return A (synthesized or added) node with all its current data. + */ +const zone_node_t *zone_update_iter_val(zone_update_iter_t *it); + +/*! + * \brief Finish the iterator and clean it up. + * + * \param it Iterator. + */ +void zone_update_iter_finish(zone_update_iter_t *it); + +/*! + * \brief Returns bool whether there are any changes at all. + * + * \param update Zone update. + */ +bool zone_update_no_change(zone_update_t *update); diff --git a/src/knot/worker/pool.c b/src/knot/worker/pool.c new file mode 100644 index 0000000..9d8413a --- /dev/null +++ b/src/knot/worker/pool.c @@ -0,0 +1,242 @@ +/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <assert.h> +#include <pthread.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> + +#include "libknot/libknot.h" +#include "knot/server/dthreads.h" +#include "knot/worker/pool.h" + +/*! + * \brief Worker pool state. + */ +struct worker_pool { + dt_unit_t *threads; + + pthread_mutex_t lock; + pthread_cond_t wake; + + bool terminating; /*!< Is the pool terminating? .*/ + bool suspended; /*!< Is execution temporarily suspended? .*/ + int running; /*!< Number of running threads. */ + worker_queue_t tasks; +}; + +/*! + * \brief Worker thread. + * + * The thread takes a task from the tasks queue and runs it, while checking + * if the dispatching of new tasks is allowed by the thread pool. + * + * An execution of a running thread cannot be enforced. + * + */ +static int worker_main(dthread_t *thread) +{ + assert(thread); + + worker_pool_t *pool = thread->data; + + pthread_mutex_lock(&pool->lock); + + for (;;) { + if (pool->terminating) { + break; + } + + task_t *task = NULL; + if (!pool->suspended) { + task = worker_queue_dequeue(&pool->tasks); + } + + if (task == NULL) { + pthread_cond_wait(&pool->wake, &pool->lock); + continue; + } + + assert(task->run); + pool->running += 1; + + pthread_mutex_unlock(&pool->lock); + task->run(task); + pthread_mutex_lock(&pool->lock); + + pool->running -= 1; + pthread_cond_broadcast(&pool->wake); + } + + pthread_mutex_unlock(&pool->lock); + + return KNOT_EOK; +} + +/* -- public API ------------------------------------------------------------ */ + +worker_pool_t *worker_pool_create(unsigned threads) +{ + worker_pool_t *pool = malloc(sizeof(worker_pool_t)); + if (pool == NULL) { + return NULL; + } + + memset(pool, 0, sizeof(worker_pool_t)); + pool->threads = dt_create(threads, worker_main, NULL, pool); + if (pool->threads == NULL) { + goto fail; + } + + if (pthread_mutex_init(&pool->lock, NULL) != 0) { + goto fail; + } + + if (pthread_cond_init(&pool->wake, NULL) != 0) { + goto fail; + } + + worker_queue_init(&pool->tasks); + + return pool; + +fail: + dt_delete(&pool->threads); + free(pool); + return NULL; +} + +void worker_pool_destroy(worker_pool_t *pool) +{ + if (!pool) { + return; + } + + dt_delete(&pool->threads); + + pthread_mutex_destroy(&pool->lock); + pthread_cond_destroy(&pool->wake); + + worker_queue_deinit(&pool->tasks); + + free(pool); +} + +void worker_pool_start(worker_pool_t *pool) +{ + if (!pool) { + return; + } + + dt_start(pool->threads); +} + +void worker_pool_stop(worker_pool_t *pool) +{ + if (!pool) { + return; + } + + pthread_mutex_lock(&pool->lock); + pool->terminating = true; + pthread_cond_broadcast(&pool->wake); + pthread_mutex_unlock(&pool->lock); + + dt_stop(pool->threads); +} + +void worker_pool_suspend(worker_pool_t *pool) +{ + if (!pool) { + return; + } + + pthread_mutex_lock(&pool->lock); + pool->suspended = true; + pthread_mutex_unlock(&pool->lock); +} + +void worker_pool_resume(worker_pool_t *pool) +{ + if (!pool) { + return; + } + + pthread_mutex_lock(&pool->lock); + pool->suspended = false; + pthread_cond_broadcast(&pool->wake); + pthread_mutex_unlock(&pool->lock); +} + +void worker_pool_join(worker_pool_t *pool) +{ + if (!pool) { + return; + } + + dt_join(pool->threads); +} + +void worker_pool_wait(worker_pool_t *pool) +{ + if (!pool) { + return; + } + + pthread_mutex_lock(&pool->lock); + while (!EMPTY_LIST(pool->tasks.list) || pool->running > 0) { + pthread_cond_wait(&pool->wake, &pool->lock); + } + pthread_mutex_unlock(&pool->lock); +} + +void worker_pool_assign(worker_pool_t *pool, struct task *task) +{ + if (!pool || !task) { + return; + } + + pthread_mutex_lock(&pool->lock); + worker_queue_enqueue(&pool->tasks, task); + pthread_cond_signal(&pool->wake); + pthread_mutex_unlock(&pool->lock); +} + +void worker_pool_clear(worker_pool_t *pool) +{ + if (!pool) { + return; + } + + pthread_mutex_lock(&pool->lock); + worker_queue_deinit(&pool->tasks); + worker_queue_init(&pool->tasks); + pthread_mutex_unlock(&pool->lock); +} + +void worker_pool_status(worker_pool_t *pool, int *running, int *queued) +{ + if (!pool) { + *running = *queued = 0; + return; + } + + pthread_mutex_lock(&pool->lock); + *running = pool->running; + *queued = worker_queue_length(&pool->tasks); + pthread_mutex_unlock(&pool->lock); +} diff --git a/src/knot/worker/pool.h b/src/knot/worker/pool.h new file mode 100644 index 0000000..4f8b3dc --- /dev/null +++ b/src/knot/worker/pool.h @@ -0,0 +1,82 @@ +/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include "knot/worker/queue.h" + +struct worker_pool; +typedef struct worker_pool worker_pool_t; + +/*! + * \brief Initialize worker pool. + * + * \param threads Number of threads to be created. + * + * \return Thread pool or NULL in case of error. + */ +worker_pool_t *worker_pool_create(unsigned threads); + +/*! + * \brief Destroy the worker pool. + */ +void worker_pool_destroy(worker_pool_t *pool); + +/*! + * \brief Start all threads in the worker pool. + */ +void worker_pool_start(worker_pool_t *pool); + +/*! + * \brief Stop processing of new tasks, start stopping worker threads when possible. + */ +void worker_pool_stop(worker_pool_t *pool); + +/*! + * \brief Temporarily suspend the execution of worker pool. + */ +void worker_pool_suspend(worker_pool_t *pool); + +/*! + * \brief Resume the execution of worker pool. + */ +void worker_pool_resume(worker_pool_t *pool); + +/*! + * \brief Wait for all threads to terminate. + */ +void worker_pool_join(worker_pool_t *pool); + +/*! + * \brief Wait till the number of pending tasks is zero. + * + */ +void worker_pool_wait(worker_pool_t *pool); + +/*! + * \brief Assign a task to be performed by a worker in the pool. + */ +void worker_pool_assign(worker_pool_t *pool, struct task *task); + +/*! + * \brief Clear all tasks enqueued in pool processing queue. + */ +void worker_pool_clear(worker_pool_t *pool); + +/*! + * \brief Obtain info regarding how the pool is busy. + */ +void worker_pool_status(worker_pool_t *pool, int *running, int *queued); diff --git a/src/knot/worker/queue.c b/src/knot/worker/queue.c new file mode 100644 index 0000000..c5c4cf7 --- /dev/null +++ b/src/knot/worker/queue.c @@ -0,0 +1,67 @@ +/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "knot/worker/queue.h" +#include "contrib/mempattern.h" + +void worker_queue_init(worker_queue_t *queue) +{ + if (!queue) { + return; + } + + memset(queue, 0, sizeof(worker_queue_t)); + + init_list(&queue->list); + mm_ctx_init(&queue->mm_ctx); +} + +void worker_queue_deinit(worker_queue_t *queue) +{ + ptrlist_free(&queue->list, &queue->mm_ctx); +} + +void worker_queue_enqueue(worker_queue_t *queue, task_t *task) +{ + if (!queue || !task) { + return; + } + + ptrlist_add(&queue->list, task, &queue->mm_ctx); +} + +task_t *worker_queue_dequeue(worker_queue_t *queue) +{ + if (!queue) { + return NULL; + } + + task_t *task = NULL; + + if (!EMPTY_LIST(queue->list)) { + ptrnode_t *node = HEAD(queue->list); + task = (void *)node->d; + rem_node(&node->n); + queue->mm_ctx.free(&node->n); + } + + return task; +} + +size_t worker_queue_length(worker_queue_t *queue) +{ + return queue ? list_size(&queue->list) : 0; +} diff --git a/src/knot/worker/queue.h b/src/knot/worker/queue.h new file mode 100644 index 0000000..2507919 --- /dev/null +++ b/src/knot/worker/queue.h @@ -0,0 +1,65 @@ +/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include "contrib/ucw/lists.h" + +struct task; +typedef void (*task_cb)(struct task *); + +/*! + * \brief Task executable by a worker. + */ +typedef struct task { + void *ctx; + task_cb run; +} task_t; + +/*! + * \brief Worker queue. + */ +typedef struct worker_queue { + knot_mm_t mm_ctx; + list_t list; +} worker_queue_t; + +/*! + * \brief Initialize worker queue. + */ +void worker_queue_init(worker_queue_t *queue); + +/*! + * \brief Deinitialize worker queue. + */ +void worker_queue_deinit(worker_queue_t *queue); + +/*! + * \brief Insert new item into the queue. + */ +void worker_queue_enqueue(worker_queue_t *queue, task_t *task); + +/*! + * \brief Remove item from the queue. + * + * \return Task or NULL if the queue is empty. + */ +task_t *worker_queue_dequeue(worker_queue_t *queue); + +/*! + * \brief Return number of tasks in worker queue. + */ +size_t worker_queue_length(worker_queue_t *queue); diff --git a/src/knot/zone/contents.c b/src/knot/zone/contents.c new file mode 100644 index 0000000..e99cb48 --- /dev/null +++ b/src/knot/zone/contents.c @@ -0,0 +1,1197 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> + +#include "libdnssec/error.h" +#include "knot/zone/contents.h" +#include "knot/common/log.h" +#include "knot/dnssec/zone-nsec.h" +#include "libknot/libknot.h" +#include "contrib/qp-trie/trie.h" +#include "contrib/macros.h" + +typedef struct { + zone_contents_apply_cb_t func; + void *data; +} zone_tree_func_t; + +typedef struct { + zone_node_t *first_node; + zone_contents_t *zone; + zone_node_t *previous_node; +} zone_adjust_arg_t; + +static int tree_apply_cb(zone_node_t **node, void *data) +{ + if (node == NULL || data == NULL) { + return KNOT_EINVAL; + } + + zone_tree_func_t *f = (zone_tree_func_t *)data; + return f->func(*node, f->data); +} + +/*! + * \brief Checks if the given node can be inserted into the given zone. + * + * Checks if both the arguments are non-NULL and if the owner of the node + * belongs to the zone (i.e. is a subdomain of the zone apex). + * + * \param zone Zone to which the node is going to be inserted. + * \param node Node to check. + * + * \retval KNOT_EOK if both arguments are non-NULL and the node belongs to the + * zone. + * \retval KNOT_EINVAL if either of the arguments is NULL. + * \retval KNOT_EOUTOFZONE if the node does not belong to the zone. + */ +static int check_node(const zone_contents_t *contents, const zone_node_t *node) +{ + assert(contents); + assert(contents->apex != NULL); + assert(node); + + if (knot_dname_in_bailiwick(node->owner, contents->apex->owner) <= 0) { + return KNOT_EOUTOFZONE; + } + + return KNOT_EOK; +} + +/*! + * \brief Destroys all RRSets in a node. + * + * This function is designed to be used in the tree-iterating functions. + * + * \param node Node to destroy RRSets from. + * \param data Unused parameter. + */ +static int destroy_node_rrsets_from_tree(zone_node_t **node, void *data) +{ + assert(node); + UNUSED(data); + + if (*node != NULL) { + node_free_rrsets(*node, NULL); + node_free(*node, NULL); + } + + return KNOT_EOK; +} + +static int create_nsec3_name(uint8_t *out, size_t out_size, + const zone_contents_t *zone, + const knot_dname_t *name) +{ + assert(out); + assert(zone); + assert(name); + + if (!knot_is_nsec3_enabled(zone)) { + return KNOT_ENSEC3PAR; + } + + return knot_create_nsec3_owner(out, out_size, name, zone->apex->owner, + &zone->nsec3_params); +} + +/*! \brief Link pointers to additional nodes for this RRSet. */ +static int discover_additionals(const knot_dname_t *owner, struct rr_data *rr_data, + zone_contents_t *zone) +{ + assert(rr_data != NULL); + + /* Drop possible previous additional nodes. */ + additional_clear(rr_data->additional); + rr_data->additional = NULL; + + const knot_rdataset_t *rrs = &rr_data->rrs; + uint16_t rdcount = rrs->count; + + uint16_t mandatory_count = 0; + uint16_t others_count = 0; + glue_t mandatory[rdcount]; + glue_t others[rdcount]; + + /* Scan new additional nodes. */ + for (uint16_t i = 0; i < rdcount; i++) { + knot_rdata_t *rdata = knot_rdataset_at(rrs, i); + const knot_dname_t *dname = knot_rdata_name(rdata, rr_data->type); + const zone_node_t *node = NULL, *encloser = NULL, *prev = NULL; + + /* Try to find node for the dname in the RDATA. */ + zone_contents_find_dname(zone, dname, &node, &encloser, &prev); + if (node == NULL && encloser != NULL + && (encloser->flags & NODE_FLAGS_WILDCARD_CHILD)) { + /* Find wildcard child in the zone. */ + node = zone_contents_find_wildcard_child(zone, encloser); + assert(node != NULL); + } + + if (node == NULL) { + continue; + } + + glue_t *glue; + if ((node->flags & (NODE_FLAGS_DELEG | NODE_FLAGS_NONAUTH)) && + rr_data->type == KNOT_RRTYPE_NS && + knot_dname_in_bailiwick(node->owner, owner) >= 0) { + glue = &mandatory[mandatory_count++]; + glue->optional = false; + } else { + glue = &others[others_count++]; + glue->optional = true; + } + glue->node = node; + glue->ns_pos = i; + } + + /* Store sorted additionals by the type, mandatory first. */ + size_t total_count = mandatory_count + others_count; + if (total_count > 0) { + rr_data->additional = malloc(sizeof(additional_t)); + if (rr_data->additional == NULL) { + return KNOT_ENOMEM; + } + rr_data->additional->count = total_count; + + size_t size = total_count * sizeof(glue_t); + rr_data->additional->glues = malloc(size); + if (rr_data->additional->glues == NULL) { + free(rr_data->additional); + return KNOT_ENOMEM; + } + + size_t mandatory_size = mandatory_count * sizeof(glue_t); + memcpy(rr_data->additional->glues, mandatory, mandatory_size); + memcpy(rr_data->additional->glues + mandatory_count, others, + size - mandatory_size); + } + + return KNOT_EOK; +} + +static int adjust_pointers(zone_node_t **tnode, void *data) +{ + assert(tnode != NULL); + assert(data != NULL); + + zone_adjust_arg_t *args = (zone_adjust_arg_t *)data; + zone_node_t *node = *tnode; + + // remember first node + if (args->first_node == NULL) { + args->first_node = node; + } + + // check if this node is not a wildcard child of its parent + if (knot_dname_is_wildcard(node->owner)) { + assert(node->parent != NULL); + node->parent->flags |= NODE_FLAGS_WILDCARD_CHILD; + } + + // set flags (delegation point, non-authoritative) + if (node->parent && + (node->parent->flags & NODE_FLAGS_DELEG || + node->parent->flags & NODE_FLAGS_NONAUTH)) { + node->flags |= NODE_FLAGS_NONAUTH; + } else if (node_rrtype_exists(node, KNOT_RRTYPE_NS) && node != args->zone->apex) { + node->flags |= NODE_FLAGS_DELEG; + } else { + // Default. + node->flags = NODE_FLAGS_AUTH; + } + + // set pointer to previous node + node->prev = args->previous_node; + + // update remembered previous pointer only if authoritative + if (!(node->flags & NODE_FLAGS_NONAUTH) && node->rrset_count > 0) { + args->previous_node = node; + } + + return KNOT_EOK; +} + +static int adjust_nsec3_pointers(zone_node_t **tnode, void *data) +{ + assert(data != NULL); + assert(tnode != NULL); + + zone_adjust_arg_t *args = (zone_adjust_arg_t *)data; + zone_node_t *node = *tnode; + const zone_node_t *ignored; + + // Connect to NSEC3 node (only if NSEC3 tree is not empty) + node->nsec3_wildcard_prev = NULL; + uint8_t nsec3_name[KNOT_DNAME_MAXLEN]; + int ret = create_nsec3_name(nsec3_name, sizeof(nsec3_name), args->zone, + node->owner); + if (ret == KNOT_EOK) { + node->nsec3_node = zone_tree_get(args->zone->nsec3_nodes, nsec3_name); + + // Connect to NSEC3 node proving nonexistence of wildcard. + size_t wildcard_size = knot_dname_size(node->owner) + 2; + if (wildcard_size <= KNOT_DNAME_MAXLEN) { + assert(wildcard_size > 2); + knot_dname_t wildcard[wildcard_size]; + memcpy(wildcard, "\x01""*", 2); + memcpy(wildcard + 2, node->owner, wildcard_size - 2); + ret = zone_contents_find_nsec3_for_name(args->zone, wildcard, &ignored, + (const zone_node_t **)&node->nsec3_wildcard_prev); + if (ret == ZONE_NAME_FOUND) { + node->nsec3_wildcard_prev = NULL; + ret = KNOT_EOK; + } + } + } else if (ret == KNOT_ENSEC3PAR) { + node->nsec3_node = NULL; + ret = KNOT_EOK; + } + + return ret; +} + +static int measure_size(zone_node_t *node, void *data){ + + size_t *size = data; + int rrset_count = node->rrset_count; + for (int i = 0; i < rrset_count; i++) { + knot_rrset_t rrset = node_rrset_at(node, i); + *size += knot_rrset_size(&rrset); + } + return KNOT_EOK; +} + +static int measure_max_ttl(zone_node_t *node, void *data){ + + uint32_t *max = data; + int rrset_count = node->rrset_count; + for (int i = 0; i < rrset_count; i++) { + *max = MAX(*max, node->rrs[i].ttl); + } + return KNOT_EOK; +} + +static bool nsec3_params_match(const knot_rdataset_t *rrs, + const dnssec_nsec3_params_t *params, + size_t rdata_pos) +{ + assert(rrs != NULL); + assert(params != NULL); + + knot_rdata_t *rdata = knot_rdataset_at(rrs, rdata_pos); + + return (knot_nsec3_alg(rdata) == params->algorithm + && knot_nsec3_iters(rdata) == params->iterations + && knot_nsec3_salt_len(rdata) == params->salt.size + && memcmp(knot_nsec3_salt(rdata), params->salt.data, + params->salt.size) == 0); +} + +/*! + * \brief Adjust normal (non NSEC3) node. + * + * Set: + * - pointer to wildcard childs in parent nodes if applicable + * - flags (delegation point, non-authoritative) + * - pointer to previous node + * - parent pointers + * + * \param tnode Zone node to adjust. + * \param data Adjusting parameters (zone_adjust_arg_t *). + */ +static int adjust_normal_node(zone_node_t **tnode, void *data) +{ + assert(tnode != NULL && *tnode); + assert(data != NULL); + + // Do cheap operations first + int ret = adjust_pointers(tnode, data); + if (ret != KNOT_EOK) { + return ret; + } + + zone_adjust_arg_t *arg = data; + measure_size(*tnode, &arg->zone->size); + measure_max_ttl(*tnode, &arg->zone->max_ttl); + + // Connect nodes to their NSEC3 nodes + return adjust_nsec3_pointers(tnode, data); +} + +/*! + * \brief Adjust NSEC3 node. + * + * Set: + * - pointer to previous node + * - pointer to node stored in owner dname + * + * \param tnode Zone node to adjust. + * \param data Adjusting parameters (zone_adjust_arg_t *). + */ +static int adjust_nsec3_node(zone_node_t **tnode, void *data) +{ + assert(data != NULL); + assert(tnode != NULL); + + zone_adjust_arg_t *args = (zone_adjust_arg_t *)data; + zone_node_t *node = *tnode; + + // remember first node + if (args->first_node == NULL) { + args->first_node = node; + } + + // set previous node + node->prev = args->previous_node; + args->previous_node = node; + + measure_size(*tnode, &args->zone->size); + measure_max_ttl(*tnode, &args->zone->max_ttl); + + // check if this node belongs to correct chain + const knot_rdataset_t *nsec3_rrs = node_rdataset(node, KNOT_RRTYPE_NSEC3); + for (uint16_t i = 0; nsec3_rrs != NULL && i < nsec3_rrs->count; i++) { + if (nsec3_params_match(nsec3_rrs, &args->zone->nsec3_params, i)) { + node->flags |= NODE_FLAGS_IN_NSEC3_CHAIN; + } + } + + return KNOT_EOK; +} + +/*! \brief Discover additional records for affected nodes. */ +static int adjust_additional(zone_node_t **tnode, void *data) +{ + assert(data != NULL); + assert(tnode != NULL); + + zone_adjust_arg_t *args = (zone_adjust_arg_t *)data; + zone_node_t *node = *tnode; + + /* Lookup additional records for specific nodes. */ + for(uint16_t i = 0; i < node->rrset_count; ++i) { + struct rr_data *rr_data = &node->rrs[i]; + if (knot_rrtype_additional_needed(rr_data->type)) { + int ret = discover_additionals(node->owner, rr_data, args->zone); + if (ret != KNOT_EOK) { + return ret; + } + } + } + + return KNOT_EOK; +} + +/*! + * \brief Tries to find the given domain name in the zone tree. + * + * \param zone Zone to search in. + * \param name Domain name to find. + * \param node Found node. + * \param previous Previous node in canonical order (i.e. the one directly + * preceding \a name in canonical order, regardless if the name + * is in the zone or not). + * + * \retval true if the domain name was found. In such case \a node holds the + * zone node with \a name as its owner. \a previous is set + * properly. + * \retval false if the domain name was not found. \a node may hold any (or none) + * node. \a previous is set properly. + */ +static bool find_in_tree(zone_tree_t *tree, const knot_dname_t *name, + zone_node_t **node, zone_node_t **previous) +{ + assert(tree != NULL); + assert(name != NULL); + assert(node != NULL); + assert(previous != NULL); + + zone_node_t *found = NULL, *prev = NULL; + + int match = zone_tree_get_less_or_equal(tree, name, &found, &prev); + if (match < 0) { + assert(0); + return false; + } + + *node = found; + *previous = prev; + + return match > 0; +} + +zone_contents_t *zone_contents_new(const knot_dname_t *apex_name) +{ + if (apex_name == NULL) { + return NULL; + } + + zone_contents_t *contents = malloc(sizeof(zone_contents_t)); + if (contents == NULL) { + return NULL; + } + + memset(contents, 0, sizeof(zone_contents_t)); + contents->apex = node_new(apex_name, NULL); + if (contents->apex == NULL) { + goto cleanup; + } + + contents->nodes = zone_tree_create(); + if (contents->nodes == NULL) { + goto cleanup; + } + + if (zone_tree_insert(contents->nodes, contents->apex) != KNOT_EOK) { + goto cleanup; + } + + return contents; + +cleanup: + free(contents->nodes); + free(contents->nsec3_nodes); + free(contents); + return NULL; +} + +static zone_node_t *get_node(const zone_contents_t *zone, const knot_dname_t *name) +{ + assert(zone); + assert(name); + + return zone_tree_get(zone->nodes, name); +} + +static int add_node(zone_contents_t *zone, zone_node_t *node, bool create_parents) +{ + if (zone == NULL || node == NULL) { + return KNOT_EINVAL; + } + + int ret = check_node(zone, node); + if (ret != KNOT_EOK) { + return ret; + } + + ret = zone_tree_insert(zone->nodes, node); + if (ret != KNOT_EOK) { + return ret; + } + + if (!create_parents) { + return KNOT_EOK; + } + + /* No parents for root domain. */ + if (*node->owner == '\0') { + return KNOT_EOK; + } + + zone_node_t *next_node = NULL; + const uint8_t *parent = knot_wire_next_label(node->owner, NULL); + + if (knot_dname_is_equal(zone->apex->owner, parent)) { + node_set_parent(node, zone->apex); + + // check if the node is not wildcard child of the parent + if (knot_dname_is_wildcard(node->owner)) { + zone->apex->flags |= NODE_FLAGS_WILDCARD_CHILD; + } + } else { + while (parent != NULL && !(next_node = get_node(zone, parent))) { + + /* Create a new node. */ + next_node = node_new(parent, NULL); + if (next_node == NULL) { + return KNOT_ENOMEM; + } + + /* Insert node to a tree. */ + ret = zone_tree_insert(zone->nodes, next_node); + if (ret != KNOT_EOK) { + node_free(next_node, NULL); + return ret; + } + + /* Update node pointers. */ + node_set_parent(node, next_node); + if (knot_dname_is_wildcard(node->owner)) { + next_node->flags |= NODE_FLAGS_WILDCARD_CHILD; + } + + node = next_node; + parent = knot_wire_next_label(parent, NULL); + } + + // set the found parent (in the zone) as the parent of the last + // inserted node + assert(node->parent == NULL); + node_set_parent(node, next_node); + } + + return KNOT_EOK; +} + +static int add_nsec3_node(zone_contents_t *zone, zone_node_t *node) +{ + if (zone == NULL || node == NULL) { + return KNOT_EINVAL; + } + + int ret = check_node(zone, node); + if (ret != KNOT_EOK) { + return ret; + } + + /* Create NSEC3 tree if not exists. */ + if (zone->nsec3_nodes == NULL) { + zone->nsec3_nodes = zone_tree_create(); + if (zone->nsec3_nodes == NULL) { + return KNOT_ENOMEM; + } + } + + // how to know if this is successful?? + ret = zone_tree_insert(zone->nsec3_nodes, node); + if (ret != KNOT_EOK) { + return ret; + } + + // no parents to be created, the only parent is the zone apex + // set the apex as the parent of the node + node_set_parent(node, zone->apex); + + // cannot be wildcard child, so nothing to be done + + return KNOT_EOK; +} + +static zone_node_t *get_nsec3_node(const zone_contents_t *zone, + const knot_dname_t *name) +{ + assert(zone); + assert(name); + + return zone_tree_get(zone->nsec3_nodes, name); +} + +static int insert_rr(zone_contents_t *z, const knot_rrset_t *rr, + zone_node_t **n, bool nsec3) +{ + if (knot_rrset_empty(rr)) { + return KNOT_EINVAL; + } + + // check if the RRSet belongs to the zone + if (knot_dname_in_bailiwick(rr->owner, z->apex->owner) < 0) { + return KNOT_EOUTOFZONE; + } + + if (*n == NULL) { + *n = nsec3 ? get_nsec3_node(z, rr->owner) : get_node(z, rr->owner); + if (*n == NULL) { + // Create new, insert + *n = node_new(rr->owner, NULL); + if (*n == NULL) { + return KNOT_ENOMEM; + } + int ret = nsec3 ? add_nsec3_node(z, *n) : add_node(z, *n, true); + if (ret != KNOT_EOK) { + node_free(*n, NULL); + *n = NULL; + } + } + } + + return node_add_rrset(*n, rr, NULL); +} + +static int remove_rr(zone_contents_t *z, const knot_rrset_t *rr, + zone_node_t **n, bool nsec3) +{ + if (knot_rrset_empty(rr)) { + return KNOT_EINVAL; + } + + // check if the RRSet belongs to the zone + if (knot_dname_in_bailiwick(rr->owner, z->apex->owner) < 0) { + return KNOT_EOUTOFZONE; + } + + zone_node_t *node; + if (*n == NULL) { + node = nsec3 ? get_nsec3_node(z, rr->owner) : get_node(z, rr->owner); + if (node == NULL) { + return KNOT_ENONODE; + } + } else { + node = *n; + } + + knot_rdataset_t *node_rrs = node_rdataset(node, rr->type); + // Subtract changeset RRS from node RRS. + int ret = knot_rdataset_subtract(node_rrs, &rr->rrs, NULL); + if (ret != KNOT_EOK) { + return ret; + } + + if (node_rrs->count == 0) { + // RRSet is empty now, remove it from node, all data freed. + node_remove_rdataset(node, rr->type); + // If node is empty now, delete it from zone tree. + if (node->rrset_count == 0 && node != z->apex) { + zone_tree_delete_empty(nsec3 ? z->nsec3_nodes : z->nodes, node); + } + } + + *n = node; + return KNOT_EOK; +} + +static int recreate_normal_tree(const zone_contents_t *z, zone_contents_t *out) +{ + out->nodes = trie_create(NULL); + if (out->nodes == NULL) { + return KNOT_ENOMEM; + } + + // Insert APEX first. + zone_node_t *apex_cpy = node_shallow_copy(z->apex, NULL); + if (apex_cpy == NULL) { + return KNOT_ENOMEM; + } + + // Normal additions need apex ... so we need to insert directly. + int ret = zone_tree_insert(out->nodes, apex_cpy); + if (ret != KNOT_EOK) { + node_free(apex_cpy, NULL); + return ret; + } + + out->apex = apex_cpy; + + trie_it_t *itt = trie_it_begin(z->nodes); + if (itt == NULL) { + return KNOT_ENOMEM; + } + + while (!trie_it_finished(itt)) { + const zone_node_t *to_cpy = (zone_node_t *)*trie_it_val(itt); + if (to_cpy == z->apex) { + // Inserted already. + trie_it_next(itt); + continue; + } + zone_node_t *to_add = node_shallow_copy(to_cpy, NULL); + if (to_add == NULL) { + trie_it_free(itt); + return KNOT_ENOMEM; + } + + int ret = add_node(out, to_add, true); + if (ret != KNOT_EOK) { + node_free(to_add, NULL); + trie_it_free(itt); + return ret; + } + trie_it_next(itt); + } + + trie_it_free(itt); + + return KNOT_EOK; +} + +static int recreate_nsec3_tree(const zone_contents_t *z, zone_contents_t *out) +{ + out->nsec3_nodes = trie_create(NULL); + if (out->nsec3_nodes == NULL) { + return KNOT_ENOMEM; + } + + trie_it_t *itt = trie_it_begin(z->nsec3_nodes); + if (itt == NULL) { + return KNOT_ENOMEM; + } + while (!trie_it_finished(itt)) { + const zone_node_t *to_cpy = (zone_node_t *)*trie_it_val(itt); + zone_node_t *to_add = node_shallow_copy(to_cpy, NULL); + if (to_add == NULL) { + trie_it_free(itt); + return KNOT_ENOMEM; + } + + int ret = add_nsec3_node(out, to_add); + if (ret != KNOT_EOK) { + trie_it_free(itt); + node_free(to_add, NULL); + return ret; + } + + trie_it_next(itt); + } + + trie_it_free(itt); + + return KNOT_EOK; +} + +// Public API + +int zone_contents_add_rr(zone_contents_t *z, const knot_rrset_t *rr, + zone_node_t **n) +{ + if (z == NULL || rr == NULL || n == NULL) { + return KNOT_EINVAL; + } + + return insert_rr(z, rr, n, knot_rrset_is_nsec3rel(rr)); +} + +int zone_contents_remove_rr(zone_contents_t *z, const knot_rrset_t *rr, + zone_node_t **n) +{ + if (z == NULL || rr == NULL || n == NULL) { + return KNOT_EINVAL; + } + + return remove_rr(z, rr, n, knot_rrset_is_nsec3rel(rr)); +} + +zone_node_t *zone_contents_get_node_for_rr(zone_contents_t *zone, const knot_rrset_t *rrset) +{ + if (zone == NULL || rrset == NULL) { + return NULL; + } + + const bool nsec3 = knot_rrset_is_nsec3rel(rrset); + zone_node_t *node = nsec3 ? get_nsec3_node(zone, rrset->owner) : + get_node(zone, rrset->owner); + if (node == NULL) { + node = node_new(rrset->owner, NULL); + int ret = nsec3 ? add_nsec3_node(zone, node) : add_node(zone, node, true); + if (ret != KNOT_EOK) { + node_free(node, NULL); + return NULL; + } + + return node; + } else { + return node; + } +} + +const zone_node_t *zone_contents_find_node(const zone_contents_t *zone, const knot_dname_t *name) +{ + if (zone == NULL || name == NULL) { + return NULL; + } + + return get_node(zone, name); +} + +zone_node_t *zone_contents_find_node_for_rr(zone_contents_t *contents, const knot_rrset_t *rrset) +{ + if (contents == NULL || rrset == NULL) { + return NULL; + } + + const bool nsec3 = knot_rrset_is_nsec3rel(rrset); + return nsec3 ? get_nsec3_node(contents, rrset->owner) : + get_node(contents, rrset->owner); +} + +int zone_contents_find_dname(const zone_contents_t *zone, + const knot_dname_t *name, + const zone_node_t **match, + const zone_node_t **closest, + const zone_node_t **previous) +{ + if (!zone || !name || !match || !closest || !previous) { + return KNOT_EINVAL; + } + + if (knot_dname_in_bailiwick(name, zone->apex->owner) < 0) { + return KNOT_EOUTOFZONE; + } + + zone_node_t *node = NULL; + zone_node_t *prev = NULL; + + int found = zone_tree_get_less_or_equal(zone->nodes, name, &node, &prev); + if (found < 0) { + // error + return found; + } else if (found == 1) { + // exact match + + assert(node && prev); + + *match = node; + *closest = node; + *previous = prev; + + return ZONE_NAME_FOUND; + } else { + // closest match + + assert(!node && prev); + + node = prev; + size_t matched_labels = knot_dname_matched_labels(node->owner, name); + while (matched_labels < knot_dname_labels(node->owner, NULL)) { + node = node->parent; + assert(node); + } + + *match = NULL; + *closest = node; + *previous = prev; + + return ZONE_NAME_NOT_FOUND; + } +} + +const zone_node_t *zone_contents_find_nsec3_node(const zone_contents_t *zone, + const knot_dname_t *name) +{ + if (zone == NULL || name == NULL) { + return NULL; + } + + return get_nsec3_node(zone, name); +} + +int zone_contents_find_nsec3_for_name(const zone_contents_t *zone, + const knot_dname_t *name, + const zone_node_t **nsec3_node, + const zone_node_t **nsec3_previous) +{ + if (zone == NULL || name == NULL || nsec3_node == NULL || + nsec3_previous == NULL) { + return KNOT_EINVAL; + } + + // check if the NSEC3 tree is not empty + if (zone_tree_is_empty(zone->nsec3_nodes)) { + return KNOT_ENSEC3CHAIN; + } + + uint8_t nsec3_name[KNOT_DNAME_MAXLEN]; + int ret = create_nsec3_name(nsec3_name, sizeof(nsec3_name), zone, name); + if (ret != KNOT_EOK) { + return ret; + } + + zone_node_t *found = NULL, *prev = NULL; + bool match = find_in_tree(zone->nsec3_nodes, nsec3_name, &found, &prev); + + *nsec3_node = found; + + if (prev == NULL) { + // either the returned node is the root of the tree, or it is + // the leftmost node in the tree; in both cases node was found + // set the previous node of the found node + assert(match); + assert(*nsec3_node != NULL); + *nsec3_previous = (*nsec3_node)->prev; + } else { + *nsec3_previous = prev; + } + + // The previous may be from wrong NSEC3 chain. Search for previous from the right chain. + const zone_node_t *original_prev = *nsec3_previous; + while (!((*nsec3_previous)->flags & NODE_FLAGS_IN_NSEC3_CHAIN)) { + *nsec3_previous = (*nsec3_previous)->prev; + if (*nsec3_previous == original_prev || *nsec3_previous == NULL) { + // cycle + *nsec3_previous = NULL; + break; + } + } + + return (match ? ZONE_NAME_FOUND : ZONE_NAME_NOT_FOUND); +} + +const zone_node_t *zone_contents_find_wildcard_child(const zone_contents_t *contents, + const zone_node_t *parent) +{ + if (contents == NULL || parent == NULL || parent->owner == NULL) { + return NULL; + } + + knot_dname_t wildcard[KNOT_DNAME_MAXLEN] = { 0x01, '*' }; + knot_dname_to_wire(wildcard + 2, parent->owner, KNOT_DNAME_MAXLEN - 2); + + return zone_contents_find_node(contents, wildcard); +} + +static int adjust_nodes(zone_tree_t *nodes, zone_adjust_arg_t *adjust_arg, + zone_tree_apply_cb_t callback) +{ + assert(adjust_arg); + assert(callback); + + if (zone_tree_is_empty(nodes)) { + return KNOT_EOK; + } + + adjust_arg->first_node = NULL; + adjust_arg->previous_node = NULL; + + int ret = zone_tree_apply(nodes, callback, adjust_arg); + + if (adjust_arg->first_node) { + adjust_arg->first_node->prev = adjust_arg->previous_node; + } + + return ret; +} + +static int load_nsec3param(zone_contents_t *contents) +{ + assert(contents); + assert(contents->apex); + + const knot_rdataset_t *rrs = NULL; + rrs = node_rdataset(contents->apex, KNOT_RRTYPE_NSEC3PARAM); + if (rrs == NULL) { + dnssec_nsec3_params_free(&contents->nsec3_params); + return KNOT_EOK; + } + + if (rrs->count < 1) { + return KNOT_EINVAL; + } + + dnssec_binary_t rdata = { + .size = rrs->rdata->len, + .data = rrs->rdata->data, + }; + + dnssec_nsec3_params_t new_params = { 0 }; + int r = dnssec_nsec3_params_from_rdata(&new_params, &rdata); + if (r != DNSSEC_EOK) { + return KNOT_EMALF; + } + + dnssec_nsec3_params_free(&contents->nsec3_params); + contents->nsec3_params = new_params; + return KNOT_EOK; +} + +static int contents_adjust(zone_contents_t *contents, bool normal) +{ + if (contents == NULL || contents->apex == NULL) { + return KNOT_EINVAL; + } + + int ret = load_nsec3param(contents); + if (ret != KNOT_EOK) { + log_zone_error(contents->apex->owner, + "failed to load NSEC3 parameters (%s)", + knot_strerror(ret)); + return ret; + } + + zone_adjust_arg_t arg = { + .zone = contents + }; + + contents->size = 0; + contents->dnssec = node_rrtype_is_signed(contents->apex, KNOT_RRTYPE_SOA); + + // NSEC3 nodes must be adjusted first, because we already need the NSEC3 chain + // to be closed before we adjust NSEC3 pointers in adjust_normal_node + ret = adjust_nodes(contents->nsec3_nodes, &arg, adjust_nsec3_node); + if (ret != KNOT_EOK) { + return ret; + } + + ret = adjust_nodes(contents->nodes, &arg, + normal ? adjust_normal_node : adjust_pointers); + if (ret != KNOT_EOK) { + return ret; + } + + return adjust_nodes(contents->nodes, &arg, adjust_additional); +} + +int zone_contents_adjust_pointers(zone_contents_t *contents) +{ + return contents_adjust(contents, false); +} + +int zone_contents_adjust_full(zone_contents_t *contents) +{ + return contents_adjust(contents, true); +} + +int zone_contents_apply(zone_contents_t *contents, + zone_contents_apply_cb_t function, void *data) +{ + if (contents == NULL) { + return KNOT_EINVAL; + } + + zone_tree_func_t f = { + .func = function, + .data = data + }; + + return zone_tree_apply(contents->nodes, tree_apply_cb, &f); +} + +int zone_contents_nsec3_apply(zone_contents_t *contents, + zone_contents_apply_cb_t function, void *data) +{ + if (contents == NULL) { + return KNOT_EINVAL; + } + + zone_tree_func_t f = { + .func = function, + .data = data + }; + + return zone_tree_apply(contents->nsec3_nodes, tree_apply_cb, &f); +} + +int zone_contents_shallow_copy(const zone_contents_t *from, zone_contents_t **to) +{ + if (from == NULL || to == NULL) { + return KNOT_EINVAL; + } + + /* Copy to same destination as source. */ + if (from == *to) { + return KNOT_EINVAL; + } + + zone_contents_t *contents = calloc(1, sizeof(zone_contents_t)); + if (contents == NULL) { + return KNOT_ENOMEM; + } + + int ret = recreate_normal_tree(from, contents); + if (ret != KNOT_EOK) { + zone_tree_free(&contents->nodes); + free(contents); + return ret; + } + + if (from->nsec3_nodes) { + ret = recreate_nsec3_tree(from, contents); + if (ret != KNOT_EOK) { + zone_tree_free(&contents->nodes); + zone_tree_free(&contents->nsec3_nodes); + free(contents); + return ret; + } + } else { + contents->nsec3_nodes = NULL; + } + + *to = contents; + return KNOT_EOK; +} + +void zone_contents_free(zone_contents_t *contents) +{ + if (contents == NULL) { + return; + } + + // free the zone tree, but only the structure + zone_tree_free(&contents->nodes); + zone_tree_free(&contents->nsec3_nodes); + + dnssec_nsec3_params_free(&contents->nsec3_params); + + free(contents); +} + +void zone_contents_deep_free(zone_contents_t *contents) +{ + if (contents == NULL) { + return; + } + + if (contents != NULL) { + // Delete NSEC3 tree. + (void)zone_tree_apply(contents->nsec3_nodes, + destroy_node_rrsets_from_tree, NULL); + + // Delete the normal tree. + (void)zone_tree_apply(contents->nodes, + destroy_node_rrsets_from_tree, NULL); + } + + zone_contents_free(contents); +} + +uint32_t zone_contents_serial(const zone_contents_t *zone) +{ + if (zone == NULL) { + return 0; + } + + const knot_rdataset_t *soa = node_rdataset(zone->apex, KNOT_RRTYPE_SOA); + if (soa == NULL) { + return 0; + } + + return knot_soa_serial(soa->rdata); +} + +void zone_contents_set_soa_serial(zone_contents_t *zone, uint32_t new_serial) +{ + knot_rdataset_t *soa; + if (zone != NULL && (soa = node_rdataset(zone->apex, KNOT_RRTYPE_SOA)) != NULL) { + knot_soa_serial_set(soa->rdata, new_serial); + } +} + +bool zone_contents_is_empty(const zone_contents_t *zone) +{ + if (zone == NULL) { + return true; + } + + bool apex_empty = (zone->apex == NULL || zone->apex->rrset_count == 0); + bool no_non_apex = (zone_tree_count(zone->nodes) <= (zone->apex != NULL ? 1 : 0)); + bool no_nsec3 = zone_tree_is_empty(zone->nsec3_nodes); + + return (apex_empty && no_non_apex && no_nsec3); +} + +size_t zone_contents_measure_size(zone_contents_t *zone) +{ + zone->size = 0; + zone_contents_apply(zone, measure_size, &zone->size); + return zone->size; +} + +uint32_t zone_contents_max_ttl(zone_contents_t *zone) +{ + zone->max_ttl = 0; + zone_contents_apply(zone, measure_max_ttl, &zone->size); + return zone->max_ttl; +} diff --git a/src/knot/zone/contents.h b/src/knot/zone/contents.h new file mode 100644 index 0000000..9ccf3c0 --- /dev/null +++ b/src/knot/zone/contents.h @@ -0,0 +1,288 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \file + * + * Zone contents structure and API for manipulating it. + * + * \addtogroup zone + * @{ + */ + +#pragma once + +#include "libdnssec/nsec.h" +#include "libknot/rrtype/nsec3param.h" +#include "knot/zone/node.h" +#include "knot/zone/zone-tree.h" + +enum zone_contents_find_dname_result { + ZONE_NAME_NOT_FOUND = 0, + ZONE_NAME_FOUND = 1 +}; + +typedef struct zone_contents { + zone_node_t *apex; /*!< Apex node of the zone (holding SOA) */ + + zone_tree_t *nodes; + zone_tree_t *nsec3_nodes; + + dnssec_nsec3_params_t nsec3_params; + size_t size; + uint32_t max_ttl; + bool dnssec; +} zone_contents_t; + +/*! + * \brief Signature of callback for zone contents apply functions. + */ +typedef int (*zone_contents_apply_cb_t)(zone_node_t *node, void *data); + +/*! + * \brief Allocate and create new zone contents. + * + * \param apex_name Name of the root node. + * + * \return New contents or NULL on error. + */ +zone_contents_t *zone_contents_new(const knot_dname_t *apex_name); + +/*! + * \brief Add an RR to contents. + * + * \param z Contents to add to. + * \param rr The RR to add. + * \param n Node to which the RR has been added to on success, unchanged otherwise. + * + * \return KNOT_E* + */ +int zone_contents_add_rr(zone_contents_t *z, const knot_rrset_t *rr, zone_node_t **n); + +/*! + * \brief Remove an RR from contents. + * + * \param z Contents to remove from. + * \param rr The RR to remove. + * \param n Node from which the RR to be removed from on success, unchanged otherwise. + * + * \return KNOT_E* + */ +int zone_contents_remove_rr(zone_contents_t *z, const knot_rrset_t *rr, zone_node_t **n); + +/*! + * \brief Get the node with this RR (the RR's owner). + * + * \param zone Contents to add to. + * \param rrset The RR to add. + * + * \return The searched node if it exists, a new added empty node or NULL on error. + */ +zone_node_t *zone_contents_get_node_for_rr(zone_contents_t *zone, const knot_rrset_t *rrset); + +/*! + * \brief Tries to find a node with the specified name in the zone. + * + * \param contents Zone where the name should be searched for. + * \param name Name to find. + * + * \return Corresponding node if found, NULL otherwise. + */ +const zone_node_t *zone_contents_find_node(const zone_contents_t *contents, const knot_dname_t *name); + +zone_node_t *zone_contents_find_node_for_rr(zone_contents_t *contents, const knot_rrset_t *rrset); + +/*! + * \brief Tries to find a node by owner in the zone contents. + * + * \param[in] contents Zone to search for the name. + * \param[in] name Domain name to search for. + * \param[out] match Matching node or NULL. + * \param[out] closest Closest matching name in the zone. + * May match \a match if found exactly. + * \param[out] previous Previous domain name in canonical order. + * Always previous, won't match \a match. + * + * \note The encloser and previous mustn't be used directly for DNSSEC proofs. + * These nodes may be empty non-terminals or not authoritative. + * + * \retval ZONE_NAME_FOUND if node with owner \a name was found. + * \retval ZONE_NAME_NOT_FOUND if it was not found. + * \retval KNOT_EINVAL + * \retval KNOT_EOUTOFZONE + */ +int zone_contents_find_dname(const zone_contents_t *contents, + const knot_dname_t *name, + const zone_node_t **match, + const zone_node_t **closest, + const zone_node_t **previous); + +/*! + * \brief Tries to find a node with the specified name among the NSEC3 nodes + * of the zone. + * + * \note This function is identical to zone_contents_get_nsec3_node(), only it + * returns constant reference. + * + * \param contents Zone where the name should be searched for. + * \param name Name to find. + * + * \return Corresponding node if found, NULL otherwise. + */ +const zone_node_t *zone_contents_find_nsec3_node(const zone_contents_t *contents, + const knot_dname_t *name); + +/*! + * \brief Finds NSEC3 node and previous NSEC3 node in canonical order, + * corresponding to the given domain name. + * + * This functions creates a NSEC3 hash of \a name and tries to find NSEC3 node + * with the hashed domain name as owner. + * + * \param[in] contents Zone to search in. + * \param[in] name Domain name to get the corresponding NSEC3 nodes for. + * \param[out] nsec3_node NSEC3 node corresponding to \a name (if found, + * otherwise this may be an arbitrary NSEC3 node). + * \param[out] nsec3_previous The NSEC3 node immediately preceding hashed domain + * name corresponding to \a name in canonical order. + * + * \retval ZONE_NAME_FOUND if the corresponding NSEC3 node was found. + * \retval ZONE_NAME_NOT_FOUND if it was not found. + * \retval KNOT_EINVAL + * \retval KNOT_ENSEC3PAR + * \retval KNOT_ECRYPTO + * \retval KNOT_ERROR + */ +int zone_contents_find_nsec3_for_name(const zone_contents_t *contents, + const knot_dname_t *name, + const zone_node_t **nsec3_node, + const zone_node_t **nsec3_previous); + +const zone_node_t *zone_contents_find_wildcard_child(const zone_contents_t *contents, + const zone_node_t *parent); + +/*! + * \brief Sets parent and previous pointers and node flags. (cheap operation) + * For both normal and NSEC3 tree + * + * \param contents Zone contents to be adjusted. + */ +int zone_contents_adjust_pointers(zone_contents_t *contents); + +/*! + * \brief Sets parent and previous pointers, sets node flags and NSEC3 links. + * This has to be called before the zone can be served. + * + * \param contents Zone contents to be adjusted. + */ +int zone_contents_adjust_full(zone_contents_t *contents); + +/*! + * \brief Applies the given function to each regular node in the zone. + * + * \param contents Nodes of this zone will be used as parameters for the function. + * \param function Function to be applied to each node of the zone. + * \param data Arbitrary data to be passed to the function. + */ +int zone_contents_apply(zone_contents_t *contents, + zone_contents_apply_cb_t function, void *data); + +/*! + * \brief Applies the given function to each NSEC3 node in the zone. + * + * \param contents NSEC3 nodes of this zone will be used as parameters for the + * function. + * \param function Function to be applied to each node of the zone. + * \param data Arbitrary data to be passed to the function. + */ +int zone_contents_nsec3_apply(zone_contents_t *contents, + zone_contents_apply_cb_t function, void *data); + +/*! + * \brief Creates a shallow copy of the zone (no stored data are copied). + * + * This function creates a new zone structure in \a to, creates new trees for + * regular nodes and for NSEC3 nodes, creates new hash table and a new domain + * table. It also fills these structures with the exact same data as the + * original zone is - no copying of stored data is done, just pointers are + * copied. + * + * \param from Original zone. + * \param to Copy of the zone. + * + * \retval KNOT_EOK + * \retval KNOT_EINVAL + * \retval KNOT_ENOMEM + */ +int zone_contents_shallow_copy(const zone_contents_t *from, zone_contents_t **to); + +/*! + * \brief Deallocate directly owned data of zone contents. + * + * \param contents Zone contents to free. + */ +void zone_contents_free(zone_contents_t *contents); + +/*! + * \brief Deallocate node RRSets inside the trees, then call zone_contents_free. + * + * \param contents Zone contents to free. + */ +void zone_contents_deep_free(zone_contents_t *contents); + +/*! + * \brief Fetch zone serial. + * + * \param zone Zone. + * + * \return serial or 0 + */ +uint32_t zone_contents_serial(const zone_contents_t *zone); + +/*! + * \brief Adjust zone serial. + * + * Works only if there is a SOA in given contents. + * + * \param zone Zone. + * \param new_serial New serial to be set. + */ +void zone_contents_set_soa_serial(zone_contents_t *zone, uint32_t new_serial); + +/*! + * \brief Return true if zone is empty. + */ +bool zone_contents_is_empty(const zone_contents_t *zone); + +/*! + * \brief Measure zone contents size. + * + * Size is measured in uncompressed wire format. Measured size is saved into + * zone contents structure. + * \return Measured size + */ +size_t zone_contents_measure_size(zone_contents_t *zone); + +/*! + * \brief Obtain maximal TTL above all the records in zone. + * + * The value is also stored in zone_contents structure. + * + * \param zone Zone in question. + * \return Maximal TTL. + */ +uint32_t zone_contents_max_ttl(zone_contents_t *zone); + +/*! @} */ diff --git a/src/knot/zone/node.c b/src/knot/zone/node.c new file mode 100644 index 0000000..2d593b9 --- /dev/null +++ b/src/knot/zone/node.c @@ -0,0 +1,312 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "knot/zone/node.h" +#include "libknot/libknot.h" +#include "contrib/mempattern.h" + +void additional_clear(additional_t *additional) +{ + if (additional == NULL) { + return; + } + + free(additional->glues); + free(additional); +} + +/*! \brief Clears allocated data in RRSet entry. */ +static void rr_data_clear(struct rr_data *data, knot_mm_t *mm) +{ + knot_rdataset_clear(&data->rrs, mm); + additional_clear(data->additional); +} + +/*! \brief Clears allocated data in RRSet entry. */ +static int rr_data_from(const knot_rrset_t *rrset, struct rr_data *data, knot_mm_t *mm) +{ + int ret = knot_rdataset_copy(&data->rrs, &rrset->rrs, mm); + if (ret != KNOT_EOK) { + return ret; + } + data->ttl = rrset->ttl; + data->type = rrset->type; + data->additional = NULL; + + return KNOT_EOK; +} + +/*! \brief Adds RRSet to node directly. */ +static int add_rrset_no_merge(zone_node_t *node, const knot_rrset_t *rrset, + knot_mm_t *mm) +{ + if (node == NULL) { + return KNOT_EINVAL; + } + + const size_t prev_nlen = node->rrset_count * sizeof(struct rr_data); + const size_t nlen = (node->rrset_count + 1) * sizeof(struct rr_data); + void *p = mm_realloc(mm, node->rrs, nlen, prev_nlen); + if (p == NULL) { + return KNOT_ENOMEM; + } + node->rrs = p; + int ret = rr_data_from(rrset, node->rrs + node->rrset_count, mm); + if (ret != KNOT_EOK) { + return ret; + } + ++node->rrset_count; + + return KNOT_EOK; +} + +/*! \brief Checks if the added RR has the same TTL as the first RR in the node. */ +static bool ttl_changed(struct rr_data *node_data, const knot_rrset_t *rrset) +{ + if (rrset->type == KNOT_RRTYPE_RRSIG || node_data->rrs.count == 0) { + return false; + } + + return rrset->ttl != node_data->ttl; +} + +zone_node_t *node_new(const knot_dname_t *owner, knot_mm_t *mm) +{ + zone_node_t *ret = mm_alloc(mm, sizeof(zone_node_t)); + if (ret == NULL) { + return NULL; + } + memset(ret, 0, sizeof(*ret)); + + if (owner) { + ret->owner = knot_dname_copy(owner, mm); + if (ret->owner == NULL) { + mm_free(mm, ret); + return NULL; + } + } + + // Node is authoritative by default. + ret->flags = NODE_FLAGS_AUTH; + + return ret; +} + +void node_free_rrsets(zone_node_t *node, knot_mm_t *mm) +{ + if (node == NULL) { + return; + } + + for (uint16_t i = 0; i < node->rrset_count; ++i) { + rr_data_clear(&node->rrs[i], mm); + } + + mm_free(mm, node->rrs); + node->rrs = NULL; + node->rrset_count = 0; +} + +void node_free(zone_node_t *node, knot_mm_t *mm) +{ + if (node == NULL) { + return; + } + + knot_dname_free(node->owner, mm); + + if (node->rrs != NULL) { + mm_free(mm, node->rrs); + } + + mm_free(mm, node); +} + +zone_node_t *node_shallow_copy(const zone_node_t *src, knot_mm_t *mm) +{ + if (src == NULL) { + return NULL; + } + + // create new node + zone_node_t *dst = node_new(src->owner, mm); + if (dst == NULL) { + return NULL; + } + + dst->flags = src->flags; + + // copy RRSets + dst->rrset_count = src->rrset_count; + size_t rrlen = sizeof(struct rr_data) * src->rrset_count; + dst->rrs = mm_alloc(mm, rrlen); + if (dst->rrs == NULL) { + node_free(dst, mm); + return NULL; + } + memcpy(dst->rrs, src->rrs, rrlen); + + for (uint16_t i = 0; i < src->rrset_count; ++i) { + // Clear additionals in the copy. + dst->rrs[i].additional = NULL; + } + + return dst; +} + +int node_add_rrset(zone_node_t *node, const knot_rrset_t *rrset, knot_mm_t *mm) +{ + if (node == NULL || rrset == NULL) { + return KNOT_EINVAL; + } + + for (uint16_t i = 0; i < node->rrset_count; ++i) { + if (node->rrs[i].type == rrset->type) { + struct rr_data *node_data = &node->rrs[i]; + const bool ttl_change = ttl_changed(node_data, rrset); + if (ttl_change) { + node_data->ttl = rrset->ttl; + } + + int ret = knot_rdataset_merge(&node_data->rrs, + &rrset->rrs, mm); + if (ret != KNOT_EOK) { + return ret; + } else { + return ttl_change ? KNOT_ETTL : KNOT_EOK; + } + } + } + + // New RRSet (with one RR) + return add_rrset_no_merge(node, rrset, mm); +} + +void node_remove_rdataset(zone_node_t *node, uint16_t type) +{ + if (node == NULL) { + return; + } + + for (int i = 0; i < node->rrset_count; ++i) { + if (node->rrs[i].type == type) { + // We need to free additionals from this rr_data before it gets overwritten. + additional_clear(node->rrs[i].additional); + memmove(node->rrs + i, node->rrs + i + 1, + (node->rrset_count - i - 1) * sizeof(struct rr_data)); + --node->rrset_count; + return; + } + } +} + +knot_rrset_t *node_create_rrset(const zone_node_t *node, uint16_t type) +{ + if (node == NULL) { + return NULL; + } + + for (uint16_t i = 0; i < node->rrset_count; ++i) { + if (node->rrs[i].type == type) { + knot_rrset_t rrset = node_rrset_at(node, i); + return knot_rrset_copy(&rrset, NULL); + } + } + + return NULL; +} + +knot_rdataset_t *node_rdataset(const zone_node_t *node, uint16_t type) +{ + if (node == NULL) { + return NULL; + } + + for (uint16_t i = 0; i < node->rrset_count; ++i) { + if (node->rrs[i].type == type) { + return &node->rrs[i].rrs; + } + } + + return NULL; +} + +void node_set_parent(zone_node_t *node, zone_node_t *parent) +{ + if (node == NULL || node->parent == parent) { + return; + } + + // decrease number of children of previous parent + if (node->parent != NULL) { + --node->parent->children; + } + // set the parent + node->parent = parent; + + // increase the count of children of the new parent + if (parent != NULL) { + ++parent->children; + } +} + +bool node_rrtype_is_signed(const zone_node_t *node, uint16_t type) +{ + if (node == NULL) { + return false; + } + + const knot_rdataset_t *rrsigs = node_rdataset(node, KNOT_RRTYPE_RRSIG); + if (rrsigs == NULL) { + return false; + } + + uint16_t rrsigs_rdata_count = rrsigs->count; + knot_rdata_t *rrsig = rrsigs->rdata; + for (uint16_t i = 0; i < rrsigs_rdata_count; ++i) { + if (knot_rrsig_type_covered(rrsig) == type) { + return true; + } + rrsig = knot_rdataset_next(rrsig); + } + + return false; +} + +bool node_bitmap_equal(const zone_node_t *a, const zone_node_t *b) +{ + if (a == NULL || b == NULL || a->rrset_count != b->rrset_count) { + return false; + } + + uint16_t i; + // heuristics: try if they are equal including order + for (i = 0; i < a->rrset_count; i++) { + if (a->rrs[i].type != b->rrs[i].type) { + break; + } + } + if (i == a->rrset_count) { + return true; + } + + for (i = 0; i < a->rrset_count; i++) { + if (node_rdataset(b, a->rrs[i].type) == NULL) { + return false; + } + } + return true; +} diff --git a/src/knot/zone/node.h b/src/knot/zone/node.h new file mode 100644 index 0000000..07b4678 --- /dev/null +++ b/src/knot/zone/node.h @@ -0,0 +1,276 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "libknot/descriptor.h" +#include "libknot/dname.h" +#include "libknot/rrset.h" +#include "libknot/rdataset.h" + +struct rr_data; + +/*! + * \brief Structure representing one node in a domain name tree, i.e. one domain + * name in a zone. + */ +typedef struct zone_node { + knot_dname_t *owner; /*!< Domain name being the owner of this node. */ + struct zone_node *parent; /*!< Parent node in the name hierarchy. */ + + /*! \brief Array with data of RRSets belonging to this node. */ + struct rr_data *rrs; + + /*! + * \brief Previous node in canonical order. Only authoritative + * nodes or delegation points are referenced by this. + */ + struct zone_node *prev; + struct zone_node *nsec3_node; /*! NSEC3 node corresponding to this node. */ + struct zone_node *nsec3_wildcard_prev; /*! NSEC3 node for proof of wildcard non-existence. */ + uint32_t children; /*!< Count of children nodes in DNS hierarchy. */ + uint16_t rrset_count; /*!< Number of RRSets stored in the node. */ + uint8_t flags; /*!< \ref node_flags enum. */ +} zone_node_t; + +/*!< \brief Glue node context. */ +typedef struct { + const zone_node_t *node; /*!< Glue node. */ + uint16_t ns_pos; /*!< Corresponding NS record position (for compression). */ + bool optional; /*!< Optional glue indicator. */ +} glue_t; + +/*!< \brief Additional data. */ +typedef struct { + glue_t *glues; /*!< Glue data. */ + uint16_t count; /*!< Number of glue nodes. */ +} additional_t; + +/*!< \brief Structure storing RR data. */ +struct rr_data { + uint32_t ttl; /*!< RRSet TTL. */ + uint16_t type; /*!< RR type of data. */ + knot_rdataset_t rrs; /*!< Data of given type. */ + additional_t *additional; /*!< Additional nodes with glues. */ +}; + +/*! \brief Flags used to mark nodes with some property. */ +enum node_flags { + /*! \brief Node is authoritative, default. */ + NODE_FLAGS_AUTH = 0 << 0, + /*! \brief Node is a delegation point (i.e. marking a zone cut). */ + NODE_FLAGS_DELEG = 1 << 0, + /*! \brief Node is not authoritative (i.e. below a zone cut). */ + NODE_FLAGS_NONAUTH = 1 << 1, + /*! \brief Node is empty and will be deleted after update. */ + NODE_FLAGS_EMPTY = 1 << 3, + /*! \brief Node has a wildcard child. */ + NODE_FLAGS_WILDCARD_CHILD = 1 << 4, + /*! \brief Is this NSEC3 node compatible with zone's NSEC3PARAMS ? */ + NODE_FLAGS_IN_NSEC3_CHAIN = 1 << 5, +}; + +/*! + * \brief Clears additional structure. + * + * \param additional Additional to clear. + */ +void additional_clear(additional_t *additional); + +/*! + * \brief Creates and initializes new node structure. + * + * \param owner Node's owner, will be duplicated. + * \param mm Memory context to use. + * + * \return Newly created node or NULL if an error occurred. + */ +zone_node_t *node_new(const knot_dname_t *owner, knot_mm_t *mm); + +/*! + * \brief Destroys allocated data within the node + * structure, but not the node itself. + * + * \param node Node that contains data to be destroyed. + * \param mm Memory context to use. + */ +void node_free_rrsets(zone_node_t *node, knot_mm_t *mm); + +/*! + * \brief Destroys the node structure. + * + * Does not destroy the data within the node. + * + * \param node Node to be destroyed. + * \param mm Memory context to use. + */ +void node_free(zone_node_t *node, knot_mm_t *mm); + +/*! + * \brief Creates a shallow copy of node structure, RR data are shared. + * + * \param src Source of the copy. + * \param mm Memory context to use. + * + * \return Copied node if success, NULL otherwise. + */ +zone_node_t *node_shallow_copy(const zone_node_t *src, knot_mm_t *mm); + +/*! + * \brief Adds an RRSet to the node. All data are copied. Owner and class are + * not used at all. + * + * \param node Node to add the RRSet to. + * \param rrset RRSet to add. + * \param mm Memory context to use. + * + * \return KNOT_E* + * \retval KNOT_ETTL RRSet TTL was updated. + */ +int node_add_rrset(zone_node_t *node, const knot_rrset_t *rrset, knot_mm_t *mm); + +/*! + * \brief Removes data for given RR type from node. + * + * \param node Node we want to delete from. + * \param type RR type to delete. + */ +void node_remove_rdataset(zone_node_t *node, uint16_t type); + +/*! + * \brief Returns the RRSet of the given type from the node. RRSet is allocated. + * + * \param node Node to get the RRSet from. + * \param type RR type of the RRSet to retrieve. + * + * \return RRSet from node \a node having type \a type, or NULL if no such + * RRSet exists in this node. + */ +knot_rrset_t *node_create_rrset(const zone_node_t *node, uint16_t type); + +/*! + * \brief Gets rdata set structure of given type from node. + * + * \param node Node to get data from. + * \param type RR type of data to get. + * + * \return Pointer to data if found, NULL otherwise. + */ +knot_rdataset_t *node_rdataset(const zone_node_t *node, uint16_t type); + +/*! + * \brief Sets the parent of the node. Also adjusts children count of parent. + * + * \param node Node to set the parent of. + * \param parent Parent to set to the node. + */ +void node_set_parent(zone_node_t *node, zone_node_t *parent); + +/*! + * \brief Checks whether node contains any RRSIG for given type. + * + * \param node Node to check in. + * \param type Type to check for. + * + * \return True/False. + */ +bool node_rrtype_is_signed(const zone_node_t *node, uint16_t type); + +/*! + * \brief Checks whether node contains RRSet for given type. + * + * \param node Node to check in. + * \param type Type to check for. + * + * \return True/False. + */ +inline static bool node_rrtype_exists(const zone_node_t *node, uint16_t type) +{ + return node_rdataset(node, type) != NULL; +} + +/*! + * \brief Checks whether node is empty. Node is empty when NULL or when no + * RRSets are in it. + * + * \param node Node to check in. + * + * \return True/False. + */ +inline static bool node_empty(const zone_node_t *node) +{ + return node == NULL || node->rrset_count == 0; +} + +/*! + * \brief Check whether two nodes have equal set of rrtypes. + * + * \param a A node. + * \param b Another node. + * + * \return True/False. + */ +bool node_bitmap_equal(const zone_node_t *a, const zone_node_t *b); + +/*! + * \brief Returns RRSet structure initialized with data from node. + * + * \param node Node containing RRSet. + * \param type RRSet type we want to get. + * + * \return RRSet structure with wanted type, or empty RRSet. + */ +static inline knot_rrset_t node_rrset(const zone_node_t *node, uint16_t type) +{ + knot_rrset_t rrset; + for (uint16_t i = 0; node && i < node->rrset_count; ++i) { + if (node->rrs[i].type == type) { + struct rr_data *rr_data = &node->rrs[i]; + knot_rrset_init(&rrset, node->owner, type, KNOT_CLASS_IN, + rr_data->ttl); + rrset.rrs = rr_data->rrs; + rrset.additional = rr_data->additional; + return rrset; + } + } + knot_rrset_init_empty(&rrset); + return rrset; +} + +/*! + * \brief Returns RRSet structure initialized with data from node at position + * equal to \a pos. + * + * \param node Node containing RRSet. + * \param pos RRSet position we want to get. + * + * \return RRSet structure with data from wanted position, or empty RRSet. + */ +static inline knot_rrset_t node_rrset_at(const zone_node_t *node, size_t pos) +{ + knot_rrset_t rrset; + if (node == NULL || pos >= node->rrset_count) { + knot_rrset_init_empty(&rrset); + return rrset; + } + + struct rr_data *rr_data = &node->rrs[pos]; + knot_rrset_init(&rrset, node->owner, rr_data->type, KNOT_CLASS_IN, + rr_data->ttl); + rrset.rrs = rr_data->rrs; + rrset.additional = rr_data->additional; + return rrset; +} diff --git a/src/knot/zone/semantic-check.c b/src/knot/zone/semantic-check.c new file mode 100644 index 0000000..e7e9129 --- /dev/null +++ b/src/knot/zone/semantic-check.c @@ -0,0 +1,1193 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stdio.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include "libdnssec/error.h" +#include "contrib/base32hex.h" +#include "contrib/string.h" +#include "libknot/libknot.h" +#include "knot/zone/semantic-check.h" +#include "knot/dnssec/rrset-sign.h" +#include "knot/dnssec/zone-nsec.h" + +static const char *error_messages[SEM_ERR_UNKNOWN + 1] = { + [SEM_ERR_SOA_NONE] = + "missing SOA at the zone apex", + + [SEM_ERR_CNAME_EXTRA_RECORDS] = + "more records exist at CNAME", + [SEM_ERR_CNAME_MULTIPLE] = + "multiple CNAME records", + + [SEM_ERR_DNAME_CHILDREN] = + "child record exists under DNAME", + + [SEM_ERR_NS_APEX] = + "missing NS at the zone apex", + [SEM_ERR_NS_GLUE] = + "missing glue record", + + [SEM_ERR_RRSIG_RDATA_TYPE_COVERED] = + "wrong type covered in RRSIG", + [SEM_ERR_RRSIG_RDATA_TTL] = + "wrong original TTL in RRSIG", + [SEM_ERR_RRSIG_RDATA_EXPIRATION] = + "expired RRSIG", + [SEM_ERR_RRSIG_RDATA_INCEPTION] = + "RRSIG inception in the future", + [SEM_ERR_RRSIG_RDATA_LABELS] = + "wrong labels in RRSIG", + [SEM_ERR_RRSIG_RDATA_OWNER] = + "wrong signer's name in RRSIG", + [SEM_ERR_RRSIG_NO_RRSIG] = + "missing RRSIG", + [SEM_ERR_RRSIG_SIGNED] = + "signed RRSIG", + [SEM_ERR_RRSIG_UNVERIFIABLE] = + "unverifiable signature", + + [SEM_ERR_NSEC_NONE] = + "missing NSEC", + [SEM_ERR_NSEC_RDATA_BITMAP] = + "incorrect type bitmap in NSEC", + [SEM_ERR_NSEC_RDATA_MULTIPLE] = + "multiple NSEC records", + [SEM_ERR_NSEC_RDATA_CHAIN] = + "incoherent NSEC chain", + + [SEM_ERR_NSEC3_NONE] = + "missing NSEC3", + [SEM_ERR_NSEC3_INSECURE_DELEGATION_OPT] = + "insecure delegation outside NSEC3 opt-out", + [SEM_ERR_NSEC3_EXTRA_RECORD] = + "invalid record type in NSEC3 chain", + [SEM_ERR_NSEC3_RDATA_TTL] = + "inconsistent TTL for NSEC3 and minimum TTL in SOA", + [SEM_ERR_NSEC3_RDATA_CHAIN] = + "incoherent NSEC3 chain", + [SEM_ERR_NSEC3_RDATA_BITMAP] = + "incorrect type bitmap in NSEC3", + [SEM_ERR_NSEC3_RDATA_FLAGS] = + "incorrect flags in NSEC3", + [SEM_ERR_NSEC3_RDATA_SALT] = + "incorrect salt in NSEC3", + [SEM_ERR_NSEC3_RDATA_ALG] = + "incorrect algorithm in NSEC3", + [SEM_ERR_NSEC3_RDATA_ITERS] = + "incorrect number of iterations in NSEC3", + + [SEM_ERR_NSEC3PARAM_RDATA_FLAGS] = + "invalid flags in NSEC3PARAM", + [SEM_ERR_NSEC3PARAM_RDATA_ALG] = + "invalid algorithm in NSEC3PARAM", + + [SEM_ERR_DS_RDATA_ALG] = + "invalid algorithm in DS", + [SEM_ERR_DS_RDATA_DIGLEN] = + "invalid digest length in DS", + + [SEM_ERR_DNSKEY_NONE] = + "missing DNSKEY", + [SEM_ERR_DNSKEY_INVALID] = + "invalid DNSKEY", + [SEM_ERR_DNSKEY_RDATA_PROTOCOL] = + "invalid protocol in DNSKEY", + + [SEM_ERR_CDS_NONE] = + "missing CDS", + [SEM_ERR_CDS_MULTIPLE] = + "multiple CDS records", + [SEM_ERR_CDS_NOT_MATCH] = + "CDS not match CDNSKEY", + + [SEM_ERR_CDNSKEY_NONE] = + "missing CDNSKEY", + [SEM_ERR_CDNSKEY_MULTIPLE] = + "multiple CDNSKEY records", + [SEM_ERR_CDNSKEY_NO_DNSKEY] = + "CDNSKEY not match DNSKEY", + + [SEM_ERR_UNKNOWN] = + "unknown error" +}; + +const char *sem_error_msg(sem_error_t code) +{ + if (code > SEM_ERR_UNKNOWN) { + code = SEM_ERR_UNKNOWN; + } + return error_messages[code]; +} + +typedef enum { + MANDATORY = 1 << 0, + OPTIONAL = 1 << 1, + NSEC = 1 << 2, + NSEC3 = 1 << 3, +} check_level_t; + +typedef struct { + zone_contents_t *zone; + sem_handler_t *handler; + const zone_node_t *next_nsec; + check_level_t level; + time_t time; +} semchecks_data_t; + +static int check_cname(const zone_node_t *node, semchecks_data_t *data); +static int check_dname(const zone_node_t *node, semchecks_data_t *data); +static int check_delegation(const zone_node_t *node, semchecks_data_t *data); +static int check_submission(const zone_node_t *node, semchecks_data_t *data); +static int check_ds(const zone_node_t *node, semchecks_data_t *data); +static int check_nsec(const zone_node_t *node, semchecks_data_t *data); +static int check_nsec3(const zone_node_t *node, semchecks_data_t *data); +static int check_nsec3_opt_out(const zone_node_t *node, semchecks_data_t *data); +static int check_rrsig(const zone_node_t *node, semchecks_data_t *data); +static int check_rrsig_signed(const zone_node_t *node, semchecks_data_t *data); +static int check_nsec_bitmap(const zone_node_t *node, semchecks_data_t *data); +static int check_nsec3_presence(const zone_node_t *node, semchecks_data_t *data); + +struct check_function { + int (*function)(const zone_node_t *, semchecks_data_t *); + check_level_t level; +}; + +/* List of function callbacks for defined check_level */ +static const struct check_function CHECK_FUNCTIONS[] = { + { check_cname, MANDATORY }, + { check_dname, MANDATORY }, + { check_delegation, OPTIONAL }, + { check_submission, OPTIONAL }, + { check_ds, OPTIONAL }, + { check_rrsig, NSEC | NSEC3 }, + { check_rrsig_signed, NSEC | NSEC3 }, + { check_nsec_bitmap, NSEC | NSEC3 }, + { check_nsec, NSEC }, + { check_nsec3, NSEC3 }, + { check_nsec3_presence, NSEC3 }, + { check_nsec3_opt_out, NSEC3 }, +}; + +static const int CHECK_FUNCTIONS_LEN = sizeof(CHECK_FUNCTIONS) + / sizeof(struct check_function); + +static int dnssec_key_from_rdata(dnssec_key_t **key, const knot_dname_t *owner, + const uint8_t *rdata, size_t rdlen) +{ + if (!key || !rdata || rdlen == 0) { + return KNOT_EINVAL; + } + + const dnssec_binary_t binary_key = { + .size = rdlen, + .data = (uint8_t *)rdata + }; + + dnssec_key_t *new_key = NULL; + int ret = dnssec_key_new(&new_key); + if (ret != DNSSEC_EOK) { + return KNOT_ENOMEM; + } + ret = dnssec_key_set_rdata(new_key, &binary_key); + if (ret != DNSSEC_EOK) { + dnssec_key_free(new_key); + return KNOT_ENOMEM; + } + if (owner) { + ret = dnssec_key_set_dname(new_key, owner); + if (ret != DNSSEC_EOK) { + dnssec_key_free(new_key); + return KNOT_ENOMEM; + } + } + + *key = new_key; + return KNOT_EOK; +} + +static int check_signature(const knot_rdata_t *rrsig, const dnssec_key_t *key, + const knot_rrset_t *covered) +{ + if (!rrsig || !key || !dnssec_key_can_verify(key)) { + return KNOT_EINVAL; + } + + int ret = KNOT_EOK; + dnssec_sign_ctx_t *sign_ctx = NULL; + + dnssec_binary_t signature = { + .size = knot_rrsig_signature_len(rrsig), + .data = (uint8_t *)knot_rrsig_signature(rrsig) + }; + if (!signature.data || !signature.size) { + ret = KNOT_EINVAL; + goto fail; + } + + if (dnssec_sign_new(&sign_ctx, key) != KNOT_EOK) { + ret = KNOT_ENOMEM; + goto fail; + } + + if (knot_sign_ctx_add_data(sign_ctx, rrsig->data, covered) != KNOT_EOK) { + ret = KNOT_ENOMEM; + goto fail; + } + + if (dnssec_sign_verify(sign_ctx, &signature) != KNOT_EOK) { + ret = KNOT_EINVAL; + goto fail; + } + +fail: + dnssec_sign_free(sign_ctx); + return ret; +} + +/*! + * \brief Semantic check - RRSIG rdata. + * + * \param handler Pointer on function to be called in case of negative check. + * \param zone The zone the rrset is in. + * \param node The node in the zone contents. + * \param rrsig RRSIG rdata. + * \param rrset RRSet signed by the RRSIG. + * \param context The time stamp we check the rrsig validity according to. + * \param level Level of the check. + * \param verified Out: the RRSIG has been verified to be signed by existing DNSKEY. + * + * \retval KNOT_EOK on success. + * \return Appropriate error code if error was found. + */ +static int check_rrsig_rdata(sem_handler_t *handler, + const zone_contents_t *zone, + const zone_node_t *node, + const knot_rdata_t *rrsig, + const knot_rrset_t *rrset, + time_t context, + check_level_t level, + bool *verified) +{ + /* Prepare additional info string. */ + char info_str[50] = { '\0' }; + char type_str[16] = { '\0' }; + knot_rrtype_to_string(rrset->type, type_str, sizeof(type_str)); + int ret = snprintf(info_str, sizeof(info_str), "(record type %s)", type_str); + if (ret < 0 || ret >= sizeof(info_str)) { + return KNOT_ENOMEM; + } + + if (knot_rrsig_type_covered(rrsig) != rrset->type) { + handler->cb(handler, zone, node, SEM_ERR_RRSIG_RDATA_TYPE_COVERED, + info_str); + } + + /* label number at the 2nd index should be same as owner's */ + uint8_t labels_rdata = knot_rrsig_labels(rrsig); + + size_t tmp = knot_dname_labels(rrset->owner, NULL) - labels_rdata; + if (tmp != 0) { + /* if name has wildcard, label must not be included */ + if (!knot_dname_is_wildcard(rrset->owner)) { + handler->cb(handler, zone, node, SEM_ERR_RRSIG_RDATA_LABELS, + info_str); + } else if (tmp != 1) { + handler->cb(handler, zone, node, SEM_ERR_RRSIG_RDATA_LABELS, + info_str); + } + } + + /* Check original TTL. */ + uint32_t original_ttl = knot_rrsig_original_ttl(rrsig); + if (original_ttl != rrset->ttl) { + handler->cb(handler, zone, node, SEM_ERR_RRSIG_RDATA_TTL, + info_str); + } + + /* Check for expired signature. */ + if (knot_rrsig_sig_expiration(rrsig) < context) { + handler->cb(handler, zone, node, SEM_ERR_RRSIG_RDATA_EXPIRATION, + info_str); + } + + /* Check inception */ + if (knot_rrsig_sig_inception(rrsig) > context) { + handler->cb(handler, zone, node, SEM_ERR_RRSIG_RDATA_INCEPTION, + info_str); + } + + /* Check signer name. */ + const knot_dname_t *signer = knot_rrsig_signer_name(rrsig); + if (!knot_dname_is_equal(signer, zone->apex->owner)) { + handler->cb(handler, zone, node, SEM_ERR_RRSIG_RDATA_OWNER, + info_str); + } + + /* Verify with public key - only one RRSIG of covered record needed */ + if (level & OPTIONAL && !*verified) { + const knot_rdataset_t *dnskeys = node_rdataset(zone->apex, KNOT_RRTYPE_DNSKEY); + if (dnskeys == NULL) { + return KNOT_EOK; + } + + for (int i = 0; i < dnskeys->count; i++) { + knot_rdata_t *dnskey = knot_rdataset_at(dnskeys, i); + uint16_t flags = knot_dnskey_flags(dnskey); + uint8_t proto = knot_dnskey_proto(dnskey); + /* RFC 4034 2.1.1 & 2.1.2 */ + if (flags & DNSKEY_FLAGS_ZSK && proto == 3) { + dnssec_key_t *key; + + ret = dnssec_key_from_rdata(&key, zone->apex->owner, + dnskey->data, dnskey->len); + if (ret != KNOT_EOK) { + continue; + } + + ret = check_signature(rrsig, key, rrset); + dnssec_key_free(key); + if (ret == KNOT_EOK) { + *verified = true; + break; + } + } + } + } + + return KNOT_EOK; +} + +static int check_rrsig_signed(const zone_node_t *node, semchecks_data_t *data) +{ + /* signed rrsig - nonsense */ + if (node_rrtype_is_signed(node, KNOT_RRTYPE_RRSIG)) { + data->handler->cb(data->handler, data->zone, node, + SEM_ERR_RRSIG_SIGNED, NULL); + } + + return KNOT_EOK; +} +/*! + * \brief Semantic check - RRSet's RRSIG. + * + * \param handler Pointer on function to be called in case of negative check. + * \param zone The zone the rrset is in. + * \param node The node in the zone contents. + * \param rrset RRSet signed by the RRSIG. + * \param context The time stamp we check the rrsig validity according to. + * \param level Level of the check. + * + * \retval KNOT_EOK on success. + * \return Appropriate error code if error was found. + */ +static int check_rrsig_in_rrset(sem_handler_t *handler, + const zone_contents_t *zone, + const zone_node_t *node, + const knot_rrset_t *rrset, + time_t context, + check_level_t level) +{ + if (handler == NULL || node == NULL || rrset == NULL) { + return KNOT_EINVAL; + } + /* Prepare additional info string. */ + char info_str[50] = { '\0' }; + char type_str[16] = { '\0' }; + knot_rrtype_to_string(rrset->type, type_str, sizeof(type_str)); + int ret = snprintf(info_str, sizeof(info_str), "(record type %s)", type_str); + if (ret < 0 || ret >= sizeof(info_str)) { + return KNOT_ENOMEM; + } + + knot_rrset_t node_rrsigs = node_rrset(node, KNOT_RRTYPE_RRSIG); + + knot_rdataset_t rrsigs; + knot_rdataset_init(&rrsigs); + ret = knot_synth_rrsig(rrset->type, &node_rrsigs.rrs, &rrsigs, NULL); + if (ret != KNOT_EOK && ret != KNOT_ENOENT) { + return ret; + } + if (ret == KNOT_ENOENT) { + handler->cb(handler, zone, node, SEM_ERR_RRSIG_NO_RRSIG, info_str); + return KNOT_EOK; + } + + bool verified = false; + knot_rdata_t *rrsig = rrsigs.rdata; + for (uint16_t i = 0; ret == KNOT_EOK && i < rrsigs.count; ++i) { + ret = check_rrsig_rdata(handler, zone, node, rrsig, rrset, + context, level, &verified); + rrsig = knot_rdataset_next(rrsig); + } + /* Only one rrsig of covered record needs to be verified by DNSKEY. */ + if (!verified) { + handler->cb(handler, zone, node, SEM_ERR_RRSIG_UNVERIFIABLE, + info_str); + } + + knot_rdataset_clear(&rrsigs, NULL); + return KNOT_EOK;; +} + +/*! + * \brief Check if glue record for delegation is present. + * + * Also check if there is NS record in the zone. + * \param node Node to check + * \param data Semantic checks context data + */ +static int check_delegation(const zone_node_t *node, semchecks_data_t *data) +{ + if (!((node->flags & NODE_FLAGS_DELEG) || data->zone->apex == node)) { + return KNOT_EOK; + } + + const knot_rdataset_t *ns_rrs = node_rdataset(node, KNOT_RRTYPE_NS); + if (ns_rrs == NULL) { + assert(data->zone->apex == node); + data->handler->cb(data->handler, data->zone, node, + SEM_ERR_NS_APEX, NULL); + return KNOT_EOK; + } + + // check glue record for delegation + for (int i = 0; i < ns_rrs->count; ++i) { + knot_rdata_t *ns_rr = knot_rdataset_at(ns_rrs, i); + const knot_dname_t *ns_dname = knot_ns_name(ns_rr); + if (knot_dname_in_bailiwick(ns_dname, node->owner) <= 0) { + continue; + } + + const zone_node_t *glue_node = + zone_contents_find_node(data->zone, ns_dname); + + if (glue_node == NULL) { + /* Try wildcard ([1]* + suffix). */ + knot_dname_t wildcard[KNOT_DNAME_MAXLEN]; + memcpy(wildcard, "\x1""*", 2); + knot_dname_to_wire(wildcard + 2, + knot_wire_next_label(ns_dname, NULL), + sizeof(wildcard) - 2); + glue_node = zone_contents_find_node(data->zone, wildcard); + } + if (!node_rrtype_exists(glue_node, KNOT_RRTYPE_A) && + !node_rrtype_exists(glue_node, KNOT_RRTYPE_AAAA)) { + data->handler->cb(data->handler, data->zone, node, + SEM_ERR_NS_GLUE, NULL); + } + } + + return KNOT_EOK; +} + +/*! + * \brief check_submission_records Check CDS and CDNSKEY + */ +static int check_submission(const zone_node_t *node, semchecks_data_t *data) +{ + const knot_rdataset_t *cdss = node_rdataset(node, KNOT_RRTYPE_CDS); + const knot_rdataset_t *cdnskeys = node_rdataset(node, KNOT_RRTYPE_CDNSKEY); + if (cdss == NULL && cdnskeys == NULL) { + return KNOT_EOK; + } else if (cdss == NULL) { + data->handler->cb(data->handler, data->zone, node, + SEM_ERR_CDS_NONE, NULL); + return KNOT_EOK; + } else if (cdnskeys == NULL) { + data->handler->cb(data->handler, data->zone, node, + SEM_ERR_CDNSKEY_NONE, NULL); + return KNOT_EOK; + } + + if (cdss->count != 1) { + data->handler->cb(data->handler, data->zone, node, + SEM_ERR_CDS_MULTIPLE, NULL); + } + if (cdnskeys->count != 1) { + data->handler->cb(data->handler, data->zone, node, + SEM_ERR_CDNSKEY_MULTIPLE, NULL); + } + + knot_rdata_t *cdnskey = cdnskeys->rdata; + knot_rdata_t *cds = cdss->rdata; + uint8_t digest_type = knot_ds_digest_type(cdss->rdata); + + const knot_rdataset_t *dnskeys = node_rdataset(data->zone->apex, + KNOT_RRTYPE_DNSKEY); + for (int i = 0; dnskeys != NULL && i < dnskeys->count; i++) { + knot_rdata_t *dnskey = knot_rdataset_at(dnskeys, i); + if (knot_rdata_cmp(dnskey, cdnskey) != 0) { + continue; + } + + dnssec_key_t *key; + int ret = dnssec_key_from_rdata(&key, data->zone->apex->owner, + dnskey->data, dnskey->len); + if (ret != KNOT_EOK) { + return ret; + } + + dnssec_binary_t cds_calc = { 0 }; + dnssec_binary_t cds_orig = { .size = cds->len, .data = cds->data }; + ret = dnssec_key_create_ds(key, digest_type, &cds_calc); + if (ret != KNOT_EOK) { + dnssec_key_free(key); + return ret; + } + + ret = dnssec_binary_cmp(&cds_orig, &cds_calc); + dnssec_binary_free(&cds_calc); + dnssec_key_free(key); + if (ret == 0) { + return KNOT_EOK; + } else { + data->handler->cb(data->handler, data->zone, node, + SEM_ERR_CDS_NOT_MATCH, NULL); + } + } + + if (dnskeys != NULL) { + data->handler->cb(data->handler, data->zone, node, + SEM_ERR_CDNSKEY_NO_DNSKEY, NULL); + } + return KNOT_EOK; +} + +/*! + * \brief Semantic check - DS record. + * + * \param node Node to check + * \param data Semantic checks context data + * + * \retval KNOT_EOK on success. + * \return Appropriate error code if error was found. + */ +static int check_ds(const zone_node_t *node, semchecks_data_t *data) +{ + const knot_rdataset_t *dss = node_rdataset(node, KNOT_RRTYPE_DS); + if (dss == NULL) { + return KNOT_EOK; + } + + for (int i = 0; i < dss->count; i++) { + knot_rdata_t *ds = knot_rdataset_at(dss, i); + uint16_t keytag = knot_ds_key_tag(ds); + uint8_t digest_type = knot_ds_digest_type(ds); + + char info[100] = ""; + (void)snprintf(info, sizeof(info), "(keytag %d)", keytag); + + if (!dnssec_algorithm_digest_support(digest_type)) { + data->handler->cb(data->handler, data->zone, node, + SEM_ERR_DS_RDATA_ALG, info); + } else { + // Sizes for different digest algorithms. + const uint16_t digest_sizes [] = { 0, 20, 32, 32, 48}; + + uint16_t digest_size = knot_ds_digest_len(ds); + + if (digest_sizes[digest_type] != digest_size) { + data->handler->cb(data->handler, data->zone, node, + SEM_ERR_DS_RDATA_DIGLEN, info); + } + } + } + + return KNOT_EOK; +} + +/*! + * \brief Run all semantic check related to RRSIG record + * + * \param node Node to check + * \param data Semantic checks context data + */ +static int check_rrsig(const zone_node_t *node, semchecks_data_t *data) +{ + assert(node); + if (node->flags & NODE_FLAGS_NONAUTH) { + return KNOT_EOK; + } + + bool deleg = node->flags & NODE_FLAGS_DELEG; + + int ret = KNOT_EOK; + + int rrset_count = node->rrset_count; + for (int i = 0; ret == KNOT_EOK && i < rrset_count; i++) { + knot_rrset_t rrset = node_rrset_at(node, i); + if (rrset.type == KNOT_RRTYPE_RRSIG) { + continue; + } + if (deleg && rrset.type != KNOT_RRTYPE_NSEC && + rrset.type != KNOT_RRTYPE_DS ) { + continue; + } + + ret = check_rrsig_in_rrset(data->handler, data->zone, node, &rrset, + data->time, data->level); + } + return ret; +} + +/*! + * \brief Add all RR types from a node into the bitmap. + */ +static void bitmap_add_all_node_rrsets(dnssec_nsec_bitmap_t *bitmap, + const zone_node_t *node) +{ + bool deleg = node->flags & NODE_FLAGS_DELEG; + for (int i = 0; i < node->rrset_count; i++) { + knot_rrset_t rr = node_rrset_at(node, i); + if (deleg && (rr.type != KNOT_RRTYPE_NS && + rr.type != KNOT_RRTYPE_DS && + rr.type != KNOT_RRTYPE_NSEC && + rr.type != KNOT_RRTYPE_RRSIG)) { + continue; + } + dnssec_nsec_bitmap_add(bitmap, rr.type); + } +} + +static char *nsec3_info(const knot_dname_t *owner, char *out, size_t out_len) +{ + char buff[KNOT_DNAME_TXT_MAXLEN + 1]; + char *str = knot_dname_to_str(buff, owner, sizeof(buff)); + if (str == NULL) { + return NULL; + } + + int ret = snprintf(out, out_len, "(NSEC3 owner=%s)", str); + if (ret <= 0 || ret >= out_len) { + return NULL; + } + + return out; +} + +/*! + * \brief Check NSEC and NSEC3 type bitmap + * + * \param node Node to check + * \param data Semantic checks context data + */ +static int check_nsec_bitmap(const zone_node_t *node, semchecks_data_t *data) +{ + assert(node); + if (node->flags & NODE_FLAGS_NONAUTH) { + return KNOT_EOK; + } + + bool nsec = data->level & NSEC; + knot_rdataset_t *nsec_rrs = NULL; + + if (nsec) { + nsec_rrs = node_rdataset(node, KNOT_RRTYPE_NSEC); + } else if (node->nsec3_node != NULL) { + nsec_rrs = node_rdataset(node->nsec3_node, KNOT_RRTYPE_NSEC3); + } + if (nsec_rrs == NULL) { + return KNOT_EOK; + } + + // create NSEC bitmap from node + dnssec_nsec_bitmap_t *node_bitmap = dnssec_nsec_bitmap_new(); + if (node_bitmap == NULL) { + return KNOT_ENOMEM; + } + bitmap_add_all_node_rrsets(node_bitmap, node); + + uint16_t node_wire_size = dnssec_nsec_bitmap_size(node_bitmap); + uint8_t *node_wire = malloc(node_wire_size); + if (node_wire == NULL) { + dnssec_nsec_bitmap_free(node_bitmap); + return KNOT_ENOMEM; + } + dnssec_nsec_bitmap_write(node_bitmap, node_wire); + dnssec_nsec_bitmap_free(node_bitmap); + + // get NSEC bitmap from NSEC node + const uint8_t *nsec_wire = NULL; + uint16_t nsec_wire_size = 0; + if (nsec) { + nsec_wire = knot_nsec_bitmap(nsec_rrs->rdata); + nsec_wire_size = knot_nsec_bitmap_len(nsec_rrs->rdata); + } else { + nsec_wire = knot_nsec3_bitmap(nsec_rrs->rdata); + nsec_wire_size = knot_nsec3_bitmap_len(nsec_rrs->rdata); + } + + if (node_wire_size != nsec_wire_size || + memcmp(node_wire, nsec_wire, node_wire_size) != 0) { + char buff[50 + KNOT_DNAME_TXT_MAXLEN]; + char *info = nsec ? NULL : nsec3_info(node->nsec3_node->owner, + buff, sizeof(buff)); + data->handler->cb(data->handler, data->zone, node, + (nsec ? SEM_ERR_NSEC_RDATA_BITMAP : SEM_ERR_NSEC3_RDATA_BITMAP), + info); + } + + free(node_wire); + return KNOT_EOK; +} + +/*! + * \brief Run NSEC related semantic checks + * + * \param node Node to check + * \param data Semantic checks context data + */ +static int check_nsec(const zone_node_t *node, semchecks_data_t *data) +{ + assert(node); + if (node->flags & NODE_FLAGS_NONAUTH) { + return KNOT_EOK; + } + + if (node->rrset_count == 0) { // empty nonterminal + return KNOT_EOK; + } + + /* check for NSEC record */ + const knot_rdataset_t *nsec_rrs = node_rdataset(node, KNOT_RRTYPE_NSEC); + if (nsec_rrs == NULL) { + data->handler->cb(data->handler, data->zone, node, + SEM_ERR_NSEC_NONE, NULL); + return KNOT_EOK; + } + + /* Test that only one record is in the NSEC RRSet */ + if (nsec_rrs->count != 1) { + data->handler->cb(data->handler, data->zone, node, + SEM_ERR_NSEC_RDATA_MULTIPLE, NULL); + } + + if (data->next_nsec != node) { + data->handler->cb(data->handler, data->zone, node, + SEM_ERR_NSEC_RDATA_CHAIN, NULL); + } + + /* + * Test that NSEC chain is coherent. + * We have already checked that every + * authoritative node contains NSEC record + * so checking should only be matter of testing + * the next link in each node. + */ + const knot_dname_t *next_domain = knot_nsec_next(nsec_rrs->rdata); + + data->next_nsec = zone_contents_find_node(data->zone, next_domain); + if (data->next_nsec == NULL) { + data->handler->cb(data->handler, data->zone, node, + SEM_ERR_NSEC_RDATA_CHAIN, NULL); + } + + return KNOT_EOK; +} + +/*! + * \brief Check if node has NSEC3 node. + * + * \param node Node to check + * \param data Semantic checks context data + */ +static int check_nsec3_presence(const zone_node_t *node, semchecks_data_t *data) +{ + bool auth = (node->flags & NODE_FLAGS_NONAUTH) == 0; + bool deleg = (node->flags & NODE_FLAGS_DELEG) != 0; + + if ((deleg && node_rrtype_exists(node, KNOT_RRTYPE_DS)) || (auth && !deleg)) { + if (node->nsec3_node == NULL) { + data->handler->cb(data->handler, data->zone, node, + SEM_ERR_NSEC3_NONE, NULL); + } + } + + return KNOT_EOK; +} + +/*! + * \brief Check NSEC3 opt-out. + * + * \param node Node to check + * \param data Semantic checks context data + */ +static int check_nsec3_opt_out(const zone_node_t *node, semchecks_data_t *data) +{ + if (!(node->nsec3_node == NULL && node->flags & NODE_FLAGS_DELEG)) { + return KNOT_EOK; + } + /* Insecure delegation, check whether it is part of opt-out span. */ + + const zone_node_t *nsec3_previous = NULL; + const zone_node_t *nsec3_node; + zone_contents_find_nsec3_for_name(data->zone, node->owner, &nsec3_node, + &nsec3_previous); + + if (nsec3_previous == NULL) { + data->handler->cb(data->handler, data->zone, node, + SEM_ERR_NSEC3_NONE, NULL); + return KNOT_EOK; + } + + const knot_rdataset_t *previous_rrs; + previous_rrs = node_rdataset(nsec3_previous, KNOT_RRTYPE_NSEC3); + assert(previous_rrs); + + /* Check for opt-out flag. */ + uint8_t flags = knot_nsec3_flags(previous_rrs->rdata); + if (!(flags & 1)) { + data->handler->cb(data->handler, data->zone, node, + SEM_ERR_NSEC3_INSECURE_DELEGATION_OPT, NULL); + } + + return KNOT_EOK; +} + +/*! + * \brief Run checks related to NSEC3. + * + * Check NSEC3 node for given node. + * Check if NSEC3 chain is coherent and cyclic. + * \param node Node to check + * \param data Semantic checks context data + */ +static int check_nsec3(const zone_node_t *node, semchecks_data_t *data) +{ + assert(node); + bool auth = (node->flags & NODE_FLAGS_NONAUTH) == 0; + bool deleg = (node->flags & NODE_FLAGS_DELEG) != 0; + + if (!auth && !deleg) { + return KNOT_EOK; + } + if (node->nsec3_node == NULL) { + return KNOT_EOK; + } + + dnssec_nsec3_params_t params_apex = { 0 }; + int ret = KNOT_EOK; + + char buff[50 + KNOT_DNAME_TXT_MAXLEN]; + char *info = nsec3_info(node->nsec3_node->owner, buff, sizeof(buff)); + + knot_rrset_t nsec3_rrs = node_rrset(node->nsec3_node, KNOT_RRTYPE_NSEC3); + if (knot_rrset_empty(&nsec3_rrs)) { + data->handler->cb(data->handler, data->zone, node, + SEM_ERR_NSEC3_NONE, info); + goto nsec3_cleanup; + } + + const knot_rdataset_t *soa_rrs = node_rdataset(data->zone->apex, KNOT_RRTYPE_SOA); + assert(soa_rrs); + uint32_t minimum_ttl = knot_soa_minimum(soa_rrs->rdata); + if (nsec3_rrs.ttl != minimum_ttl) { + data->handler->cb(data->handler, data->zone, node, + SEM_ERR_NSEC3_RDATA_TTL, info); + } + + // Check parameters. + const knot_rdataset_t *nsec3param = node_rdataset(data->zone->apex, + KNOT_RRTYPE_NSEC3PARAM); + dnssec_binary_t rdata = { + .size = nsec3param->rdata->len, + .data = nsec3param->rdata->data + }; + ret = dnssec_nsec3_params_from_rdata(¶ms_apex, &rdata); + if (ret != DNSSEC_EOK) { + ret = knot_error_from_libdnssec(ret); + goto nsec3_cleanup; + } + + if (knot_nsec3_flags(nsec3_rrs.rrs.rdata) > 1) { + data->handler->cb(data->handler, data->zone, node, + SEM_ERR_NSEC3_RDATA_FLAGS, info); + } + + dnssec_binary_t salt = { + .size = knot_nsec3_salt_len(nsec3_rrs.rrs.rdata), + .data = (uint8_t *)knot_nsec3_salt(nsec3_rrs.rrs.rdata), + }; + + if (dnssec_binary_cmp(&salt, ¶ms_apex.salt)) { + data->handler->cb(data->handler, data->zone, node, + SEM_ERR_NSEC3_RDATA_SALT, info); + } + + if (knot_nsec3_alg(nsec3_rrs.rrs.rdata) != params_apex.algorithm) { + data->handler->cb(data->handler, data->zone, node, + SEM_ERR_NSEC3_RDATA_ALG, info); + } + + if (knot_nsec3_iters(nsec3_rrs.rrs.rdata) != params_apex.iterations) { + data->handler->cb(data->handler, data->zone, node, + SEM_ERR_NSEC3_RDATA_ITERS, info); + } + + // Get next nsec3 node. + const zone_node_t *apex = data->zone->apex; + const uint8_t *next_dname_str = knot_nsec3_next(nsec3_rrs.rrs.rdata); + uint8_t next_dname_str_size = knot_nsec3_next_len(nsec3_rrs.rrs.rdata); + uint8_t next_dname[KNOT_DNAME_MAXLEN]; + ret = knot_nsec3_hash_to_dname(next_dname, sizeof(next_dname), + next_dname_str, next_dname_str_size, + apex->owner); + if (ret != KNOT_EOK) { + goto nsec3_cleanup; + } + + const zone_node_t *next_nsec3 = zone_contents_find_nsec3_node(data->zone, + next_dname); + if (next_nsec3 == NULL || next_nsec3->prev != node->nsec3_node) { + uint8_t *next = NULL; + int32_t next_len = base32hex_encode_alloc(next_dname_str, + next_dname_str_size, + &next); + char *hash_info = NULL; + if (next != NULL) { + hash_info = sprintf_alloc("(next hash %.*s)", next_len, next); + free(next); + } + data->handler->cb(data->handler, data->zone, node, + SEM_ERR_NSEC3_RDATA_CHAIN, hash_info); + free(hash_info); + } + + ret = check_rrsig(node->nsec3_node, data); + if (ret != KNOT_EOK) { + goto nsec3_cleanup; + } + + // Check that the node only contains NSEC3 and RRSIG. + for (int i = 0; ret == KNOT_EOK && i < node->nsec3_node->rrset_count; i++) { + knot_rrset_t rrset = node_rrset_at(node->nsec3_node, i); + uint16_t type = rrset.type; + if (type != KNOT_RRTYPE_NSEC3 && type != KNOT_RRTYPE_RRSIG) { + data->handler->cb(data->handler, data->zone, node->nsec3_node, + SEM_ERR_NSEC3_EXTRA_RECORD, NULL); + } + } + +nsec3_cleanup: + dnssec_nsec3_params_free(¶ms_apex); + + return ret; +} + +/*! + * \brief Check if CNAME record contains other records + * + * \param node Node to check + * \param data Semantic checks context data + */ +static int check_cname(const zone_node_t *node, semchecks_data_t *data) +{ + const knot_rdataset_t *cname_rrs = node_rdataset(node, KNOT_RRTYPE_CNAME); + if (cname_rrs == NULL) { + return KNOT_EOK; + } + + unsigned rrset_limit = 1; + /* With DNSSEC node can contain RRSIGs or NSEC */ + if (node_rrtype_exists(node, KNOT_RRTYPE_NSEC)) { + rrset_limit += 1; + } + if (node_rrtype_exists(node, KNOT_RRTYPE_RRSIG)) { + rrset_limit += 1; + } + + if (node->rrset_count > rrset_limit) { + data->handler->fatal_error = true; + data->handler->cb(data->handler, data->zone, node, + SEM_ERR_CNAME_EXTRA_RECORDS, NULL); + } + if (cname_rrs->count != 1) { + data->handler->fatal_error = true; + data->handler->cb(data->handler, data->zone, node, + SEM_ERR_CNAME_MULTIPLE, NULL); + } + + return KNOT_EOK; +} + +/*! + * \brief Check if DNAME record has children. + * + * \param node Node to check + * \param data Semantic checks context data + */ +static int check_dname(const zone_node_t *node, semchecks_data_t *data) +{ + if (node->parent != NULL && node_rrtype_exists(node->parent, KNOT_RRTYPE_DNAME)) { + data->handler->fatal_error = true; + data->handler->cb(data->handler, data->zone, node, + SEM_ERR_DNAME_CHILDREN, NULL); + } + + return KNOT_EOK; +} + +/*! + * \brief Check that NSEC chain is cyclic. + * + * Run only once per zone. Check that last NSEC node points to first one. + * \param data Semantic checks context data + */ +static int check_nsec_cyclic(semchecks_data_t *data) +{ + if (data->next_nsec == NULL) { + data->handler->cb(data->handler, data->zone, data->zone->apex, + SEM_ERR_NSEC_RDATA_CHAIN, NULL); + return KNOT_EOK; + } + if (!knot_dname_is_equal(data->next_nsec->owner, data->zone->apex->owner)) { + data->handler->cb(data->handler, data->zone, data->next_nsec, + SEM_ERR_NSEC_RDATA_CHAIN, NULL); + } + + return KNOT_EOK; +} + +/*! + * \brief Call all semantic checks for each node. + * + * This function is called as callback from zone_contents_tree_apply_inorder. + * Checks are functions from global const array check_functions. + * + * \param node Node to be checked + * \param data Semantic checks context data + */ +static int do_checks_in_tree(zone_node_t *node, void *data) +{ + semchecks_data_t *s_data = (semchecks_data_t *)data; + + int ret = KNOT_EOK; + + for (int i = 0; ret == KNOT_EOK && i < CHECK_FUNCTIONS_LEN; ++i) { + if (CHECK_FUNCTIONS[i].level & s_data->level) { + ret = CHECK_FUNCTIONS[i].function(node, s_data); + } + } + + + return ret; +} + +static void check_nsec3param(knot_rdataset_t *nsec3param, zone_contents_t *zone, + sem_handler_t *handler, semchecks_data_t *data) +{ + assert(nsec3param); + + data->level |= NSEC3; + uint8_t param = knot_nsec3param_flags(nsec3param->rdata); + if ((param & ~1) != 0) { + handler->cb(handler, zone, zone->apex, SEM_ERR_NSEC3PARAM_RDATA_FLAGS, + NULL); + } + + param = knot_nsec3param_alg(nsec3param->rdata); + if (param != DNSSEC_NSEC3_ALGORITHM_SHA1) { + handler->cb(handler, zone, zone->apex, SEM_ERR_NSEC3PARAM_RDATA_ALG, + NULL); + } +} + +static void check_dnskey(zone_contents_t *zone, sem_handler_t *handler) +{ + const knot_rdataset_t *dnskeys = node_rdataset(zone->apex, KNOT_RRTYPE_DNSKEY); + if (dnskeys == NULL) { + handler->cb(handler, zone, zone->apex, SEM_ERR_DNSKEY_NONE, NULL); + return; + } + + for (int i = 0; i < dnskeys->count; i++) { + knot_rdata_t *dnskey = knot_rdataset_at(dnskeys, i); + dnssec_key_t *key; + int ret = dnssec_key_from_rdata(&key, zone->apex->owner, + dnskey->data, dnskey->len); + if (ret == KNOT_EOK) { + dnssec_key_free(key); + } else { + handler->cb(handler, zone, zone->apex, SEM_ERR_DNSKEY_INVALID, NULL); + } + + if (knot_dnskey_proto(dnskey) != 3) { + handler->cb(handler, zone, zone->apex, SEM_ERR_DNSKEY_RDATA_PROTOCOL, + NULL); + } + + dnssec_key_algorithm_t alg = knot_dnskey_alg(dnskey); + if (!dnssec_algorithm_key_support(alg)) { + char *info = sprintf_alloc("(unsupported algorithm %d)", alg); + handler->cb(handler, zone, zone->apex, SEM_ERR_DNSKEY_INVALID, info); + free(info); + } + } +} + +int sem_checks_process(zone_contents_t *zone, bool optional, sem_handler_t *handler, + time_t time) +{ + if (zone == NULL || handler == NULL) { + return KNOT_EINVAL; + } + + semchecks_data_t data = { + .handler = handler, + .zone = zone, + .next_nsec = zone->apex, + .level = MANDATORY, + .time = time, + }; + + if (optional) { + data.level |= OPTIONAL; + if (zone->dnssec) { + knot_rdataset_t *nsec3param = node_rdataset(zone->apex, + KNOT_RRTYPE_NSEC3PARAM); + if (nsec3param != NULL) { + data.level |= NSEC3; + check_nsec3param(nsec3param, zone, handler, &data); + } else { + data.level |= NSEC; + } + check_dnskey(zone, handler); + } + } + + int ret = zone_contents_apply(zone, do_checks_in_tree, &data); + if (ret != KNOT_EOK) { + return ret; + } + if (data.handler->fatal_error) { + return KNOT_ESEMCHECK; + } + + // check cyclic chain after every node was checked + if (data.level & NSEC) { + check_nsec_cyclic(&data); + } + if (data.handler->fatal_error) { + return KNOT_ESEMCHECK; + } + + return KNOT_EOK; +} diff --git a/src/knot/zone/semantic-check.h b/src/knot/zone/semantic-check.h new file mode 100644 index 0000000..2010ec9 --- /dev/null +++ b/src/knot/zone/semantic-check.h @@ -0,0 +1,123 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "knot/zone/node.h" +#include "knot/zone/contents.h" + +/*! + *\brief Internal error constants. + */ +typedef enum { + // Mandatory checks. + SEM_ERR_SOA_NONE, + + SEM_ERR_CNAME_EXTRA_RECORDS, + SEM_ERR_CNAME_MULTIPLE, + + SEM_ERR_DNAME_CHILDREN, + + // Optional checks. + SEM_ERR_NS_APEX, + SEM_ERR_NS_GLUE, + + SEM_ERR_RRSIG_RDATA_TYPE_COVERED, + SEM_ERR_RRSIG_RDATA_TTL, + SEM_ERR_RRSIG_RDATA_EXPIRATION, + SEM_ERR_RRSIG_RDATA_INCEPTION, + SEM_ERR_RRSIG_RDATA_LABELS, + SEM_ERR_RRSIG_RDATA_OWNER, + SEM_ERR_RRSIG_NO_RRSIG, + SEM_ERR_RRSIG_SIGNED, + SEM_ERR_RRSIG_TTL, + SEM_ERR_RRSIG_UNVERIFIABLE, + + SEM_ERR_NSEC_NONE, + SEM_ERR_NSEC_RDATA_BITMAP, + SEM_ERR_NSEC_RDATA_MULTIPLE, + SEM_ERR_NSEC_RDATA_CHAIN, + + SEM_ERR_NSEC3_NONE, + SEM_ERR_NSEC3_INSECURE_DELEGATION_OPT, + SEM_ERR_NSEC3_EXTRA_RECORD, + SEM_ERR_NSEC3_RDATA_TTL, + SEM_ERR_NSEC3_RDATA_CHAIN, + SEM_ERR_NSEC3_RDATA_BITMAP, + SEM_ERR_NSEC3_RDATA_FLAGS, + SEM_ERR_NSEC3_RDATA_SALT, + SEM_ERR_NSEC3_RDATA_ALG, + SEM_ERR_NSEC3_RDATA_ITERS, + + SEM_ERR_NSEC3PARAM_RDATA_FLAGS, + SEM_ERR_NSEC3PARAM_RDATA_ALG, + + SEM_ERR_DS_RDATA_ALG, + SEM_ERR_DS_RDATA_DIGLEN, + + SEM_ERR_DNSKEY_NONE, + SEM_ERR_DNSKEY_INVALID, + SEM_ERR_DNSKEY_RDATA_PROTOCOL, + + SEM_ERR_CDS_NONE, + SEM_ERR_CDS_MULTIPLE, + SEM_ERR_CDS_NOT_MATCH, + + SEM_ERR_CDNSKEY_NONE, + SEM_ERR_CDNSKEY_MULTIPLE, + SEM_ERR_CDNSKEY_NO_DNSKEY, + + // General error! + SEM_ERR_UNKNOWN +} sem_error_t; + +const char *sem_error_msg(sem_error_t code); + +/*! + * \brief Structure for handling semantic errors. + */ +typedef struct sem_handler sem_handler_t; + +/*! + * \brief Callback for handle error. + * + * Return KNOT_EOK to continue in semantic checks. + * Return other KNOT_E* to stop semantic check with error. + */ +typedef void (*sem_callback) (sem_handler_t *ctx, const zone_contents_t *zone, + const zone_node_t *node, sem_error_t error, const char *data); + +struct sem_handler { + sem_callback cb; + bool fatal_error; +}; + +/*! + * \brief Check zone for semantic errors. + * + * Errors are logged in error handler. + * + * \param zone Zone to be searched / checked. + * \param optional To do also optional check. + * \param handler Semantic error handler. + * \param time Check zone at given time (rrsig expiration). + * + * \retval KNOT_EOK no error found + * \retval KNOT_ESEMCHECK found semantic error + * \retval KNOT_EINVAL or other error + */ +int sem_checks_process(zone_contents_t *zone, bool optional, sem_handler_t *handler, + time_t time); diff --git a/src/knot/zone/serial.c b/src/knot/zone/serial.c new file mode 100644 index 0000000..9846f92 --- /dev/null +++ b/src/knot/zone/serial.c @@ -0,0 +1,84 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <assert.h> +#include <time.h> + +#include "knot/conf/conf.h" +#include "knot/zone/serial.h" + +static const serial_cmp_result_t diffbrief2result[4] = { + [0] = SERIAL_EQUAL, + [1] = SERIAL_GREATER, + [2] = SERIAL_INCOMPARABLE, + [3] = SERIAL_LOWER, +}; + +serial_cmp_result_t serial_compare(uint32_t s1, uint32_t s2) +{ + uint64_t diff = ((uint64_t)s1 + ((uint64_t)1 << 32) - s2) & 0xffffffff; + int diffbrief = (diff >> 31 << 1) | ((diff & 0x7fffffff) ? 1 : 0); + assert(diffbrief > -1 && diffbrief < 4); + return diffbrief2result[diffbrief]; +} + +static uint32_t serial_next_date(uint32_t current) +{ + uint32_t next = current + 1; + + struct tm now; + time_t current_time = time(NULL); + struct tm *gmtime_result = gmtime_r(¤t_time, &now); + if (gmtime_result == NULL) { + return next; + } + + uint32_t yyyyMMdd00 = (1900 + now.tm_year) * 1000000 + + ( 1 + now.tm_mon ) * 10000 + + ( now.tm_mday) * 100; + + if (next < yyyyMMdd00) { + next = yyyyMMdd00; + } + + return next; +} + +uint32_t serial_next(uint32_t current, int policy) +{ + uint32_t candidate; + switch (policy) { + case SERIAL_POLICY_INCREMENT: + return current + 1; + case SERIAL_POLICY_UNIXTIME: + candidate = time(NULL); + if (serial_compare(candidate, current) != SERIAL_GREATER) { + return current + 1; + } else { + return candidate; + } + case SERIAL_POLICY_DATESERIAL: + return serial_next_date(current); + default: + assert(0); + return 0; + } +} + +serial_cmp_result_t kserial_cmp(kserial_t a, kserial_t b) +{ + return ((a.valid && b.valid) ? serial_compare(a.serial, b.serial) : SERIAL_INCOMPARABLE); +} diff --git a/src/knot/zone/serial.h b/src/knot/zone/serial.h new file mode 100644 index 0000000..3453de8 --- /dev/null +++ b/src/knot/zone/serial.h @@ -0,0 +1,72 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include <stdbool.h> +#include <stdint.h> + +/*! + * \brief result of serial comparison. LOWER means that the first serial is lower that the second. + * + * Example: (serial_compare(a, b) & SERIAL_MASK_LEQ) means "a <= b". + */ +typedef enum { + SERIAL_INCOMPARABLE = 0x0, + SERIAL_LOWER = 0x1, + SERIAL_GREATER = 0x2, + SERIAL_EQUAL = 0x3, + SERIAL_MASK_LEQ = SERIAL_LOWER, + SERIAL_MASK_GEQ = SERIAL_GREATER, +} serial_cmp_result_t; + +/*! + * \brief Compares two zone serials. + */ +serial_cmp_result_t serial_compare(uint32_t s1, uint32_t s2); + +inline static bool serial_equal(uint32_t a, uint32_t b) +{ + return serial_compare(a, b) == SERIAL_EQUAL; +} + +/*! + * \brief Get next serial for given serial update policy. + * + * \param current Current SOA serial. + * \param policy SERIAL_POLICY_INCREMENT, SERIAL_POLICY_UNIXTIME or + * SERIAL_POLICY_DATESERIAL. + * + * \return New serial. + */ +uint32_t serial_next(uint32_t current, int policy); + +typedef struct { + uint32_t serial; + bool valid; +} kserial_t; + +/*! + * \brief Compares two kserials. + * + * If any of them is invalid, they are INCOMPARABLE. + */ +serial_cmp_result_t kserial_cmp(kserial_t a, kserial_t b); + +inline static bool kserial_equal(kserial_t a, kserial_t b) +{ + return kserial_cmp(a, b) == SERIAL_EQUAL; +} diff --git a/src/knot/zone/timers.c b/src/knot/zone/timers.c new file mode 100644 index 0000000..0ff8aed --- /dev/null +++ b/src/knot/zone/timers.c @@ -0,0 +1,288 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "knot/zone/timers.h" + +#include "contrib/wire_ctx.h" +#include "libknot/db/db.h" +#include "libknot/db/db_lmdb.h" + +/* + * # Timer database + * + * Timer database stores timestaps of events which need to be retained + * across server restarts. The key in the database is the zone name in + * wire format. The value contains serialized timers. + * + * # Serialization format + * + * The value is a sequence of timers. Each timer consists of the timer + * identifier (1 byte, unsigned integer) and timer value (8 bytes, unsigned + * integer, network order). + * + * For example, the following byte sequence: + * + * 81 00 00 00 00 57 e3 e8 0a 82 00 00 00 00 57 e3 e9 a1 + * + * Encodes the following timers: + * + * last_flush = 1474553866 + * last_refresh = 1474554273 + */ + +/** + * \brief Timer database fields identifiers. + * + * Valid ID starts with '1' in MSB to avoid conflicts with "old timers". + */ +enum timer_id { + TIMER_INVALID = 0, + TIMER_SOA_EXPIRE = 0x80, + TIMER_LAST_FLUSH, + TIMER_LAST_REFRESH, + TIMER_NEXT_REFRESH, + TIMER_LAST_RESALT, + TIMER_NEXT_PARENT_DS_Q +}; + +#define TIMER_COUNT 6 +#define TIMER_SIZE (sizeof(uint8_t) + sizeof(uint64_t)) +#define SERIALIZED_SIZE (TIMER_COUNT * TIMER_SIZE) + +/*! + * \brief Serialize timers into a binary buffer. + */ +static int serialize_timers(const zone_timers_t *timers, uint8_t *data, size_t size) +{ + if (!timers || !data || size != SERIALIZED_SIZE) { + return KNOT_EINVAL; + } + + wire_ctx_t wire = wire_ctx_init(data, size); + + wire_ctx_write_u8(&wire, TIMER_SOA_EXPIRE); + wire_ctx_write_u64(&wire, timers->soa_expire); + wire_ctx_write_u8(&wire, TIMER_LAST_FLUSH); + wire_ctx_write_u64(&wire, timers->last_flush); + wire_ctx_write_u8(&wire, TIMER_LAST_REFRESH); + wire_ctx_write_u64(&wire, timers->last_refresh); + wire_ctx_write_u8(&wire, TIMER_NEXT_REFRESH); + wire_ctx_write_u64(&wire, timers->next_refresh); + wire_ctx_write_u8(&wire, TIMER_LAST_RESALT); + wire_ctx_write_u64(&wire, timers->last_resalt); + wire_ctx_write_u8(&wire, TIMER_NEXT_PARENT_DS_Q); + wire_ctx_write_u64(&wire, timers->next_parent_ds_q); + + assert(wire.error == KNOT_EOK); + assert(wire_ctx_available(&wire) == 0); + + return KNOT_EOK; +} + +/*! + * \brief Deserialize timers from a binary buffer. + * + * \note Unknown timers are ignored. + */ +static int deserialize_timers(zone_timers_t *timers_ptr, + const uint8_t *data, size_t size) +{ + if (!timers_ptr || !data) { + return KNOT_EINVAL; + } + + zone_timers_t timers = { 0 }; + + wire_ctx_t wire = wire_ctx_init_const(data, size); + while (wire_ctx_available(&wire) >= TIMER_SIZE) { + uint8_t id = wire_ctx_read_u8(&wire); + uint64_t value = wire_ctx_read_u64(&wire); + switch (id) { + case TIMER_SOA_EXPIRE: timers.soa_expire = value; break; + case TIMER_LAST_FLUSH: timers.last_flush = value; break; + case TIMER_LAST_REFRESH: timers.last_refresh = value; break; + case TIMER_NEXT_REFRESH: timers.next_refresh = value; break; + case TIMER_LAST_RESALT: timers.last_resalt = value; break; + case TIMER_NEXT_PARENT_DS_Q: timers.next_parent_ds_q = value; break; + default: break; // ignore + } + } + + if (wire_ctx_available(&wire) != 0) { + return KNOT_EMALF; + } + + assert(wire.error == KNOT_EOK); + + *timers_ptr = timers; + return KNOT_EOK; +} + +static int txn_write_timers(knot_db_txn_t *txn, const knot_dname_t *zone, + const zone_timers_t *timers) +{ + uint8_t data[SERIALIZED_SIZE] = { 0 }; + int ret = serialize_timers(timers, data, sizeof(data)); + if (ret != KNOT_EOK) { + return ret; + } + + knot_db_val_t key = { (uint8_t *)zone, knot_dname_size(zone) }; + knot_db_val_t val = { data, sizeof(data) }; + + return knot_db_lmdb_api()->insert(txn, &key, &val, 0); +} + +static int txn_read_timers(knot_db_txn_t *txn, const knot_dname_t *zone, + zone_timers_t *timers) +{ + knot_db_val_t key = { (uint8_t *)zone, knot_dname_size(zone) }; + knot_db_val_t val = { 0 }; + int ret = knot_db_lmdb_api()->find(txn, &key, &val, 0); + if (ret != KNOT_EOK) { + return ret; + } + + return deserialize_timers(timers, val.data, val.len); +} + +int zone_timers_open(const char *path, knot_db_t **db, size_t mapsize) +{ + if (path == NULL || db == NULL) { + return KNOT_EINVAL; + } + + struct knot_db_lmdb_opts opts = KNOT_DB_LMDB_OPTS_INITIALIZER; + opts.mapsize = mapsize; + opts.path = path; + + return knot_db_lmdb_api()->init(db, NULL, &opts); +} + +void zone_timers_close(knot_db_t *db) +{ + if (db == NULL) { + return; + } + + knot_db_lmdb_api()->deinit(db); +} + +int zone_timers_read(knot_db_t *db, const knot_dname_t *zone, + zone_timers_t *timers) +{ + if (!db || !zone || !timers) { + return KNOT_EINVAL; + } + + const knot_db_api_t *db_api = knot_db_lmdb_api(); + assert(db_api); + + knot_db_txn_t txn = { 0 }; + int ret = db_api->txn_begin(db, &txn, KNOT_DB_RDONLY); + if (ret != KNOT_EOK) { + return ret; + } + + ret = txn_read_timers(&txn, zone, timers); + db_api->txn_abort(&txn); + + return ret; +} + +int zone_timers_write_begin(knot_db_t *db, knot_db_txn_t *txn) +{ + memset(txn, 0, sizeof(*txn)); + return knot_db_lmdb_api()->txn_begin(db, txn, KNOT_DB_SORTED); +} + +int zone_timers_write_end(knot_db_txn_t *txn) +{ + return knot_db_lmdb_api()->txn_commit(txn); +} + +int zone_timers_write(knot_db_t *db, const knot_dname_t *zone, + const zone_timers_t *timers, knot_db_txn_t *txn) +{ + if (!zone || !timers || (!db && !txn)) { + return KNOT_EINVAL; + } + + const knot_db_api_t *db_api = knot_db_lmdb_api(); + assert(db_api); + + knot_db_txn_t static_txn, *mytxn = txn; + if (txn == NULL) { + mytxn = &static_txn; + int ret = db_api->txn_begin(db, mytxn, KNOT_DB_SORTED); + if (ret != KNOT_EOK) { + return ret; + } + } + + int ret = txn_write_timers(mytxn, zone, timers); + if (ret != KNOT_EOK) { + db_api->txn_abort(mytxn); + return ret; + } + + if (txn == NULL) { + db_api->txn_commit(mytxn); + } + + return KNOT_EOK; +} + +int zone_timers_sweep(knot_db_t *db, sweep_cb keep_zone, void *cb_data) +{ + if (!db || !keep_zone) { + return KNOT_EINVAL; + } + + const knot_db_api_t *db_api = knot_db_lmdb_api(); + assert(db_api); + + knot_db_txn_t txn = { 0 }; + int ret = db_api->txn_begin(db, &txn, KNOT_DB_SORTED); + if (ret != KNOT_EOK) { + return ret; + } + + knot_db_iter_t *it = NULL; + for (it = db_api->iter_begin(&txn, 0); it != NULL; it = db_api->iter_next(it)) { + knot_db_val_t key = { 0 }; + ret = db_api->iter_key(it, &key); + if (ret != KNOT_EOK) { + break; + } + + const knot_dname_t *zone = (const knot_dname_t *)key.data; + if (!keep_zone(zone, cb_data)) { + ret = db_api->del(&txn, &key); + if (ret != KNOT_EOK) { + break; + } + } + } + db_api->iter_finish(it); + + if (ret != KNOT_EOK) { + db_api->txn_abort(&txn); + return ret; + } + + return db_api->txn_commit(&txn); +} diff --git a/src/knot/zone/timers.h b/src/knot/zone/timers.h new file mode 100644 index 0000000..4c8d0fa --- /dev/null +++ b/src/knot/zone/timers.h @@ -0,0 +1,119 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <stdint.h> +#include <time.h> + +#include "libknot/db/db.h" +#include "libknot/dname.h" + +/*! + * \brief Persistent zone timers. + */ +struct zone_timers { + uint32_t soa_expire; //!< SOA expire value. + time_t last_flush; //!< Last zone file synchronization. + time_t last_refresh; //!< Last successful zone refresh attempt. + time_t next_refresh; //!< Next zone refresh attempt. + time_t last_resalt; //!< Last NSEC3 resalt + time_t next_parent_ds_q; //!< Next parent ds query +}; + +typedef struct zone_timers zone_timers_t; + +/*! + * \brief Open zone timers database. + * + * \param[in] path Path to a directory with the database. + * \param[out] db Created database. + * \param[in] mapsize LMDB mapsize. + * + * \return KNOT_E* + */ +int zone_timers_open(const char *path, knot_db_t **db, size_t mapsize); + +/*! + * \brief Closes zone timers database. + * + * \param db Timer database. + */ +void zone_timers_close(knot_db_t *db); + +/*! + * \brief Load timers for one zone. + * + * \param[in] db Timer database. + * \param[in] zone Zone name. + * \param[out] timers Loaded timers + * + * \return KNOT_E* + * \retval KNOT_ENOENT Zone not found in the database. + */ +int zone_timers_read(knot_db_t *db, const knot_dname_t *zone, + zone_timers_t *timers); + +/*! + * \brief Init txn for zone_timers_write() + * + * \param db Timer database. + * \param txn Handler to be initialized. + * + * \return KNOT_E* + */ +int zone_timers_write_begin(knot_db_t *db, knot_db_txn_t *txn); + +/*! + * \brief Close txn for zone_timers_write() + * + * \param txn Handler to be closed. + * + * \return KNOT_E* + */ +int zone_timers_write_end(knot_db_txn_t *txn); + +/*! + * \brief Write timers for one zone. + * + * \param db Timer database. + * \param zone Zone name. + * \param timers Loaded timers + * \param txn Transaction handler obtained from zone_timers_write_begin() + * + * \return KNOT_E* + */ +int zone_timers_write(knot_db_t *db, const knot_dname_t *zone, + const zone_timers_t *timers, knot_db_txn_t *txn); + +/*! + * \brief Callback used in \ref zone_timers_sweep. + * + * \retval true for zones to preserve. + * \retval false for zones to remove. + */ +typedef bool (*sweep_cb)(const knot_dname_t *zone, void *data); + +/*! + * \brief Selectively delete zones from the database. + * + * \param db Timer dababase. + * \param keep_zone Filtering callback. + * \param cb_data Data passed to callback function. + * + * \return KNOT_E* + */ +int zone_timers_sweep(knot_db_t *db, sweep_cb keep_zone, void *cb_data); diff --git a/src/knot/zone/zone-diff.c b/src/knot/zone/zone-diff.c new file mode 100644 index 0000000..6022d4e --- /dev/null +++ b/src/knot/zone/zone-diff.c @@ -0,0 +1,389 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> +#include <stdlib.h> +#include <inttypes.h> + +#include "libknot/libknot.h" +#include "knot/zone/zone-diff.h" +#include "knot/zone/serial.h" + +struct zone_diff_param { + zone_tree_t *nodes; + changeset_t *changeset; + bool ignore_dnssec; +}; + +static bool rrset_is_dnssec(const knot_rrset_t *rrset) +{ + switch (rrset->type) { + case KNOT_RRTYPE_RRSIG: + case KNOT_RRTYPE_NSEC: + case KNOT_RRTYPE_NSEC3: + return true; + default: + return false; + } +} + +static int load_soas(const zone_contents_t *zone1, const zone_contents_t *zone2, + changeset_t *changeset) +{ + assert(zone1); + assert(zone2); + assert(changeset); + + const zone_node_t *apex1 = zone1->apex; + const zone_node_t *apex2 = zone2->apex; + if (apex1 == NULL || apex2 == NULL) { + return KNOT_EINVAL; + } + + knot_rrset_t soa_rrset1 = node_rrset(apex1, KNOT_RRTYPE_SOA); + knot_rrset_t soa_rrset2 = node_rrset(apex2, KNOT_RRTYPE_SOA); + if (knot_rrset_empty(&soa_rrset1) || knot_rrset_empty(&soa_rrset2)) { + return KNOT_EINVAL; + } + + if (soa_rrset1.rrs.count == 0 || + soa_rrset2.rrs.count == 0) { + return KNOT_EINVAL; + } + + uint32_t soa_serial1 = knot_soa_serial(soa_rrset1.rrs.rdata); + uint32_t soa_serial2 = knot_soa_serial(soa_rrset2.rrs.rdata); + + if (serial_compare(soa_serial1, soa_serial2) == SERIAL_EQUAL) { + return KNOT_ENODIFF; + } + + if (serial_compare(soa_serial1, soa_serial2) != SERIAL_LOWER) { + return KNOT_ERANGE; + } + + changeset->soa_from = knot_rrset_copy(&soa_rrset1, NULL); + if (changeset->soa_from == NULL) { + return KNOT_ENOMEM; + } + changeset->soa_to = knot_rrset_copy(&soa_rrset2, NULL); + if (changeset->soa_to == NULL) { + knot_rrset_free(changeset->soa_from, NULL); + return KNOT_ENOMEM; + } + + return KNOT_EOK; +} + +static int add_node(const zone_node_t *node, changeset_t *changeset, bool ignore_dnssec) +{ + /* Add all rrsets from node. */ + for (unsigned i = 0; i < node->rrset_count; i++) { + knot_rrset_t rrset = node_rrset_at(node, i); + + if (ignore_dnssec && rrset_is_dnssec(&rrset)) { + continue; + } + + int ret = changeset_add_addition(changeset, &rrset, 0); + if (ret != KNOT_EOK) { + return ret; + } + } + + return KNOT_EOK; +} + +static int remove_node(const zone_node_t *node, changeset_t *changeset, bool ignore_dnssec) +{ + /* Remove all the RRSets of the node. */ + for (unsigned i = 0; i < node->rrset_count; i++) { + knot_rrset_t rrset = node_rrset_at(node, i); + + if (ignore_dnssec && rrset_is_dnssec(&rrset)) { + continue; + } + + int ret = changeset_add_removal(changeset, &rrset, 0); + if (ret != KNOT_EOK) { + return ret; + } + } + + return KNOT_EOK; +} + +static int rdata_return_changes(const knot_rrset_t *rrset1, + const knot_rrset_t *rrset2, + knot_rrset_t *changes) +{ + if (rrset1 == NULL || rrset2 == NULL) { + return KNOT_EINVAL; + } + + /* Create fake RRSet, it will be easier to handle. */ + knot_rrset_init(changes, rrset1->owner, rrset1->type, rrset1->rclass, rrset1->ttl); + + /* + * Take one rdata from first list and search through the second list + * looking for an exact match. If no match occurs, it means that this + * particular RR has changed. + * After the list has been traversed, we have a list of + * changed/removed rdatas. This has awful computation time. + */ + bool ttl_differ = rrset1->ttl != rrset2->ttl && rrset1->type != KNOT_RRTYPE_RRSIG; + knot_rdata_t *rr1 = rrset1->rrs.rdata; + for (uint16_t i = 0; i < rrset1->rrs.count; ++i) { + if (ttl_differ || !knot_rdataset_member(&rrset2->rrs, rr1)) { + /* + * No such RR is present in 'rrset2'. We'll copy + * index 'i' into 'changes' RRSet. + */ + int ret = knot_rdataset_add(&changes->rrs, rr1, NULL); + if (ret != KNOT_EOK) { + knot_rdataset_clear(&changes->rrs, NULL); + return ret; + } + } + rr1 = knot_rdataset_next(rr1); + } + + return KNOT_EOK; +} + +static int diff_rrsets(const knot_rrset_t *rrset1, const knot_rrset_t *rrset2, + changeset_t *changeset) +{ + if (changeset == NULL || (rrset1 == NULL && rrset2 == NULL)) { + return KNOT_EINVAL; + } + /* + * The easiest solution is to remove all the RRs that had no match and + * to add all RRs that had no match, but those from second RRSet. */ + + /* Get RRs to add to zone and to remove from zone. */ + knot_rrset_t to_remove; + knot_rrset_t to_add; + if (rrset1 != NULL && rrset2 != NULL) { + int ret = rdata_return_changes(rrset1, rrset2, &to_remove); + if (ret != KNOT_EOK) { + return ret; + } + + ret = rdata_return_changes(rrset2, rrset1, &to_add); + if (ret != KNOT_EOK) { + return ret; + } + } + + if (!knot_rrset_empty(&to_remove)) { + int ret = changeset_add_removal(changeset, &to_remove, 0); + knot_rdataset_clear(&to_remove.rrs, NULL); + if (ret != KNOT_EOK) { + knot_rdataset_clear(&to_add.rrs, NULL); + return ret; + } + } + + if (!knot_rrset_empty(&to_add)) { + int ret = changeset_add_addition(changeset, &to_add, 0); + knot_rdataset_clear(&to_add.rrs, NULL); + return ret; + } + + return KNOT_EOK; +} + +/*!< \todo this could be generic function for adding / removing. */ +static int knot_zone_diff_node(zone_node_t **node_ptr, void *data) +{ + if (node_ptr == NULL || *node_ptr == NULL || data == NULL) { + return KNOT_EINVAL; + } + + zone_node_t *node = *node_ptr; + + struct zone_diff_param *param = (struct zone_diff_param *)data; + if (param->changeset == NULL) { + return KNOT_EINVAL; + } + + /* + * First, we have to search the second tree to see if there's according + * node, if not, the whole node has been removed. + */ + zone_node_t *node_in_second_tree = zone_tree_get(param->nodes, node->owner); + if (node_in_second_tree == NULL) { + return remove_node(node, param->changeset, param->ignore_dnssec); + } + + assert(node_in_second_tree != node); + + /* The nodes are in both trees, we have to diff each RRSet. */ + if (node->rrset_count == 0) { + /* + * If there are no RRs in the first tree, all of the RRs + * in the second tree will have to be inserted to ADD section. + */ + return add_node(node_in_second_tree, param->changeset, param->ignore_dnssec); + } + + for (unsigned i = 0; i < node->rrset_count; i++) { + /* Search for the RRSet in the node from the second tree. */ + knot_rrset_t rrset = node_rrset_at(node, i); + + /* SOAs are handled explicitly. */ + if (rrset.type == KNOT_RRTYPE_SOA) { + continue; + } + + if (param->ignore_dnssec && rrset_is_dnssec(&rrset)) { + continue; + } + + knot_rrset_t rrset_from_second_node = + node_rrset(node_in_second_tree, rrset.type); + if (knot_rrset_empty(&rrset_from_second_node)) { + /* RRSet has been removed. Make a copy and remove. */ + int ret = changeset_add_removal( + param->changeset, &rrset, 0); + if (ret != KNOT_EOK) { + return ret; + } + } else { + /* Diff RRSets. */ + int ret = diff_rrsets(&rrset, &rrset_from_second_node, + param->changeset); + if (ret != KNOT_EOK) { + return ret; + } + } + } + + for (unsigned i = 0; i < node_in_second_tree->rrset_count; i++) { + /* Search for the RRSet in the node from the second tree. */ + knot_rrset_t rrset = node_rrset_at(node_in_second_tree, i); + + /* SOAs are handled explicitly. */ + if (rrset.type == KNOT_RRTYPE_SOA) { + continue; + } + + if (param->ignore_dnssec && rrset_is_dnssec(&rrset)) { + continue; + } + + knot_rrset_t rrset_from_first_node = node_rrset(node, rrset.type); + if (knot_rrset_empty(&rrset_from_first_node)) { + /* RRSet has been added. Make a copy and add. */ + int ret = changeset_add_addition( + param->changeset, &rrset, 0); + if (ret != KNOT_EOK) { + return ret; + } + } + } + + return KNOT_EOK; +} + +/*!< \todo possibly not needed! */ +static int add_new_nodes(zone_node_t **node_ptr, void *data) +{ + if (node_ptr == NULL || *node_ptr == NULL || data == NULL) { + return KNOT_EINVAL; + } + + zone_node_t *node = *node_ptr; + + struct zone_diff_param *param = (struct zone_diff_param *)data; + if (param->changeset == NULL) { + return KNOT_EINVAL; + } + + /* + * If a node is not present in the second zone, it is a new node + * and has to be added to changeset. Differencies on the RRSet level are + * already handled. + */ + zone_node_t *new_node = zone_tree_get(param->nodes, node->owner); + if (new_node == NULL) { + assert(node); + return add_node(node, param->changeset, param->ignore_dnssec); + } + + return KNOT_EOK; +} + +static int load_trees(zone_tree_t *nodes1, zone_tree_t *nodes2, + changeset_t *changeset, bool ignore_dnssec) +{ + assert(changeset); + + struct zone_diff_param param = { + .changeset = changeset, + .ignore_dnssec = ignore_dnssec, + }; + + // Traverse one tree, compare every node, each RRSet with its rdata. + param.nodes = nodes2; + int ret = zone_tree_apply(nodes1, knot_zone_diff_node, ¶m); + if (ret != KNOT_EOK) { + return ret; + } + + // Some nodes may have been added. Add missing nodes to changeset. + param.nodes = nodes1; + return zone_tree_apply(nodes2, add_new_nodes, ¶m); +} + +int zone_contents_diff(const zone_contents_t *zone1, const zone_contents_t *zone2, + changeset_t *changeset, bool ignore_dnssec) +{ + if (zone1 == NULL || zone2 == NULL || changeset == NULL) { + return KNOT_EINVAL; + } + + int ret_soa = load_soas(zone1, zone2, changeset); + if (ret_soa != KNOT_EOK && ret_soa != KNOT_ENODIFF) { + return ret_soa; + } + + int ret = load_trees(zone1->nodes, zone2->nodes, changeset, ignore_dnssec); + if (ret != KNOT_EOK) { + return ret; + } + + ret = load_trees(zone1->nsec3_nodes, zone2->nsec3_nodes, changeset, ignore_dnssec); + if (ret != KNOT_EOK) { + return ret; + } + + if (ret_soa == KNOT_ENODIFF && !changeset_empty(changeset)) { + return KNOT_ESEMCHECK; + } + + return ret_soa; +} + +int zone_tree_add_diff(zone_tree_t *t1, zone_tree_t *t2, changeset_t *changeset) +{ + if (changeset == NULL) { + return KNOT_EINVAL; + } + + return load_trees(t1, t2, changeset, false); +} diff --git a/src/knot/zone/zone-diff.h b/src/knot/zone/zone-diff.h new file mode 100644 index 0000000..5f467aa --- /dev/null +++ b/src/knot/zone/zone-diff.h @@ -0,0 +1,31 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "knot/zone/contents.h" +#include "knot/updates/changesets.h" + +/*! + * \brief Create diff between two zone trees. + * */ +int zone_contents_diff(const zone_contents_t *zone1, const zone_contents_t *zone2, + changeset_t *changeset, bool ignore_dnssec); + +/*! + * \brief Add diff between two zone trees into the changeset. + */ +int zone_tree_add_diff(zone_tree_t *t1, zone_tree_t *t2, changeset_t *changeset); diff --git a/src/knot/zone/zone-dump.c b/src/knot/zone/zone-dump.c new file mode 100644 index 0000000..9729cbc --- /dev/null +++ b/src/knot/zone/zone-dump.c @@ -0,0 +1,226 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <inttypes.h> + +#include "knot/dnssec/zone-nsec.h" +#include "knot/zone/zone-dump.h" +#include "libknot/libknot.h" + +/*! \brief Size of auxiliary buffer. */ +#define DUMP_BUF_LEN (70 * 1024) + +/*! \brief Dump parameters. */ +typedef struct { + FILE *file; + char *buf; + size_t buflen; + uint64_t rr_count; + bool dump_rrsig; + bool dump_nsec; + const knot_dname_t *origin; + const knot_dump_style_t *style; + const char *first_comment; +} dump_params_t; + +static int apex_node_dump_text(zone_node_t *node, dump_params_t *params) +{ + knot_rrset_t soa = node_rrset(node, KNOT_RRTYPE_SOA); + knot_dump_style_t soa_style = *params->style; + + // Dump SOA record as a first. + if (!params->dump_nsec) { + int ret = knot_rrset_txt_dump(&soa, ¶ms->buf, ¶ms->buflen, + &soa_style); + if (ret < 0) { + return ret; + } + params->rr_count += soa.rrs.count; + fprintf(params->file, "%s", params->buf); + params->buf[0] = '\0'; + } + + // Dump other records. + for (uint16_t i = 0; i < node->rrset_count; i++) { + knot_rrset_t rrset = node_rrset_at(node, i); + switch (rrset.type) { + case KNOT_RRTYPE_NSEC: + continue; + case KNOT_RRTYPE_RRSIG: + continue; + case KNOT_RRTYPE_SOA: + continue; + default: + break; + } + + int ret = knot_rrset_txt_dump(&rrset, ¶ms->buf, ¶ms->buflen, + params->style); + if (ret < 0) { + return ret; + } + params->rr_count += rrset.rrs.count; + fprintf(params->file, "%s", params->buf); + params->buf[0] = '\0'; + } + + return KNOT_EOK; +} + +static int node_dump_text(zone_node_t *node, void *data) +{ + dump_params_t *params = (dump_params_t *)data; + + // Zone apex rrsets. + if (node->owner == params->origin && !params->dump_rrsig && + !params->dump_nsec) { + apex_node_dump_text(node, params); + return KNOT_EOK; + } + + // Dump non-apex rrsets. + for (uint16_t i = 0; i < node->rrset_count; i++) { + knot_rrset_t rrset = node_rrset_at(node, i); + switch (rrset.type) { + case KNOT_RRTYPE_RRSIG: + if (params->dump_rrsig) { + break; + } + continue; + case KNOT_RRTYPE_NSEC: + if (params->dump_nsec) { + break; + } + continue; + case KNOT_RRTYPE_NSEC3: + if (params->dump_nsec) { + break; + } + continue; + default: + if (params->dump_nsec || params->dump_rrsig) { + continue; + } + break; + } + + // Dump block comment if available. + if (params->first_comment != NULL) { + fprintf(params->file, "%s", params->first_comment); + params->first_comment = NULL; + } + + int ret = knot_rrset_txt_dump(&rrset, ¶ms->buf, ¶ms->buflen, + params->style); + if (ret < 0) { + return ret; + } + params->rr_count += rrset.rrs.count; + fprintf(params->file, "%s", params->buf); + params->buf[0] = '\0'; + } + + return KNOT_EOK; +} + +int zone_dump_text(zone_contents_t *zone, FILE *file, bool comments) +{ + if (zone == NULL || file == NULL) { + return KNOT_EINVAL; + } + + // Allocate auxiliary buffer for dumping operations. + char *buf = malloc(DUMP_BUF_LEN); + if (buf == NULL) { + return KNOT_ENOMEM; + } + + if (comments) { + fprintf(file, ";; Zone dump (Knot DNS %s)\n", PACKAGE_VERSION); + } + + // Set structure with parameters. + zone_node_t *apex = zone->apex; + dump_params_t params = { + .file = file, + .buf = buf, + .buflen = DUMP_BUF_LEN, + .rr_count = 0, + .origin = apex->owner, + .style = &KNOT_DUMP_STYLE_DEFAULT, + .dump_rrsig = false, + .dump_nsec = false + }; + + // Dump standard zone records without RRSIGS. + int ret = zone_contents_apply(zone, node_dump_text, ¶ms); + if (ret != KNOT_EOK) { + return ret; + } + + // Dump RRSIG records if available. + params.dump_rrsig = true; + params.dump_nsec = false; + params.first_comment = comments ? ";; DNSSEC signatures\n" : NULL; + ret = zone_contents_apply(zone, node_dump_text, ¶ms); + if (ret != KNOT_EOK) { + return ret; + } + + // Dump NSEC chain if available. + params.dump_rrsig = false; + params.dump_nsec = true; + params.first_comment = comments ? ";; DNSSEC NSEC chain\n" : NULL; + ret = zone_contents_apply(zone, node_dump_text, ¶ms); + if (ret != KNOT_EOK) { + return ret; + } + + // Dump NSEC3 chain if available. + params.dump_rrsig = false; + params.dump_nsec = true; + params.first_comment = comments ? ";; DNSSEC NSEC3 chain\n" : NULL; + ret = zone_contents_nsec3_apply(zone, node_dump_text, ¶ms); + if (ret != KNOT_EOK) { + return ret; + } + + params.dump_rrsig = true; + params.dump_nsec = false; + params.first_comment = comments ? ";; DNSSEC NSEC3 signatures\n" : NULL; + ret = zone_contents_nsec3_apply(zone, node_dump_text, ¶ms); + if (ret != KNOT_EOK) { + return ret; + } + + if (comments) { + // Create formatted date-time string. + time_t now = time(NULL); + struct tm tm; + localtime_r(&now, &tm); + char date[64]; + strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S %Z", &tm); + + // Dump trailing statistics. + fprintf(file, ";; Written %"PRIu64" records\n" + ";; Time %s\n", + params.rr_count, date); + } + + free(params.buf); // params.buf may be != buf because of knot_rrset_txt_dump_dynamic() + + return KNOT_EOK; +} diff --git a/src/knot/zone/zone-dump.h b/src/knot/zone/zone-dump.h new file mode 100644 index 0000000..e9d8be8 --- /dev/null +++ b/src/knot/zone/zone-dump.h @@ -0,0 +1,41 @@ +/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \file + * + * \brief Zone text dump facility. + * + * \addtogroup zone + * @{ + */ + +#pragma once + +#include "knot/zone/zone.h" + +/*! + * \brief Dumps given zone to text file. + * + * \param zone Zone to be saved. + * \param file File to write to. + * \param comments Add separating comments indicator. + * + * \retval KNOT_EOK on success. + * \retval < 0 if error. + */ +int zone_dump_text(zone_contents_t *zone, FILE *file, bool comments); + +/*! @} */ diff --git a/src/knot/zone/zone-load.c b/src/knot/zone/zone-load.c new file mode 100644 index 0000000..6b1c68e --- /dev/null +++ b/src/knot/zone/zone-load.c @@ -0,0 +1,154 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "knot/common/log.h" +#include "knot/journal/journal.h" +#include "knot/zone/zone-diff.h" +#include "knot/zone/zone-load.h" +#include "knot/zone/zonefile.h" +#include "knot/dnssec/key-events.h" +#include "knot/dnssec/zone-events.h" +#include "knot/updates/apply.h" +#include "libknot/libknot.h" + +int zone_load_contents(conf_t *conf, const knot_dname_t *zone_name, + zone_contents_t **contents) +{ + if (conf == NULL || zone_name == NULL || contents == NULL) { + return KNOT_EINVAL; + } + + char *zonefile = conf_zonefile(conf, zone_name); + conf_val_t val = conf_zone_get(conf, C_SEM_CHECKS, zone_name); + + zloader_t zl; + int ret = zonefile_open(&zl, zonefile, zone_name, conf_bool(&val), time(NULL)); + free(zonefile); + if (ret != KNOT_EOK) { + return ret; + } + + sem_handler_t handler = { + .cb = err_handler_logger + }; + + zl.err_handler = &handler; + zl.creator->master = !zone_load_can_bootstrap(conf, zone_name); + + *contents = zonefile_load(&zl); + zonefile_close(&zl); + if (*contents == NULL) { + return KNOT_ERROR; + } + + return KNOT_EOK; +} + +int zone_load_journal(conf_t *conf, zone_t *zone, zone_contents_t *contents) +{ + if (conf == NULL || zone == NULL || contents == NULL) { + return KNOT_EINVAL; + } + + /* Check if journal is used (later in zone_changes_load() and zone is not empty. */ + if (zone_contents_is_empty(contents)) { + return KNOT_EOK; + } + + /* Fetch SOA serial. */ + uint32_t serial = zone_contents_serial(contents); + + /* Load journal */ + list_t chgs; + init_list(&chgs); + int ret = zone_changes_load(conf, zone, &chgs, serial); + if (ret != KNOT_EOK && ret != KNOT_ENOENT) { + changesets_free(&chgs); + return ret; + } + if (EMPTY_LIST(chgs)) { + return KNOT_EOK; + } + + /* Apply changesets. */ + apply_ctx_t a_ctx = { 0 }; + apply_init_ctx(&a_ctx, contents, 0); + + ret = apply_changesets_directly(&a_ctx, &chgs); + if (ret == KNOT_EOK) { + log_zone_info(zone->name, "changes from journal applied %u -> %u", + serial, zone_contents_serial(contents)); + } else { + log_zone_error(zone->name, "failed to apply journal changes %u -> %u (%s)", + serial, zone_contents_serial(contents), + knot_strerror(ret)); + } + + update_cleanup(&a_ctx); + changesets_free(&chgs); + + return ret; +} + +int zone_load_from_journal(conf_t *conf, zone_t *zone, zone_contents_t **contents) +{ + if (conf == NULL || zone == NULL || contents == NULL) { + return KNOT_EINVAL; + } + + list_t chgs; + init_list(&chgs); + int ret = zone_in_journal_load(conf, zone, &chgs); + if (ret != KNOT_EOK) { + changesets_free(&chgs); + return ret; // include ENOENT, which is normal operation + } + + changeset_t *boo_ch = (changeset_t *)HEAD(chgs); + rem_node(&boo_ch->n); + ret = changeset_to_contents(boo_ch, contents); + if (ret != KNOT_EOK) { + changesets_free(&chgs); + return ret; + } + + apply_ctx_t a_ctx = { 0 }; + apply_init_ctx(&a_ctx, *contents, 0); + ret = apply_changesets_directly(&a_ctx, &chgs); + if (ret == KNOT_EOK) { + log_zone_info(zone->name, "zone loaded from journal, serial %u", + zone_contents_serial(*contents)); + } else { + log_zone_error(zone->name, "failed to load zone from journal (%s)", + knot_strerror(ret)); + } + update_cleanup(&a_ctx); + changesets_free(&chgs); + + return ret; +} + +bool zone_load_can_bootstrap(conf_t *conf, const knot_dname_t *zone_name) +{ + if (conf == NULL || zone_name == NULL) { + return false; + } + + conf_val_t val = conf_zone_get(conf, C_MASTER, zone_name); + size_t count = conf_val_count(&val); + + return count > 0; +} diff --git a/src/knot/zone/zone-load.h b/src/knot/zone/zone-load.h new file mode 100644 index 0000000..3210476 --- /dev/null +++ b/src/knot/zone/zone-load.h @@ -0,0 +1,63 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include "knot/conf/conf.h" +#include "knot/zone/zone.h" +#include "knot/dnssec/zone-events.h" // zone_sign_reschedule_t + +/*! + * \brief Load zone contents according to the configuration. + * + * \param conf + * \param zone_name + * \param contents + * \return KNOT_EOK or an error + */ +int zone_load_contents(conf_t *conf, const knot_dname_t *zone_name, + zone_contents_t **contents); + +/*! + * \brief Update zone contents from the journal. + * + * \warning If error, the zone is in inconsistent state and should be freed. + * + * \param conf + * \param zone + * \param contents + * \return KNOT_EOK or an error + */ +int zone_load_journal(conf_t *conf, zone_t *zone, zone_contents_t *contents); + +/*! + * \brief Load zone contents from journal (headless). + * + * \param conf + * \param zone + * \param contents + * \return KNOT_EOK or an error + */ +int zone_load_from_journal(conf_t *conf, zone_t *zone, + zone_contents_t **contents); + +/*! + * \brief Check if zone can be bootstrapped. + * + * \param conf + * \param zone_name + */ +bool zone_load_can_bootstrap(conf_t *conf, const knot_dname_t *zone_name); diff --git a/src/knot/zone/zone-tree.c b/src/knot/zone/zone-tree.c new file mode 100644 index 0000000..d46b5ae --- /dev/null +++ b/src/knot/zone/zone-tree.c @@ -0,0 +1,209 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> +#include <stdlib.h> + +#include "knot/zone/zone-tree.h" +#include "libknot/consts.h" +#include "libknot/errcode.h" +#include "contrib/macros.h" + +zone_tree_t *zone_tree_create(void) +{ + return trie_create(NULL); +} + +int zone_tree_insert(zone_tree_t *tree, zone_node_t *node) +{ + if (tree == NULL || node == NULL) { + return KNOT_EINVAL; + } + + assert(node->owner); + knot_dname_storage_t lf_storage; + uint8_t *lf = knot_dname_lf(node->owner, lf_storage); + assert(lf); + + *trie_get_ins(tree, (char *)lf + 1, *lf) = node; + + return KNOT_EOK; +} + +zone_node_t *zone_tree_get(zone_tree_t *tree, const knot_dname_t *owner) +{ + if (owner == NULL) { + return NULL; + } + + if (zone_tree_is_empty(tree)) { + return NULL; + } + + knot_dname_storage_t lf_storage; + uint8_t *lf = knot_dname_lf(owner, lf_storage); + assert(lf); + + trie_val_t *val = trie_get_try(tree, (char *)lf + 1, *lf); + if (val == NULL) { + return NULL; + } + + return *val; +} + +int zone_tree_get_less_or_equal(zone_tree_t *tree, + const knot_dname_t *owner, + zone_node_t **found, + zone_node_t **previous) +{ + if (owner == NULL || found == NULL || previous == NULL) { + return KNOT_EINVAL; + } + + if (zone_tree_is_empty(tree)) { + return KNOT_ENONODE; + } + + knot_dname_storage_t lf_storage; + uint8_t *lf = knot_dname_lf(owner, lf_storage); + assert(lf); + + trie_val_t *fval = NULL; + int ret = trie_get_leq(tree, (char *)lf + 1, *lf, &fval); + if (fval != NULL) { + *found = (zone_node_t *)(*fval); + } + + int exact_match = 0; + if (ret == KNOT_EOK) { + if (fval != NULL) { + *previous = (*found)->prev; + } + exact_match = 1; + } else if (ret == 1) { + *previous = *found; + *found = NULL; + } else { + /* Previous should be the rightmost node. + * For regular zone it is the node left of apex, but for some + * cases like NSEC3, there is no such sort of thing (name wise). + */ + /*! \todo We could store rightmost node in zonetree probably. */ + trie_it_t *i = trie_it_begin(tree); + *previous = *(zone_node_t **)trie_it_val(i); /* leftmost */ + *previous = (*previous)->prev; /* rightmost */ + *found = NULL; + trie_it_free(i); + } + + return exact_match; +} + +/*! \brief Removes node with the given owner from the zone tree. */ +static void remove_node(zone_tree_t *tree, const knot_dname_t *owner) +{ + assert(owner); + + if (zone_tree_is_empty(tree)) { + return; + } + + knot_dname_storage_t lf_storage; + uint8_t *lf = knot_dname_lf(owner, lf_storage); + assert(lf); + + trie_val_t *rval = trie_get_try(tree, (char *)lf + 1, *lf); + if (rval != NULL) { + trie_del(tree, (char *)lf + 1, *lf, NULL); + } +} + +/*! \brief Clears wildcard child if set in parent node. */ +static void fix_wildcard_child(zone_node_t *node, const knot_dname_t *owner) +{ + if ((node->flags & NODE_FLAGS_WILDCARD_CHILD) + && knot_dname_is_wildcard(owner)) { + node->flags &= ~NODE_FLAGS_WILDCARD_CHILD; + } +} + +void zone_tree_delete_empty(zone_tree_t *tree, zone_node_t *node) +{ + if (tree == NULL || node == NULL) { + return; + } + + if (node->rrset_count == 0 && node->children == 0) { + zone_node_t *parent_node = node->parent; + if (parent_node != NULL) { + parent_node->children--; + fix_wildcard_child(parent_node, node->owner); + if (parent_node->parent != NULL) { /* Is not apex */ + // Recurse using the parent node, do not delete possibly empty parent. + zone_tree_delete_empty(tree, parent_node); + } + } + + // Delete node + remove_node(tree, node->owner); + node_free(node, NULL); + } +} + +int zone_tree_apply(zone_tree_t *tree, zone_tree_apply_cb_t function, void *data) +{ + if (function == NULL) { + return KNOT_EINVAL; + } + + if (zone_tree_is_empty(tree)) { + return KNOT_EOK; + } + + return trie_apply(tree, (int (*)(trie_val_t *, void *))function, data); +} + +void zone_tree_free(zone_tree_t **tree) +{ + if (tree == NULL || *tree == NULL) { + return; + } + + trie_free(*tree); + *tree = NULL; +} + +static int zone_tree_free_node(zone_node_t **node, void *data) +{ + UNUSED(data); + + if (node) { + node_free(*node, NULL); + } + + return KNOT_EOK; +} + +void zone_tree_deep_free(zone_tree_t **tree) +{ + if (tree == NULL || *tree == NULL) { + return; + } + + (void)zone_tree_apply(*tree, zone_tree_free_node, NULL); + zone_tree_free(tree); +} diff --git a/src/knot/zone/zone-tree.h b/src/knot/zone/zone-tree.h new file mode 100644 index 0000000..d3cc909 --- /dev/null +++ b/src/knot/zone/zone-tree.h @@ -0,0 +1,142 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "contrib/qp-trie/trie.h" +#include "knot/zone/node.h" + +typedef trie_t zone_tree_t; + +/*! + * \brief Signature of callback for zone apply functions. + */ +typedef int (*zone_tree_apply_cb_t)(zone_node_t **node, void *data); + +/*! + * \brief Creates the zone tree. + * + * \return created zone tree structure. + */ +zone_tree_t *zone_tree_create(void); + +/*! + * \brief Return number of nodes in the zone tree. + * + * \param tree Zone tree. + * + * \return number of nodes in tree. + */ +inline static size_t zone_tree_count(const zone_tree_t *tree) +{ + if (tree == NULL) { + return 0; + } + + return trie_weight(tree); +} + +/*! + * \brief Checks if the zone tree is empty. + * + * \param tree Zone tree to check. + * + * \return Nonzero if the zone tree is empty. + */ +inline static bool zone_tree_is_empty(const zone_tree_t *tree) +{ + return zone_tree_count(tree) == 0; +} + +/*! + * \brief Inserts the given node into the zone tree. + * + * \param tree Zone tree to insert the node into. + * \param node Node to insert. + * + * \retval KNOT_EOK + * \retval KNOT_EINVAL + * \retval KNOT_ENOMEM + */ +int zone_tree_insert(zone_tree_t *tree, zone_node_t *node); + +/*! + * \brief Finds node with the given owner in the zone tree. + * + * \param tree Zone tree to search in. + * \param owner Owner of the node to find. + * + * \retval Found node or NULL. + */ +zone_node_t *zone_tree_get(zone_tree_t *tree, const knot_dname_t *owner); + +/*! + * \brief Tries to find the given domain name in the zone tree and returns the + * associated node and previous node in canonical order. + * + * \param tree Zone to search in. + * \param owner Owner of the node to find. + * \param found Found node. + * \param previous Previous node in canonical order (i.e. the one directly + * preceding \a owner in canonical order, regardless if the name + * is in the zone or not). + * + * \retval > 0 if the domain name was found. In such case \a found holds the + * zone node with \a owner as its owner. + * \a previous is set properly. + * \retval 0 if the domain name was not found. \a found may hold any (or none) + * node. \a previous is set properly. + * \retval KNOT_EINVAL + * \retval KNOT_ENOMEM + */ +int zone_tree_get_less_or_equal(zone_tree_t *tree, + const knot_dname_t *owner, + zone_node_t **found, + zone_node_t **previous); + +/*! + * \brief Delete a node that has no RRSets and no children. + * + * \param tree The tree to remove from. + * \param node The node to remove. + */ +void zone_tree_delete_empty(zone_tree_t *tree, zone_node_t *node); + +/*! + * \brief Applies the given function to each node in the zone in order. + * + * \param tree Zone tree to apply the function to. + * \param function Function to be applied to each node of the zone. + * \param data Arbitrary data to be passed to the function. + * + * \retval KNOT_EOK + * \retval KNOT_EINVAL + */ +int zone_tree_apply(zone_tree_t *tree, zone_tree_apply_cb_t function, void *data); + +/*! + * \brief Destroys the zone tree, not touching the saved data. + * + * \param tree Zone tree to be destroyed. + */ +void zone_tree_free(zone_tree_t **tree); + +/*! + * \brief Destroys the zone tree, together with the saved data. + * + * \param tree Zone tree to be destroyed. + */ +void zone_tree_deep_free(zone_tree_t **tree); diff --git a/src/knot/zone/zone.c b/src/knot/zone/zone.c new file mode 100644 index 0000000..efc0caa --- /dev/null +++ b/src/knot/zone/zone.c @@ -0,0 +1,731 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <urcu.h> + +#include "knot/common/log.h" +#include "knot/conf/module.h" +#include "knot/dnssec/kasp/kasp_db.h" +#include "knot/nameserver/process_query.h" +#include "knot/query/requestor.h" +#include "knot/updates/zone-update.h" +#include "knot/zone/contents.h" +#include "knot/zone/serial.h" +#include "knot/zone/zone.h" +#include "knot/zone/zonefile.h" +#include "libknot/libknot.h" +#include "contrib/sockaddr.h" +#include "contrib/trim.h" +#include "contrib/mempattern.h" +#include "contrib/ucw/lists.h" +#include "contrib/ucw/mempool.h" + +#define JOURNAL_LOCK_MUTEX (&zone->journal_lock) +#define JOURNAL_LOCK_RW pthread_mutex_lock(JOURNAL_LOCK_MUTEX); +#define JOURNAL_UNLOCK_RW pthread_mutex_unlock(JOURNAL_LOCK_MUTEX); + +static void free_ddns_queue(zone_t *zone) +{ + ptrnode_t *node = NULL, *nxt = NULL; + WALK_LIST_DELSAFE(node, nxt, zone->ddns_queue) { + knot_request_free(node->d, NULL); + } + ptrlist_free(&zone->ddns_queue, NULL); +} + +/*! \brief Open journal for zone. */ +static int open_journal(zone_t *zone) +{ + assert(zone); + + int ret = journal_open(zone->journal, zone->journal_db, zone->name); + if (ret != KNOT_EOK) { + log_zone_error(zone->name, "failed to open journal '%s'", + (*zone->journal_db)->path); + } + + return ret; +} + +/*! \brief Close the zone journal. */ +static void close_journal(zone_t *zone) +{ + assert(zone); + journal_close(zone->journal); +} + +/*! + * \param allow_empty_zone useful when need to flush journal but zone is not yet loaded + * ...in this case we actually don't have to do anything because the zonefile is current, + * but we must mark the journal as flushed + */ +static int flush_journal(conf_t *conf, zone_t *zone, bool allow_empty_zone) +{ + /*! @note Function expects nobody will change zone contents meanwile. */ + + assert(zone); + + int ret = KNOT_EOK; + + bool force = zone->flags & ZONE_FORCE_FLUSH; + zone->flags &= ~ZONE_FORCE_FLUSH; + + if (zone_contents_is_empty(zone->contents)) { + if (allow_empty_zone && zone->journal && journal_exists(zone->journal_db, zone->name)) { + ret = journal_flush(zone->journal); + } else { + ret = KNOT_EINVAL; + } + goto flush_journal_replan; + } + + /* Check for disabled zonefile synchronization. */ + conf_val_t val = conf_zone_get(conf, C_ZONEFILE_SYNC, zone->name); + if (conf_int(&val) < 0 && !force) { + log_zone_warning(zone->name, "zonefile synchronization disabled, " + "use force command to override it"); + return KNOT_EOK; + } + + /* Check for updated zone. */ + zone_contents_t *contents = zone->contents; + uint32_t serial_to = zone_contents_serial(contents); + if (!force && zone->zonefile.exists && zone->zonefile.serial == serial_to && + !zone->zonefile.resigned) { + ret = KNOT_EOK; /* No differences. */ + goto flush_journal_replan; + } + + char *zonefile = conf_zonefile(conf, zone->name); + + /* Synchronize journal. */ + ret = zonefile_write(zonefile, contents); + if (ret != KNOT_EOK) { + log_zone_warning(zone->name, "failed to update zone file (%s)", + knot_strerror(ret)); + free(zonefile); + goto flush_journal_replan; + } + + if (zone->zonefile.exists) { + log_zone_info(zone->name, "zone file updated, serial %u -> %u", + zone->zonefile.serial, serial_to); + } else { + log_zone_info(zone->name, "zone file updated, serial %u", + serial_to); + } + + /* Update zone version. */ + struct stat st; + if (stat(zonefile, &st) < 0) { + log_zone_warning(zone->name, "failed to update zone file (%s)", + knot_strerror(knot_map_errno())); + free(zonefile); + ret = KNOT_EACCES; + goto flush_journal_replan; + } + + free(zonefile); + + /* Update zone file attributes. */ + zone->zonefile.exists = true; + zone->zonefile.mtime = st.st_mtime; + zone->zonefile.serial = serial_to; + zone->zonefile.resigned = false; + + /* Flush journal. */ + if (zone->journal && journal_exists(zone->journal_db, zone->name)) { + ret = open_journal(zone); + if (ret != KNOT_EOK) { + goto flush_journal_replan; + } + + ret = journal_flush(zone->journal); + if (ret != KNOT_EOK) { + goto flush_journal_replan; + } + } + + /* Trim extra heap. */ + mem_trim(); + +flush_journal_replan: + /* Plan next journal flush after proper period. */ + zone->timers.last_flush = time(NULL); + val = conf_zone_get(conf, C_ZONEFILE_SYNC, zone->name); + int64_t sync_timeout = conf_int(&val); + if (sync_timeout > 0) { + time_t next_flush = zone->timers.last_flush + sync_timeout; + zone_events_schedule_at(zone, ZONE_EVENT_FLUSH, 0, + ZONE_EVENT_FLUSH, next_flush); + } + + return ret; +} + +zone_t* zone_new(const knot_dname_t *name) +{ + zone_t *zone = malloc(sizeof(zone_t)); + if (zone == NULL) { + return NULL; + } + memset(zone, 0, sizeof(zone_t)); + + zone->name = knot_dname_copy(name, NULL); + if (zone->name == NULL) { + free(zone); + return NULL; + } + + // Journal + zone->journal = journal_new(); + if (zone->journal == NULL) { + knot_dname_free(zone->name, NULL); + free(zone); + return NULL; + } + + // DDNS + pthread_mutex_init(&zone->ddns_lock, NULL); + zone->ddns_queue_size = 0; + init_list(&zone->ddns_queue); + + // Journal lock + pthread_mutex_init(&zone->journal_lock, NULL); + + // Preferred master lock + pthread_mutex_init(&zone->preferred_lock, NULL); + + // Initialize events + zone_events_init(zone); + + // Initialize query modules list. + init_list(&zone->query_modules); + + return zone; +} + +void zone_control_clear(zone_t *zone) +{ + if (zone == NULL) { + return; + } + + zone_update_clear(zone->control_update); + free(zone->control_update); + zone->control_update = NULL; +} + +void zone_free(zone_t **zone_ptr) +{ + if (zone_ptr == NULL || *zone_ptr == NULL) { + return; + } + + zone_t *zone = *zone_ptr; + + close_journal(zone); + + zone_events_deinit(zone); + + knot_dname_free(zone->name, NULL); + + journal_free(&zone->journal); + + free_ddns_queue(zone); + pthread_mutex_destroy(&zone->ddns_lock); + pthread_mutex_destroy(&zone->journal_lock); + + /* Control update. */ + zone_control_clear(zone); + + /* Free preferred master. */ + pthread_mutex_destroy(&zone->preferred_lock); + free(zone->preferred_master); + + /* Free zone contents. */ + zone_contents_deep_free(zone->contents); + + conf_deactivate_modules(&zone->query_modules, &zone->query_plan); + + free(zone); + *zone_ptr = NULL; +} + +int zone_change_store(conf_t *conf, zone_t *zone, changeset_t *change) +{ + if (conf == NULL || zone == NULL || change == NULL) { + return KNOT_EINVAL; + } + + JOURNAL_LOCK_RW + + int ret = open_journal(zone); + if (ret == KNOT_EOK) { + ret = journal_store_changeset(zone->journal, change); + if (ret == KNOT_EBUSY) { + log_zone_notice(zone->name, "journal is full, flushing"); + + /* Transaction rolled back, journal released, we may flush. */ + ret = flush_journal(conf, zone, true); + if (ret == KNOT_EOK) { + ret = journal_store_changeset(zone->journal, change); + } + } + } + + JOURNAL_UNLOCK_RW + + return ret; +} + +int zone_changes_clear(conf_t *conf, zone_t *zone) +{ + if (conf == NULL || zone == NULL) { + return KNOT_EINVAL; + } + + JOURNAL_LOCK_RW + + int ret = KNOT_EOK; + + if (journal_exists(zone->journal_db, zone->name)) { + ret = open_journal(zone); + if (ret == KNOT_EOK) { + ret = journal_drop_changesets(zone->journal); + } + } + + JOURNAL_UNLOCK_RW + + return ret; +} + +int zone_changes_load(conf_t *conf, zone_t *zone, list_t *dst, uint32_t from) +{ + if (conf == NULL || zone == NULL || dst == NULL) { + return KNOT_EINVAL; + } + + int ret = KNOT_ENOENT; + + if (journal_exists(zone->journal_db, zone->name)) { + ret = open_journal(zone); + } + + if (ret == KNOT_EOK) { + ret = journal_load_changesets(zone->journal, dst, from); + } + + return ret; +} + +int zone_chgset_ctx_load(conf_t *conf, zone_t *zone, chgset_ctx_list_t *dst, uint32_t from) +{ + if (conf == NULL || zone == NULL || dst == NULL) { + return KNOT_EINVAL; + } + + int ret = KNOT_ENOENT; + + if (journal_exists(zone->journal_db, zone->name)) { + ret = open_journal(zone); + } + + if (ret == KNOT_EOK) { + ret = journal_load_chgset_ctx(zone->journal, dst, from); + } + + return ret; +} + +int zone_in_journal_load(conf_t *conf, zone_t *zone, list_t *dst) +{ + if (conf == NULL || zone == NULL || dst == NULL) { + return KNOT_EINVAL; + } + + int ret = KNOT_ENOENT; + + if (journal_exists(zone->journal_db, zone->name)) { + ret = open_journal(zone); + } + + if (ret == KNOT_EOK) { + ret = journal_load_bootstrap(zone->journal, dst); + } + + return ret; +} + +int zone_in_journal_store(conf_t *conf, zone_t *zone, zone_contents_t *new_contents) +{ + if (conf == NULL || zone == NULL || new_contents == NULL) { + return KNOT_EINVAL; + } + + changeset_t *co_ch = changeset_from_contents(new_contents); + int ret = co_ch ? zone_change_store(conf, zone, co_ch) : KNOT_ENOMEM; + changeset_from_contents_free(co_ch); + + if (ret == KNOT_EOK) { + log_zone_info(zone->name, "zone stored to journal, serial %u", + zone_contents_serial(new_contents)); + } + + return ret; +} + +int zone_flush_journal(conf_t *conf, zone_t *zone) +{ + if (conf == NULL || zone == NULL) { + return KNOT_EINVAL; + } + + JOURNAL_LOCK_RW + + // NO open_journal() here. + + int ret = flush_journal(conf, zone, false); + + JOURNAL_UNLOCK_RW + + return ret; +} + +int zone_journal_serial(conf_t *conf, zone_t *zone, bool *is_empty, uint32_t *serial_to) +{ + if (conf == NULL || zone == NULL || is_empty == NULL || serial_to == NULL) { + return KNOT_EINVAL; + } + + int ret = open_journal(zone); + if (ret == KNOT_EOK) { + kserial_t ks; + journal_metadata_info(zone->journal, is_empty, NULL, NULL, NULL, &ks, NULL, NULL); + *serial_to = (ks.valid ? ks.serial : 0); + } + + return ret; +} + +zone_contents_t *zone_switch_contents(zone_t *zone, zone_contents_t *new_contents) +{ + if (zone == NULL) { + return NULL; + } + + zone_contents_t *old_contents; + zone_contents_t **current_contents = &zone->contents; + old_contents = rcu_xchg_pointer(current_contents, new_contents); + + return old_contents; +} + +bool zone_is_slave(conf_t *conf, const zone_t *zone) +{ + if (conf == NULL || zone == NULL) { + return false; + } + + conf_val_t val = conf_zone_get(conf, C_MASTER, zone->name); + return conf_val_count(&val) > 0 ? true : false; +} + +void zone_set_preferred_master(zone_t *zone, const struct sockaddr_storage *addr) +{ + if (zone == NULL || addr == NULL) { + return; + } + + pthread_mutex_lock(&zone->preferred_lock); + free(zone->preferred_master); + zone->preferred_master = malloc(sizeof(struct sockaddr_storage)); + *zone->preferred_master = *addr; + pthread_mutex_unlock(&zone->preferred_lock); +} + +void zone_clear_preferred_master(zone_t *zone) +{ + if (zone == NULL) { + return; + } + + pthread_mutex_lock(&zone->preferred_lock); + free(zone->preferred_master); + zone->preferred_master = NULL; + pthread_mutex_unlock(&zone->preferred_lock); +} + +const knot_rdataset_t *zone_soa(const zone_t *zone) +{ + if (!zone || zone_contents_is_empty(zone->contents)) { + return NULL; + } + + return node_rdataset(zone->contents->apex, KNOT_RRTYPE_SOA); +} + +bool zone_expired(const zone_t *zone) +{ + if (!zone) { + return false; + } + + const zone_timers_t *timers = &zone->timers; + + return timers->last_refresh > 0 && timers->soa_expire > 0 && + timers->last_refresh + timers->soa_expire <= time(NULL); +} + +/*! + * \brief Get preferred zone master while checking its existence. + */ +int static preferred_master(conf_t *conf, zone_t *zone, conf_remote_t *master) +{ + pthread_mutex_lock(&zone->preferred_lock); + + if (zone->preferred_master == NULL) { + pthread_mutex_unlock(&zone->preferred_lock); + return KNOT_ENOENT; + } + + conf_val_t masters = conf_zone_get(conf, C_MASTER, zone->name); + while (masters.code == KNOT_EOK) { + conf_val_t addr = conf_id_get(conf, C_RMT, C_ADDR, &masters); + size_t addr_count = conf_val_count(&addr); + + for (size_t i = 0; i < addr_count; i++) { + conf_remote_t remote = conf_remote(conf, &masters, i); + if (sockaddr_net_match((struct sockaddr *)&remote.addr, + (struct sockaddr *)zone->preferred_master, + -1)) { + *master = remote; + pthread_mutex_unlock(&zone->preferred_lock); + return KNOT_EOK; + } + } + + conf_val_next(&masters); + } + + pthread_mutex_unlock(&zone->preferred_lock); + + return KNOT_ENOENT; +} + +int zone_master_try(conf_t *conf, zone_t *zone, zone_master_cb callback, + void *callback_data, const char *err_str) +{ + if (conf == NULL || zone == NULL || callback == NULL || err_str == NULL) { + return KNOT_EINVAL; + } + + /* Try the preferred server. */ + + conf_remote_t preferred = { { AF_UNSPEC } }; + if (preferred_master(conf, zone, &preferred) == KNOT_EOK) { + int ret = callback(conf, zone, &preferred, callback_data); + if (ret == KNOT_EOK) { + return ret; + } + } + + /* Try all the other servers. */ + + bool success = false; + + conf_val_t masters = conf_zone_get(conf, C_MASTER, zone->name); + while (masters.code == KNOT_EOK) { + conf_val_t addr = conf_id_get(conf, C_RMT, C_ADDR, &masters); + size_t addr_count = conf_val_count(&addr); + + for (size_t i = 0; i < addr_count; i++) { + conf_remote_t master = conf_remote(conf, &masters, i); + if (preferred.addr.ss_family != AF_UNSPEC && + sockaddr_net_match((struct sockaddr *)&master.addr, + (struct sockaddr *)&preferred.addr, + -1)) { + preferred.addr.ss_family = AF_UNSPEC; + continue; + } + + int ret = callback(conf, zone, &master, callback_data); + if (ret == KNOT_EOK) { + success = true; + break; + } + + char addr_str[SOCKADDR_STRLEN] = { 0 }; + sockaddr_tostr(addr_str, sizeof(addr_str), + (struct sockaddr *)&master.addr); + log_zone_debug(zone->name, "%s, remote %s, address %s, failed (%s)", + err_str, conf_str(&masters), addr_str, + knot_strerror(ret)); + } + + if (!success) { + log_zone_warning(zone->name, "%s, remote %s not usable", + err_str, conf_str(&masters)); + } + + conf_val_next(&masters); + } + + return success ? KNOT_EOK : KNOT_ENOMASTER; +} + +int zone_update_enqueue(zone_t *zone, knot_pkt_t *pkt, knotd_qdata_params_t *params) +{ + if (zone == NULL || pkt == NULL || params == NULL) { + return KNOT_EINVAL; + } + + /* Create serialized request. */ + struct knot_request *req = malloc(sizeof(struct knot_request)); + if (req == NULL) { + return KNOT_ENOMEM; + } + memset(req, 0, sizeof(struct knot_request)); + + /* Copy socket and request. */ + req->fd = dup(params->socket); + memcpy(&req->remote, params->remote, sizeof(struct sockaddr_storage)); + + req->query = knot_pkt_new(NULL, pkt->max_size, NULL); + int ret = knot_pkt_copy(req->query, pkt); + if (ret != KNOT_EOK) { + knot_pkt_free(req->query); + free(req); + return ret; + } + + pthread_mutex_lock(&zone->ddns_lock); + + /* Enqueue created request. */ + ptrlist_add(&zone->ddns_queue, req, NULL); + ++zone->ddns_queue_size; + + pthread_mutex_unlock(&zone->ddns_lock); + + /* Schedule UPDATE event. */ + zone_events_schedule_now(zone, ZONE_EVENT_UPDATE); + + return KNOT_EOK; +} + +size_t zone_update_dequeue(zone_t *zone, list_t *updates) +{ + if (zone == NULL || updates == NULL) { + return 0; + } + + pthread_mutex_lock(&zone->ddns_lock); + if (EMPTY_LIST(zone->ddns_queue)) { + /* Lost race during reload. */ + pthread_mutex_unlock(&zone->ddns_lock); + return 0; + } + + *updates = zone->ddns_queue; + size_t update_count = zone->ddns_queue_size; + init_list(&zone->ddns_queue); + zone->ddns_queue_size = 0; + + pthread_mutex_unlock(&zone->ddns_lock); + + return update_count; +} + +int zone_dump_to_dir(conf_t *conf, zone_t *zone, const char *dir) +{ + if (zone == NULL || dir == NULL) { + return KNOT_EINVAL; + } + + size_t dir_len = strlen(dir); + if (dir_len == 0) { + return KNOT_EINVAL; + } + + char *zonefile = conf_zonefile(conf, zone->name); + char *zonefile_basename = strrchr(zonefile, '/'); + if (zonefile_basename == NULL) { + zonefile_basename = zonefile; + } + + size_t target_length = strlen(zonefile_basename) + dir_len + 2; + char target[target_length]; + (void)snprintf(target, target_length, "%s/%s", dir, zonefile_basename); + if (strcmp(target, zonefile) == 0) { + free(zonefile); + return KNOT_EDENIED; + } + free(zonefile); + + return zonefile_write(target, zone->contents); +} + +int zone_set_master_serial(zone_t *zone, uint32_t serial) +{ + int ret = kasp_db_open(*kaspdb()); + if (ret == KNOT_EOK) { + ret = kasp_db_store_serial(*kaspdb(), zone->name, KASPDB_SERIAL_MASTER, serial); + } + return ret; +} + +int zone_get_master_serial(zone_t *zone, uint32_t *serial) +{ + if (!kasp_db_exists(*kaspdb())) { + *serial = zone_contents_serial(zone->contents); + return KNOT_EOK; + } + int ret = kasp_db_open(*kaspdb()); + if (ret != KNOT_EOK) { + return ret; + } + ret = kasp_db_load_serial(*kaspdb(), zone->name, KASPDB_SERIAL_MASTER, serial); + if (ret == KNOT_ENOENT) { + *serial = zone_contents_serial(zone->contents); + return KNOT_EOK; + } + return ret; +} + +int zone_set_lastsigned_serial(zone_t *zone, uint32_t serial) +{ + int ret = kasp_db_open(*kaspdb()); + if (ret == KNOT_EOK) { + ret = kasp_db_store_serial(*kaspdb(), zone->name, KASPDB_SERIAL_LASTSIGNED, serial); + } + return ret; +} + +bool zone_get_lastsigned_serial(zone_t *zone, uint32_t *serial) +{ + if (!kasp_db_exists(*kaspdb())) { + return false; + } + int ret = kasp_db_open(*kaspdb()); + if (ret == KNOT_EOK) { + ret = kasp_db_load_serial(*kaspdb(), zone->name, KASPDB_SERIAL_LASTSIGNED, serial); + } + return (ret == KNOT_EOK); +} diff --git a/src/knot/zone/zone.h b/src/knot/zone/zone.h new file mode 100644 index 0000000..360e222 --- /dev/null +++ b/src/knot/zone/zone.h @@ -0,0 +1,177 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "knot/conf/conf.h" +#include "knot/conf/confio.h" +#include "knot/journal/journal.h" +#include "knot/events/events.h" +#include "knot/zone/contents.h" +#include "knot/zone/timers.h" +#include "libknot/dname.h" +#include "libknot/packet/pkt.h" + +struct zone_update; + +/*! + * \brief Zone flags. + */ +typedef enum zone_flag_t { + ZONE_FORCE_AXFR = 1 << 0, /* Force AXFR as next transfer. */ + ZONE_FORCE_RESIGN = 1 << 1, /* Force zone re-sign. */ + ZONE_FORCE_FLUSH = 1 << 2, /* Force zone flush. */ +} zone_flag_t; + +/*! + * \brief Structure for holding DNS zone. + */ +typedef struct zone +{ + knot_dname_t *name; + zone_contents_t *contents; + zone_flag_t flags; + + /*! \brief Dynamic configuration zone change type. */ + conf_io_type_t change_type; + + /*! \brief Zonefile parameters. */ + struct { + time_t mtime; + uint32_t serial; + bool exists; + bool resigned; + } zonefile; + + /*! \brief Zone events. */ + zone_timers_t timers; //!< Persistent zone timers. + zone_events_t events; //!< Zone events timers. + + /*! \brief DDNS queue and lock. */ + pthread_mutex_t ddns_lock; + size_t ddns_queue_size; + list_t ddns_queue; + + /*! \brief Control update context. */ + struct zone_update *control_update; + + /*! \brief Journal structure. */ + journal_t *journal; + + /*! \brief Journal access lock. */ + pthread_mutex_t journal_lock; + + /*! \brief Ptr to journal DB (in struct server) */ + journal_db_t **journal_db; + + /*! \brief Preferred master lock. */ + pthread_mutex_t preferred_lock; + /*! \brief Preferred master for remote operation. */ + struct sockaddr_storage *preferred_master; + + /*! \brief Query modules. */ + list_t query_modules; + struct query_plan *query_plan; +} zone_t; + +/*! + * \brief Creates new zone with emtpy zone content. + * + * \param name Zone name. + * + * \return The initialized zone structure or NULL if an error occurred. + */ +zone_t* zone_new(const knot_dname_t *name); + +/*! + * \brief Deallocates the zone structure. + * + * \note The function also deallocates all bound structures (contents, etc.). + * + * \param zone_ptr Zone to be freed. + */ +void zone_free(zone_t **zone_ptr); + +/*! + * \brief Clears possible control update transaction. + * + * \param zone Zone to be cleared. + */ +void zone_control_clear(zone_t *zone); + +int zone_change_store(conf_t *conf, zone_t *zone, changeset_t *change); +int zone_changes_clear(conf_t *conf, zone_t *zone); +int zone_changes_load(conf_t *conf, zone_t *zone, list_t *dst, uint32_t from); +int zone_chgset_ctx_load(conf_t *conf, zone_t *zone, chgset_ctx_list_t *dst, uint32_t from); +int zone_in_journal_load(conf_t *conf, zone_t *zone, list_t *dst); +int zone_in_journal_store(conf_t *conf, zone_t *zone, zone_contents_t *new_contents); +int zone_journal_serial(conf_t *conf, zone_t *zone, bool *is_empty, uint32_t *serial_to); + +/*! \brief Synchronize zone file with journal. */ +int zone_flush_journal(conf_t *conf, zone_t *zone); + +/*! + * \brief Atomically switch the content of the zone. + */ +zone_contents_t *zone_switch_contents(zone_t *zone, zone_contents_t *new_contents); + +/*! \brief Checks if the zone is slave. */ +bool zone_is_slave(conf_t *conf, const zone_t *zone); + +/*! \brief Sets the address as a preferred master address. */ +void zone_set_preferred_master(zone_t *zone, const struct sockaddr_storage *addr); + +/*! \brief Clears the current preferred master address. */ +void zone_clear_preferred_master(zone_t *zone); + +/*! \brief Get zone SOA RR. */ +const knot_rdataset_t *zone_soa(const zone_t *zone); + +/*! \brief Check if zone is expired according to timers. */ +bool zone_expired(const zone_t *zone); + +typedef int (*zone_master_cb)(conf_t *conf, zone_t *zone, const conf_remote_t *remote, + void *data); + +/*! + * \brief Perform an action with a first working master server. + * + * The function iterates over available masters. For each master, the callback + * function is called. If the callback function succeeds (\ref KNOT_EOK is + * returned), the iteration is terminated. + * + * \return Error code from the last callback. + */ +int zone_master_try(conf_t *conf, zone_t *zone, zone_master_cb callback, + void *callback_data, const char *err_str); + + +/*! \brief Enqueue UPDATE request for processing. */ +int zone_update_enqueue(zone_t *zone, knot_pkt_t *pkt, knotd_qdata_params_t *params); + +/*! \brief Dequeue UPDATE request. Returns number of queued updates. */ +size_t zone_update_dequeue(zone_t *zone, list_t *updates); + +/*! \brief Write zone contents to zonefile, but into different directory. */ +int zone_dump_to_dir(conf_t *conf, zone_t *zone, const char *dir); + +int zone_set_master_serial(zone_t *zone, uint32_t serial); + +int zone_get_master_serial(zone_t *zone, uint32_t *serial); + +int zone_set_lastsigned_serial(zone_t *zone, uint32_t serial); + +bool zone_get_lastsigned_serial(zone_t *zone, uint32_t *serial); diff --git a/src/knot/zone/zonedb-load.c b/src/knot/zone/zonedb-load.c new file mode 100644 index 0000000..a6e9834 --- /dev/null +++ b/src/knot/zone/zonedb-load.c @@ -0,0 +1,346 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <assert.h> +#include <urcu.h> + +#include "knot/common/log.h" +#include "knot/conf/module.h" +#include "knot/events/replan.h" +#include "knot/zone/timers.h" +#include "knot/zone/zone-load.h" +#include "knot/zone/zone.h" +#include "knot/zone/zonedb-load.h" +#include "knot/zone/zonedb.h" +#include "knot/zone/zonefile.h" +#include "libknot/libknot.h" + +static bool zone_file_updated(conf_t *conf, const zone_t *old_zone, + const knot_dname_t *zone_name) +{ + assert(conf); + assert(zone_name); + + char *zonefile = conf_zonefile(conf, zone_name); + time_t mtime; + int ret = zonefile_exists(zonefile, &mtime); + free(zonefile); + + return (ret == KNOT_EOK && old_zone != NULL && + !(old_zone->zonefile.exists && old_zone->zonefile.mtime == mtime)); +} + +static zone_t *create_zone_from(const knot_dname_t *name, server_t *server) +{ + zone_t *zone = zone_new(name); + if (!zone) { + return NULL; + } + + zone->journal_db = &server->journal_db; + + int result = zone_events_setup(zone, server->workers, &server->sched, + server->timers_db); + if (result != KNOT_EOK) { + zone_free(&zone); + return NULL; + } + + return zone; +} + +/*! + * \brief Set timer if unset (value is 0). + */ +static void time_set_default(time_t *time, time_t value) +{ + assert(time); + + if (*time == 0) { + *time = value; + } +} + +/*! + * \brief Set default timers for new zones or invalidate if not valid. + */ +static void timers_sanitize(conf_t *conf, zone_t *zone) +{ + assert(conf); + assert(zone); + + time_t now = time(NULL); + + // replace SOA expire if we have better knowledge + if (!zone_contents_is_empty(zone->contents)) { + const knot_rdataset_t *soa = zone_soa(zone); + zone->timers.soa_expire = knot_soa_expire(soa->rdata); + } + + // assume now if we don't know when we flushed + time_set_default(&zone->timers.last_flush, now); + + if (zone_is_slave(conf, zone)) { + // assume now if we don't know + time_set_default(&zone->timers.last_refresh, now); + time_set_default(&zone->timers.next_refresh, now); + } else { + // invalidate if we don't have a master + zone->timers.last_refresh = 0; + zone->timers.next_refresh = 0; + } +} + +static zone_t *create_zone_reload(conf_t *conf, const knot_dname_t *name, + server_t *server, zone_t *old_zone) +{ + zone_t *zone = create_zone_from(name, server); + if (!zone) { + return NULL; + } + + zone->contents = old_zone->contents; + + zone->timers = old_zone->timers; + timers_sanitize(conf, zone); + + if (zone_file_updated(conf, old_zone, name) && !zone_expired(zone)) { + replan_load_updated(zone, old_zone); + } else { + zone->zonefile = old_zone->zonefile; + replan_load_current(conf, zone, old_zone); + } + + if (old_zone->control_update != NULL) { + log_zone_warning(old_zone->name, "control transaction aborted"); + zone_control_clear(old_zone); + } + + return zone; +} + +static zone_t *create_zone_new(conf_t *conf, const knot_dname_t *name, + server_t *server) +{ + zone_t *zone = create_zone_from(name, server); + if (!zone) { + return NULL; + } + + int ret = zone_timers_read(server->timers_db, name, &zone->timers); + if (ret != KNOT_EOK && ret != KNOT_ENOENT) { + log_zone_error(zone->name, "failed to load persistent timers (%s)", + knot_strerror(ret)); + zone_free(&zone); + return NULL; + } + + timers_sanitize(conf, zone); + + if (zone_expired(zone)) { + // expired => force bootstrap, no load attempt + log_zone_info(zone->name, "zone will be bootstrapped"); + assert(zone_is_slave(conf, zone)); + replan_load_bootstrap(conf, zone); + } else { + log_zone_info(zone->name, "zone will be loaded"); + replan_load_new(zone); // if load fails, fallback to bootstrap + } + + return zone; +} + +/*! + * \brief Load or reload the zone. + * + * \param conf Configuration. + * \param server Server. + * \param old_zone Already loaded zone (can be NULL). + * + * \return Error code, KNOT_EOK if successful. + */ +static zone_t *create_zone(conf_t *conf, const knot_dname_t *name, server_t *server, + zone_t *old_zone) +{ + assert(conf); + assert(name); + assert(server); + + if (old_zone) { + return create_zone_reload(conf, name, server, old_zone); + } else { + return create_zone_new(conf, name, server); + } +} + +static void mark_changed_zones(knot_zonedb_t *zonedb, trie_t *changed) +{ + if (changed == NULL) { + return; + } + + trie_it_t *it = trie_it_begin(changed); + for (; !trie_it_finished(it); trie_it_next(it)) { + const knot_dname_t *name = + (const knot_dname_t *)trie_it_key(it, NULL); + + zone_t *zone = knot_zonedb_find(zonedb, name); + if (zone != NULL) { + conf_io_type_t type = (conf_io_type_t)(*trie_it_val(it)); + assert(!(type & CONF_IO_TSET)); + zone->change_type = type; + } + } + trie_it_free(it); +} + +/*! + * \brief Create new zone database. + * + * Zones that should be retained are just added from the old database to the + * new. New zones are loaded. + * + * \param conf New server configuration. + * \param server Server instance. + * + * \return New zone database. + */ +static knot_zonedb_t *create_zonedb(conf_t *conf, server_t *server) +{ + assert(conf); + assert(server); + + knot_zonedb_t *db_old = server->zone_db; + knot_zonedb_t *db_new = knot_zonedb_new(); + if (!db_new) { + return NULL; + } + + bool full = !(conf->io.flags & CONF_IO_FACTIVE) || + (conf->io.flags & CONF_IO_FRLD_ZONES); + + /* Mark changed zones. */ + if (!full) { + mark_changed_zones(server->zone_db, conf->io.zones); + } + + for (conf_iter_t iter = conf_iter(conf, C_ZONE); iter.code == KNOT_EOK; + conf_iter_next(conf, &iter)) { + conf_val_t id = conf_iter_id(conf, &iter); + const knot_dname_t *name = conf_dname(&id); + + zone_t *old_zone = knot_zonedb_find(db_old, name); + if (old_zone != NULL && !full) { + /* Reuse unchanged zone. */ + if (!(old_zone->change_type & CONF_IO_TRELOAD)) { + knot_zonedb_insert(db_new, old_zone); + continue; + } + } + + zone_t *zone = create_zone(conf, name, server, old_zone); + if (zone == NULL) { + log_zone_error(name, "zone cannot be created"); + continue; + } + + conf_activate_modules(conf, zone->name, &zone->query_modules, + &zone->query_plan); + + knot_zonedb_insert(db_new, zone); + } + + return db_new; +} + +/*! + * \brief Schedule deletion of old zones, and free the zone db structure. + * + * \note Zone content may be preserved in the new zone database, in this case + * new and old zone share the contents. Shared content is not freed. + * + * \param conf New server configuration. + * \param db_old Old zone database to remove. + * \param db_new New zone database for comparison if full reload. + */ +static void remove_old_zonedb(conf_t *conf, knot_zonedb_t *db_old, + knot_zonedb_t *db_new) +{ + if (db_old == NULL) { + return; + } + + bool full = !(conf->io.flags & CONF_IO_FACTIVE) || + (conf->io.flags & CONF_IO_FRLD_ZONES); + + knot_zonedb_iter_t *it = knot_zonedb_iter_begin(db_old); + + while (!knot_zonedb_iter_finished(it)) { + zone_t *zone = knot_zonedb_iter_val(it); + + if (full) { + /* Check if reloaded (reused contents). */ + if (knot_zonedb_find(db_new, zone->name)) { + zone->contents = NULL; + } + /* Completely new zone. */ + } else { + /* Check if reloaded (reused contents). */ + if (zone->change_type & CONF_IO_TRELOAD) { + zone->contents = NULL; + zone_free(&zone); + /* Check if removed (drop also contents). */ + } else if (zone->change_type & CONF_IO_TUNSET) { + zone_free(&zone); + } + /* Completely reused zone. */ + } + + knot_zonedb_iter_next(it); + } + + knot_zonedb_iter_free(it); + + if (full) { + knot_zonedb_deep_free(&db_old); + } else { + knot_zonedb_free(&db_old); + } +} + +void zonedb_reload(conf_t *conf, server_t *server) +{ + if (conf == NULL || server == NULL) { + return; + } + + /* Insert all required zones to the new zone DB. */ + knot_zonedb_t *db_new = create_zonedb(conf, server); + if (db_new == NULL) { + log_error("failed to create new zone database"); + return; + } + + /* Switch the databases. */ + knot_zonedb_t **db_current = &server->zone_db; + knot_zonedb_t *db_old = rcu_xchg_pointer(db_current, db_new); + + /* Wait for readers to finish reading old zone database. */ + synchronize_rcu(); + + /* Remove old zone DB. */ + remove_old_zonedb(conf, db_old, db_new); +} diff --git a/src/knot/zone/zonedb-load.h b/src/knot/zone/zonedb-load.h new file mode 100644 index 0000000..fda12d8 --- /dev/null +++ b/src/knot/zone/zonedb-load.h @@ -0,0 +1,28 @@ +/* Copyright (C) 2015 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include "knot/conf/conf.h" +#include "knot/server/server.h" + +/*! + * \brief Update zone database according to configuration. + * + * \param[in] conf Configuration. + * \param[in] server Server instance. + */ +void zonedb_reload(conf_t *conf, server_t *server); diff --git a/src/knot/zone/zonedb.c b/src/knot/zone/zonedb.c new file mode 100644 index 0000000..d949a59 --- /dev/null +++ b/src/knot/zone/zonedb.c @@ -0,0 +1,167 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> +#include <stdlib.h> + +#include "knot/zone/zonedb.h" +#include "libknot/packet/wire.h" +#include "contrib/mempattern.h" +#include "contrib/ucw/mempool.h" + +/*! \brief Discard zone in zone database. */ +static void discard_zone(zone_t *zone) +{ + // Don't flush if removed zone (no previous configuration available). + if (conf_rawid_exists(conf(), C_ZONE, zone->name, knot_dname_size(zone->name))) { + // Flush if bootstrapped or if the journal doesn't exist. + if (!zone->zonefile.exists || !journal_exists(zone->journal_db, zone->name)) { + zone_flush_journal(conf(), zone); + } else { + bool empty; + uint32_t journal_serial, zone_serial = zone_contents_serial(zone->contents); + int ret = zone_journal_serial(conf(), zone, &empty, &journal_serial); + if (ret != KNOT_EOK || empty || journal_serial != zone_serial) { + zone_flush_journal(conf(), zone); + } + } + } + + zone_free(&zone); +} + +knot_zonedb_t *knot_zonedb_new(void) +{ + knot_zonedb_t *db = calloc(1, sizeof(knot_zonedb_t)); + if (db == NULL) { + return NULL; + } + + mm_ctx_mempool(&db->mm, MM_DEFAULT_BLKSIZE); + + db->trie = trie_create(&db->mm); + if (db->trie == NULL) { + mp_delete(db->mm.ctx); + free(db); + return NULL; + } + + return db; +} + +int knot_zonedb_insert(knot_zonedb_t *db, zone_t *zone) +{ + if (db == NULL || zone == NULL) { + return KNOT_EINVAL; + } + + assert(zone->name); + knot_dname_storage_t lf_storage; + uint8_t *lf = knot_dname_lf(zone->name, lf_storage); + assert(lf); + + *trie_get_ins(db->trie, (char *)lf + 1, *lf) = zone; + + return KNOT_EOK; +} + +int knot_zonedb_del(knot_zonedb_t *db, const knot_dname_t *zone_name) +{ + if (db == NULL || zone_name == NULL) { + return KNOT_EINVAL; + } + + knot_dname_storage_t lf_storage; + uint8_t *lf = knot_dname_lf(zone_name, lf_storage); + assert(lf); + + trie_val_t *rval = trie_get_try(db->trie, (char *)lf + 1, *lf); + if (rval == NULL) { + return KNOT_ENOENT; + } + + return trie_del(db->trie, (char *)lf + 1, *lf, NULL); +} + +zone_t *knot_zonedb_find(knot_zonedb_t *db, const knot_dname_t *zone_name) +{ + if (db == NULL) { + return NULL; + } + + knot_dname_storage_t lf_storage; + uint8_t *lf = knot_dname_lf(zone_name, lf_storage); + assert(lf); + + trie_val_t *val = trie_get_try(db->trie, (char *)lf + 1, *lf); + if (val == NULL) { + return NULL; + } + + return *val; +} + +zone_t *knot_zonedb_find_suffix(knot_zonedb_t *db, const knot_dname_t *zone_name) +{ + if (db == NULL || zone_name == NULL) { + return NULL; + } + + while (true) { + knot_dname_storage_t lf_storage; + uint8_t *lf = knot_dname_lf(zone_name, lf_storage); + assert(lf); + + trie_val_t *val = trie_get_try(db->trie, (char *)lf + 1, *lf); + if (val != NULL) { + return *val; + } else if (zone_name[0] == 0) { + return NULL; + } + + zone_name = knot_wire_next_label(zone_name, NULL); + } +} + +size_t knot_zonedb_size(const knot_zonedb_t *db) +{ + if (db == NULL) { + return 0; + } + + return trie_weight(db->trie); +} + +void knot_zonedb_free(knot_zonedb_t **db) +{ + if (db == NULL || *db == NULL) { + return; + } + + mp_delete((*db)->mm.ctx); + free(*db); + *db = NULL; +} + +void knot_zonedb_deep_free(knot_zonedb_t **db) +{ + if (db == NULL || *db == NULL) { + return; + } + + knot_zonedb_foreach(*db, discard_zone); + knot_zonedb_free(db); +} diff --git a/src/knot/zone/zonedb.h b/src/knot/zone/zonedb.h new file mode 100644 index 0000000..c5fab4d --- /dev/null +++ b/src/knot/zone/zonedb.h @@ -0,0 +1,123 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \file + * + * \brief Zone database represents a list of managed zones. + */ + +#pragma once + +#include "knot/zone/zone.h" +#include "libknot/dname.h" +#include "contrib/qp-trie/trie.h" + +typedef struct { + trie_t *trie; + knot_mm_t mm; +} knot_zonedb_t; + +/* + * Mapping of iterators to internal data structure. + */ +typedef trie_it_t knot_zonedb_iter_t; +#define knot_zonedb_iter_begin(db) trie_it_begin((db)->trie) +#define knot_zonedb_iter_finished(it) trie_it_finished(it) +#define knot_zonedb_iter_next(it) trie_it_next(it) +#define knot_zonedb_iter_free(it) trie_it_free(it) +#define knot_zonedb_iter_val(it) *trie_it_val(it) + +/* + * Simple foreach() access with callback and variable number of callback params. + */ +#define knot_zonedb_foreach(db, callback, ...) \ +{ \ + knot_zonedb_iter_t *it = knot_zonedb_iter_begin((db)); \ + while(!knot_zonedb_iter_finished(it)) { \ + callback((zone_t *)knot_zonedb_iter_val(it), ##__VA_ARGS__); \ + knot_zonedb_iter_next(it); \ + } \ + knot_zonedb_iter_free(it); \ +} + +/*! + * \brief Allocates and initializes the zone database structure. + * + * \return Pointer to the created zone database structure or NULL if an error + * occurred. + */ +knot_zonedb_t *knot_zonedb_new(void); + +/*! + * \brief Adds new zone to the database. + * + * \param db Zone database to store the zone. + * \param zone Parsed zone. + * + * \retval KNOT_EOK + * \retval KNOT_EZONEIN + */ +int knot_zonedb_insert(knot_zonedb_t *db, zone_t *zone); + +/*! + * \brief Removes the given zone from the database if it exists. + * + * \param db Zone database to remove from. + * \param zone_name Name of the zone to be removed. + * + * \retval KNOT_EOK + * \retval KNOT_ENOZONE + */ +int knot_zonedb_del(knot_zonedb_t *db, const knot_dname_t *zone_name); + +/*! + * \brief Finds zone exactly matching the given zone name. + * + * \param db Zone database to search in. + * \param zone_name Domain name representing the zone name. + * + * \return Zone with \a zone_name being the owner of the zone apex or NULL if + * not found. + */ +zone_t *knot_zonedb_find(knot_zonedb_t *db, const knot_dname_t *zone_name); + +/*! + * \brief Finds zone the given domain name should belong to. + * + * \param db Zone database to search in. + * \param zone_name Domain name to find zone for. + * + * \retval Zone in which the domain name should be present or NULL if no such + * zone is found. + */ +zone_t *knot_zonedb_find_suffix(knot_zonedb_t *db, const knot_dname_t *zone_name); + +size_t knot_zonedb_size(const knot_zonedb_t *db); + +/*! + * \brief Destroys and deallocates the zone database structure (but not the + * zones within). + * + * \param db Zone database to be destroyed. + */ +void knot_zonedb_free(knot_zonedb_t **db); + +/*! + * \brief Destroys and deallocates the whole zone database including the zones. + * + * \param db Zone database to be destroyed. + */ +void knot_zonedb_deep_free(knot_zonedb_t **db); diff --git a/src/knot/zone/zonefile.c b/src/knot/zone/zonefile.c new file mode 100644 index 0000000..37fc90b --- /dev/null +++ b/src/knot/zone/zonefile.c @@ -0,0 +1,343 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <time.h> +#include <unistd.h> +#include <inttypes.h> + +#include "libknot/libknot.h" +#include "contrib/files.h" +#include "knot/common/log.h" +#include "knot/dnssec/zone-nsec.h" +#include "knot/zone/semantic-check.h" +#include "knot/zone/contents.h" +#include "knot/zone/zonefile.h" +#include "knot/zone/zone-dump.h" + +#define ERROR(zone, fmt, ...) log_zone_error(zone, "zone loader, " fmt, ##__VA_ARGS__) +#define WARNING(zone, fmt, ...) log_zone_warning(zone, "zone loader, " fmt, ##__VA_ARGS__) +#define NOTICE(zone, fmt, ...) log_zone_notice(zone, "zone loader, " fmt, ##__VA_ARGS__) + +static void process_error(zs_scanner_t *s) +{ + zcreator_t *zc = s->process.data; + const knot_dname_t *zname = zc->z->apex->owner; + + ERROR(zname, "%s in zone, file '%s', line %"PRIu64" (%s)", + s->error.fatal ? "fatal error" : "error", + s->file.name, s->line_counter, + zs_strerror(s->error.code)); +} + +static bool handle_err(zcreator_t *zc, const knot_rrset_t *rr, int ret, bool master) +{ + const knot_dname_t *zname = zc->z->apex->owner; + + char buff[KNOT_DNAME_TXT_MAXLEN + 1]; + char *owner = knot_dname_to_str(buff, rr->owner, sizeof(buff)); + if (owner == NULL) { + owner = ""; + } + + if (ret == KNOT_EOUTOFZONE) { + WARNING(zname, "ignoring out-of-zone data, owner %s", owner); + return true; + } else if (ret == KNOT_ETTL) { + char type[16] = { '\0' }; + knot_rrtype_to_string(rr->type, type, sizeof(type)); + NOTICE(zname, "TTL mismatch, owner %s, type %s, TTL set to %u", + owner, type, rr->ttl); + return true; + } else { + ERROR(zname, "failed to process record, owner %s", owner); + return false; + } +} + +int zcreator_step(zcreator_t *zc, const knot_rrset_t *rr) +{ + if (zc == NULL || rr == NULL || rr->rrs.count != 1) { + return KNOT_EINVAL; + } + + if (rr->type == KNOT_RRTYPE_SOA && + node_rrtype_exists(zc->z->apex, KNOT_RRTYPE_SOA)) { + // Ignore extra SOA + return KNOT_EOK; + } + + zone_node_t *node = NULL; + int ret = zone_contents_add_rr(zc->z, rr, &node); + if (ret != KNOT_EOK) { + if (!handle_err(zc, rr, ret, zc->master)) { + // Fatal error + return ret; + } + } + + return KNOT_EOK; +} + +/*! \brief Creates RR from parser input, passes it to handling function. */ +static void process_data(zs_scanner_t *scanner) +{ + zcreator_t *zc = scanner->process.data; + if (zc->ret != KNOT_EOK) { + scanner->state = ZS_STATE_STOP; + return; + } + + knot_dname_t *owner = knot_dname_copy(scanner->r_owner, NULL); + if (owner == NULL) { + zc->ret = KNOT_ENOMEM; + return; + } + + knot_rrset_t rr; + knot_rrset_init(&rr, owner, scanner->r_type, scanner->r_class, scanner->r_ttl); + + int ret = knot_rrset_add_rdata(&rr, scanner->r_data, scanner->r_data_length, NULL); + if (ret != KNOT_EOK) { + knot_rrset_clear(&rr, NULL); + zc->ret = ret; + return; + } + + /* Convert RDATA dnames to lowercase before adding to zone. */ + ret = knot_rrset_rr_to_canonical(&rr); + if (ret != KNOT_EOK) { + knot_rrset_clear(&rr, NULL); + zc->ret = ret; + return; + } + + zc->ret = zcreator_step(zc, &rr); + knot_rrset_clear(&rr, NULL); +} + +int zonefile_open(zloader_t *loader, const char *source, + const knot_dname_t *origin, bool semantic_checks, time_t time) +{ + if (!loader) { + return KNOT_EINVAL; + } + + memset(loader, 0, sizeof(zloader_t)); + + /* Check zone file. */ + if (access(source, F_OK | R_OK) != 0) { + return KNOT_EACCES; + } + + /* Create context. */ + zcreator_t *zc = malloc(sizeof(zcreator_t)); + if (zc == NULL) { + return KNOT_ENOMEM; + } + memset(zc, 0, sizeof(zcreator_t)); + + zc->z = zone_contents_new(origin); + if (zc->z == NULL) { + free(zc); + return KNOT_ENOMEM; + } + + /* Prepare textual owner for zone scanner. */ + char *origin_str = knot_dname_to_str_alloc(origin); + if (origin_str == NULL) { + zone_contents_deep_free(zc->z); + free(zc); + return KNOT_ENOMEM; + } + + if (zs_init(&loader->scanner, origin_str, KNOT_CLASS_IN, 3600) != 0 || + zs_set_input_file(&loader->scanner, source) != 0 || + zs_set_processing(&loader->scanner, process_data, process_error, zc) != 0) { + zs_deinit(&loader->scanner); + free(origin_str); + zone_contents_deep_free(zc->z); + free(zc); + return KNOT_EFILE; + } + free(origin_str); + + loader->source = strdup(source); + loader->creator = zc; + loader->semantic_checks = semantic_checks; + loader->time = time; + + return KNOT_EOK; +} + +zone_contents_t *zonefile_load(zloader_t *loader) +{ + if (!loader) { + return NULL; + } + + zcreator_t *zc = loader->creator; + const knot_dname_t *zname = zc->z->apex->owner; + + assert(zc); + int ret = zs_parse_all(&loader->scanner); + if (ret != 0 && loader->scanner.error.counter == 0) { + ERROR(zname, "failed to load zone, file '%s' (%s)", + loader->source, zs_strerror(loader->scanner.error.code)); + goto fail; + } + + if (zc->ret != KNOT_EOK) { + ERROR(zname, "failed to load zone, file '%s' (%s)", + loader->source, knot_strerror(zc->ret)); + goto fail; + } + + if (loader->scanner.error.counter > 0) { + ERROR(zname, "failed to load zone, file '%s', %"PRIu64" errors", + loader->source, loader->scanner.error.counter); + goto fail; + } + + if (!node_rrtype_exists(loader->creator->z->apex, KNOT_RRTYPE_SOA)) { + loader->err_handler->fatal_error = true; + loader->err_handler->cb(loader->err_handler, zc->z, NULL, + SEM_ERR_SOA_NONE, NULL); + goto fail; + } + + ret = zone_contents_adjust_full(zc->z); + if (ret != KNOT_EOK) { + ERROR(zname, "failed to finalize zone contents (%s)", + knot_strerror(ret)); + goto fail; + } + + ret = sem_checks_process(zc->z, loader->semantic_checks, + loader->err_handler, loader->time); + + if (ret != KNOT_EOK) { + ERROR(zname, "failed to load zone, file '%s' (%s)", + loader->source, knot_strerror(ret)); + goto fail; + } + + return zc->z; + +fail: + zone_contents_deep_free(zc->z); + return NULL; +} + +int zonefile_exists(const char *path, time_t *mtime) +{ + if (path == NULL) { + return KNOT_EINVAL; + } + + struct stat zonefile_st = { 0 }; + if (stat(path, &zonefile_st) < 0) { + return knot_map_errno(); + } + + if (mtime != NULL) { + *mtime = zonefile_st.st_mtime; + } + + return KNOT_EOK; +} + +int zonefile_write(const char *path, zone_contents_t *zone) +{ + if (!zone || !path) { + return KNOT_EINVAL; + } + + int ret = make_path(path, S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP); + if (ret != KNOT_EOK) { + return ret; + } + + FILE *file = NULL; + char *tmp_name = NULL; + ret = open_tmp_file(path, &tmp_name, &file, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP); + if (ret != KNOT_EOK) { + return ret; + } + + ret = zone_dump_text(zone, file, true); + fclose(file); + if (ret != KNOT_EOK) { + unlink(tmp_name); + free(tmp_name); + return ret; + } + + /* Swap temporary zonefile and new zonefile. */ + ret = rename(tmp_name, path); + if (ret != 0) { + ret = knot_map_errno(); + unlink(tmp_name); + free(tmp_name); + return ret; + } + + free(tmp_name); + + return KNOT_EOK; +} + +void zonefile_close(zloader_t *loader) +{ + if (!loader) { + return; + } + + zs_deinit(&loader->scanner); + free(loader->source); + free(loader->creator); +} + +void err_handler_logger(sem_handler_t *handler, const zone_contents_t *zone, + const zone_node_t *node, sem_error_t error, const char *data) +{ + assert(handler != NULL); + assert(zone != NULL); + + char buff[KNOT_DNAME_TXT_MAXLEN + 1] = ""; + if (node != NULL) { + (void)knot_dname_to_str(buff, node->owner, sizeof(buff)); + } + + log_fmt_zone(handler->fatal_error ? LOG_ERR : LOG_WARNING, + LOG_SOURCE_ZONE, zone->apex->owner, NULL, + "check%s%s, %s%s%s", + (node != NULL ? ", node " : ""), + (node != NULL ? buff : ""), + sem_error_msg(error), + (data != NULL ? " " : ""), + (data != NULL ? data : "")); +} + +#undef ERROR +#undef WARNING +#undef NOTICE diff --git a/src/knot/zone/zonefile.h b/src/knot/zone/zonefile.h new file mode 100644 index 0000000..90283ee --- /dev/null +++ b/src/knot/zone/zonefile.h @@ -0,0 +1,104 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <stdbool.h> +#include <stdio.h> + +#include "knot/zone/zone.h" +#include "knot/zone/semantic-check.h" +#include "libzscanner/scanner.h" +/*! + * \brief Zone creator structure. + */ +typedef struct zcreator { + zone_contents_t *z; /*!< Created zone. */ + bool master; /*!< True if server is a primary master for the zone. */ + int ret; /*!< Return value. */ +} zcreator_t; + +/*! + * \brief Zone loader structure. + */ +typedef struct { + char *source; /*!< Zone source file. */ + bool semantic_checks; /*!< Do semantic checks. */ + sem_handler_t *err_handler; /*!< Semantic checks error handler. */ + zcreator_t *creator; /*!< Loader context. */ + zs_scanner_t scanner; /*!< Zone scanner. */ + time_t time; /*!< time for zone check. */ +} zloader_t; + +void err_handler_logger(sem_handler_t *handler, const zone_contents_t *zone, + const zone_node_t *node, sem_error_t error, const char *data); + +/*! + * \brief Open zone file for loading. + * + * \param loader Output zone loader. + * \param source Source file name. + * \param origin Zone origin. + * \param semantic_checks Perform semantic checks. + * \param time Time for semantic check. + * + * \retval Initialized loader on success. + * \retval NULL on error. + */ +int zonefile_open(zloader_t *loader, const char *source, + const knot_dname_t *origin, bool semantic_checks, time_t time); + +/*! + * \brief Loads zone from a zone file. + * + * \param loader Zone loader instance. + * + * \retval Loaded zone contents on success. + * \retval NULL otherwise. + */ +zone_contents_t *zonefile_load(zloader_t *loader); + +/*! + * \brief Checks if zonefile exists. + * + * \param path Zonefile path. + * \param mtime Zonefile mtime if exists (can be NULL). + * + * \return KNOT_E* + */ +int zonefile_exists(const char *path, time_t *mtime); + +/*! + * \brief Write zone contents to zone file. + */ +int zonefile_write(const char *path, zone_contents_t *zone); + +/*! + * \brief Close zone file loader. + * + * \param loader Zone loader instance. + */ +void zonefile_close(zloader_t *loader); + +/*! + * \brief Adds one RR into zone. + * + * \param zl Zone loader. + * \param rr RR to add. + * + * \return KNOT_E* + */ +int zcreator_step(zcreator_t *zl, const knot_rrset_t *rr); diff --git a/src/knotd.pc.in b/src/knotd.pc.in new file mode 100644 index 0000000..6db74ca --- /dev/null +++ b/src/knotd.pc.in @@ -0,0 +1,9 @@ +prefix=@prefix@ +exec_prefix=@prefix@ +libdir=@libdir@ +module_instdir=@module_instdir@ + +Name: knotd +Description: Knot DNS daemon +URL: https://www.knot-dns.cz +Version: @PACKAGE_VERSION@ diff --git a/src/libdnssec.pc.in b/src/libdnssec.pc.in new file mode 100644 index 0000000..6c0aa5c --- /dev/null +++ b/src/libdnssec.pc.in @@ -0,0 +1,13 @@ +prefix=@prefix@ +exec_prefix=@prefix@ +libdir=@libdir@ +includedir=@includedir@ +soname=@libdnssec_SONAME@ + +Name: libdnssec +Description: Knot DNS DNSSEC library +URL: https://www.knot-dns.cz +Version: @PACKAGE_VERSION@ +Requires.private: gnutls >= 3.3 +Libs: -L${libdir} -ldnssec +Cflags: -I${includedir} diff --git a/src/libdnssec/Makefile.inc b/src/libdnssec/Makefile.inc new file mode 100644 index 0000000..112f95b --- /dev/null +++ b/src/libdnssec/Makefile.inc @@ -0,0 +1,87 @@ +lib_LTLIBRARIES += libdnssec.la +pkgconfig_DATA += libdnssec.pc + +noinst_LTLIBRARIES += libshared.la + +libshared_la_CPPFLAGS = $(AM_CPPFLAGS) $(gnutls_CFLAGS) + +libshared_la_SOURCES = \ + libdnssec/shared/bignum.c \ + libdnssec/shared/bignum.h \ + libdnssec/shared/binary_wire.h \ + libdnssec/shared/dname.c \ + libdnssec/shared/dname.h \ + libdnssec/shared/fs.c \ + libdnssec/shared/fs.h \ + libdnssec/shared/hex.c \ + libdnssec/shared/hex.h \ + libdnssec/shared/keyid_gnutls.c \ + libdnssec/shared/keyid_gnutls.h \ + libdnssec/shared/pem.c \ + libdnssec/shared/pem.h \ + libdnssec/shared/shared.h + +libdnssec_la_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAG_VISIBILITY) $(gnutls_CFLAGS) +libdnssec_la_LDFLAGS = $(AM_LDFLAGS) $(libdnssec_VERSION_INFO) $(gnutls_LIBS) +if EXCLUDE_LIBS_LIBDNSSEC +libdnssec_la_LDFLAGS += $(LDFLAG_EXCLUDE_LIBS) +endif +libdnssec_la_LIBADD = libshared.la +if ENABLE_PKCS11 +libdnssec_la_LIBADD += $(pthread_LIBS) +endif + +include_libdnssecdir = $(includedir)/libdnssec +include_libdnssec_HEADERS = \ + libdnssec/binary.h \ + libdnssec/crypto.h \ + libdnssec/dnssec.h \ + libdnssec/error.h \ + libdnssec/key.h \ + libdnssec/keyid.h \ + libdnssec/keystore.h \ + libdnssec/keytag.h \ + libdnssec/list.h \ + libdnssec/nsec.h \ + libdnssec/random.h \ + libdnssec/sign.h \ + libdnssec/tsig.h \ + libdnssec/version.h + +libdnssec_la_SOURCES = \ + libdnssec/contrib/vpool.c \ + libdnssec/contrib/vpool.h \ + libdnssec/binary.c \ + libdnssec/crypto.c \ + libdnssec/error.c \ + libdnssec/key/algorithm.c \ + libdnssec/key/algorithm.h \ + libdnssec/key/convert.c \ + libdnssec/key/convert.h \ + libdnssec/key/dnskey.c \ + libdnssec/key/dnskey.h \ + libdnssec/key/ds.c \ + libdnssec/key/internal.h \ + libdnssec/key/key.c \ + libdnssec/key/keytag.c \ + libdnssec/key/privkey.c \ + libdnssec/key/privkey.h \ + libdnssec/key/simple.c \ + libdnssec/keyid.c \ + libdnssec/keystore/internal.h \ + libdnssec/keystore/keystore.c \ + libdnssec/keystore/pkcs11.c \ + libdnssec/keystore/pkcs8.c \ + libdnssec/keystore/pkcs8_dir.c \ + libdnssec/list/list.c \ + libdnssec/list/ucw_clists.h \ + libdnssec/nsec/bitmap.c \ + libdnssec/nsec/hash.c \ + libdnssec/nsec/nsec.c \ + libdnssec/p11/p11.c \ + libdnssec/p11/p11.h \ + libdnssec/random.c \ + libdnssec/sign/der.c \ + libdnssec/sign/der.h \ + libdnssec/sign/sign.c \ + libdnssec/tsig.c diff --git a/src/libdnssec/binary.c b/src/libdnssec/binary.c new file mode 100644 index 0000000..a57b421 --- /dev/null +++ b/src/libdnssec/binary.c @@ -0,0 +1,169 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <assert.h> +#include <string.h> + +#include "libdnssec/binary.h" +#include "libdnssec/error.h" +#include "libdnssec/shared/shared.h" + +// Workaround for symbol redefinition if linked statically. +#define base64_encode base64encode +#define base64_decode base64decode +#define base64_encode_alloc base64encodealloc +#define base64_decode_alloc base64decodealloc +#include "contrib/base64.c" + +/* -- public API ----------------------------------------------------------- */ + +_public_ +int dnssec_binary_alloc(dnssec_binary_t *data, size_t size) +{ + if (!data || size == 0) { + return DNSSEC_EINVAL; + } + + uint8_t *new_data = calloc(1, size); + if (!new_data) { + return DNSSEC_ENOMEM; + } + + data->data = new_data; + data->size = size; + + return DNSSEC_EOK; +} + +_public_ +void dnssec_binary_free(dnssec_binary_t *binary) +{ + if (!binary) { + return; + } + + free(binary->data); + clear_struct(binary); +} + +_public_ +int dnssec_binary_dup(const dnssec_binary_t *from, dnssec_binary_t *to) +{ + if (!from || !to) { + return DNSSEC_EINVAL; + } + + uint8_t *copy = malloc(from->size); + if (copy == NULL) { + return DNSSEC_ENOMEM; + } + + memmove(copy, from->data, from->size); + + to->size = from->size; + to->data = copy; + + return DNSSEC_EOK; +} + +_public_ +int dnssec_binary_resize(dnssec_binary_t *data, size_t new_size) +{ + if (!data) { + return DNSSEC_EINVAL; + } + + uint8_t *new_data = realloc(data->data, new_size); + if (new_size > 0 && new_data == NULL) { + return DNSSEC_ENOMEM; + } + + data->data = new_data; + data->size = new_size; + + return DNSSEC_EOK; +} + +_public_ +int dnssec_binary_cmp(const dnssec_binary_t *one, const dnssec_binary_t *two) +{ + if (one == two) { + return 0; + } + + uint8_t *data_one = one && one->size > 0 ? one->data : NULL; + uint8_t *data_two = two && two->size > 0 ? two->data : NULL; + + if (data_one == data_two) { + return 0; + } else if (data_one == NULL) { + return -1; + } else if (data_two == NULL) { + return +1; + } + + size_t min_size = one->size <= two->size ? one->size : two->size; + int cmp = memcmp(data_one, data_two, min_size); + if (cmp != 0) { + return cmp; + } else if (one->size == two->size) { + return 0; + } else if (one->size < two->size) { + return -1; + } else { + return +1; + } +} + +_public_ +int dnssec_binary_from_base64(const dnssec_binary_t *base64, + dnssec_binary_t *binary) +{ + if (!base64 || !binary) { + return DNSSEC_EINVAL; + } + + uint8_t *data; + int32_t size = base64_decode_alloc(base64->data, base64->size, &data); + if (size < 0) { + return DNSSEC_EINVAL; + } + + binary->data = data; + binary->size = size; + + return DNSSEC_EOK; +} + +_public_ +int dnssec_binary_to_base64(const dnssec_binary_t *binary, + dnssec_binary_t *base64) +{ + if (!binary || !base64) { + return DNSSEC_EINVAL; + } + + uint8_t *data; + int32_t size = base64_encode_alloc(binary->data, binary->size, &data); + if (size < 0) { + return DNSSEC_EINVAL; + } + + base64->data = data; + base64->size = size; + + return DNSSEC_EOK; +} diff --git a/src/libdnssec/binary.h b/src/libdnssec/binary.h new file mode 100644 index 0000000..fb0cf9b --- /dev/null +++ b/src/libdnssec/binary.h @@ -0,0 +1,133 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +/*! + * \file + * + * \addtogroup binary + * + * \brief Universal binary data container. + * + * The module provides universal binary data container extensively used by + * a lot of functions provided by the library. + * + * Example of use: + * ~~~~~ {.c} + * + * dnssec_binary_t data = { 0 }; + * + * int result = dnssec_binary_alloc(&data, 32); + * if (result != DNSSEC_EOK) { + * return result; + * } + * + * memcpy(&data.data, buffer, data.size); + * + * // ... + * + * dnssec_binary_free(&data); + * + * ~~~~~ + * + * @{ + */ + +#pragma once + +#include <stdint.h> +#include <stdlib.h> + +/*! + * Universal structure to hold binary data. + */ +typedef struct dnssec_binary { + size_t size; /*!< Size of the binary data. */ + uint8_t *data; /*!< Stored data. */ +} dnssec_binary_t; + +/*! + * Allocate new binary data structure. + * + * \param[out] data Binary to be allocated. + * \param[in] size Requested size of the binary. + * + * \return Error code, DNSSEC_EOK if successful. + */ +int dnssec_binary_alloc(dnssec_binary_t *data, size_t size); + +/*! + * Free content of binary structure. + * + * \param binary Binary structure to be freed. + */ +void dnssec_binary_free(dnssec_binary_t *binary); + +/*! + * Create a copy of a binary structure. + * + * \param[in] from Source of the copy. + * \param[out] to Target of the copy. + * + * \return Error code, DNSSEC_EOK if successful. + */ +int dnssec_binary_dup(const dnssec_binary_t *from, dnssec_binary_t *to); + +/*! + * Resize binary structure to a new size. + * + * Internally uses realloc, which means that this function can be also used + * as a malloc or free. + * + * \param data Binary to be resized. + * \param new_size New size. + * + * \return Error code, DNSSEC_EOK if successful. + */ +int dnssec_binary_resize(dnssec_binary_t *data, size_t new_size); + +/*! + * Compare two binary structures (equivalent of memcmp). + * + * \note NULL sorts before data. + * + * \param one First binary. + * \param two Second binary. + * + * \return 0 if one equals two, <0 if one sorts before two, >0 otherwise. + */ +int dnssec_binary_cmp(const dnssec_binary_t *one, const dnssec_binary_t *two); + +/*! + * Allocate binary from Base64 encoded string. + * + * \param[in] base64 Base64 encoded data. + * \param[out] binary Decoded binary data. + * + * \return Error code, DNSSEC_EOK if successful. + */ +int dnssec_binary_from_base64(const dnssec_binary_t *base64, + dnssec_binary_t *binary); + +/*! + * Create Base64 encoded string from binary data. + * + * \param[in] binary Binary data. + * \param[out] base64 Base64 encode data. + * + * \return Error code, DNSSEC_EOK if successful. + */ +int dnssec_binary_to_base64(const dnssec_binary_t *binary, + dnssec_binary_t *base64); +/*! @} */ diff --git a/src/libdnssec/contrib/vpool.c b/src/libdnssec/contrib/vpool.c new file mode 100644 index 0000000..d2e1006 --- /dev/null +++ b/src/libdnssec/contrib/vpool.c @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2006, 2008 Alexey Vatchenko <av@bsdua.org> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> + +#include <errno.h> +#include <limits.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +#include "libdnssec/contrib/vpool.h" + +static void vpool_shift(struct vpool *pool); +static int vpool_new_size(struct vpool *pool, size_t datsize, + size_t *size); +static int vpool_resize(struct vpool *pool, size_t datsize); + +static void +vpool_shift(struct vpool *pool) +{ + if (pool->v_buf != pool->v_basebuf) { + memmove(pool->v_basebuf, pool->v_buf, pool->v_off); + pool->v_buf = pool->v_basebuf; + } +} + +static int +vpool_new_size(struct vpool *pool, size_t datsize, size_t *size) +{ + size_t need; + size_t rem; + + if (datsize <= pool->v_size - pool->v_off) { + *size = pool->v_size; + return (0); + } + + /* Check limit of new requested size */ + if (pool->v_limit - pool->v_off < datsize) { + return (EFBIG); + } + need = pool->v_off + datsize; + + /* Check limit of new size aligned to block size */ + rem = need % pool->v_blksize; + if (rem != 0) { + if (pool->v_limit - pool->v_off >= + datsize + (pool->v_blksize - rem)) { + need += pool->v_blksize - rem; + } else { + need = pool->v_limit; + } + } + + *size = need; + return (0); +} + +static int +vpool_resize(struct vpool *pool, size_t datsize) +{ + void *ret; + size_t size; + int error; + + error = vpool_new_size(pool, datsize, &size); + if (error != 0) { + return (error); + } + + if (size > pool->v_size) { + ret = malloc(size); + if (ret == NULL) { + return (ENOMEM); + } + + memcpy(ret, pool->v_buf, pool->v_off); + free(pool->v_basebuf); + pool->v_basebuf = pool->v_buf = ret; + pool->v_size = size; + } else if ((pool->v_size - pool->v_off) - + (pool->v_buf - pool->v_basebuf) < datsize) { + vpool_shift(pool); + } + + return (0); +} + +void +vpool_init(struct vpool *pool, size_t blksize, size_t limit) +{ + + pool->v_basebuf = pool->v_buf = NULL; + pool->v_off = pool->v_size = 0; + + pool->v_blksize = (blksize == 0) ? 4096 : blksize; /* XXX */ + pool->v_limit = (limit == 0) ? SIZE_MAX : limit; + + pool->v_lasterr = 0; +} + +void +vpool_final(struct vpool *pool) +{ + free(pool->v_basebuf); +} + +void +vpool_reset(struct vpool *pool) +{ + free(pool->v_basebuf); + pool->v_basebuf = pool->v_buf = NULL; + pool->v_off = pool->v_size = 0; + pool->v_lasterr = 0; +} + +void +vpool_wipe(struct vpool *pool) +{ + pool->v_off = 0; + pool->v_lasterr = 0; +} + +void * +vpool_insert(struct vpool *pool, size_t where, void *data, size_t datsize) +{ + void *ret; + int error; + + error = vpool_resize(pool, datsize); + if (error != 0) { + pool->v_lasterr = error; + return (NULL); + } + + /* + * If ``where'' is greater than or equal to offset then + * we are appending data to the end of the buffer. + */ + if (where > pool->v_off) { + where = pool->v_off; + } + + ret = (uint8_t *)pool->v_buf + where; + if (pool->v_off - where > 0) { + memmove(ret + datsize, ret, pool->v_off - where); + } + memcpy(ret, data, datsize); + pool->v_off += datsize; + pool->v_lasterr = 0; + + return (ret); +} + +void * +vpool_expand(struct vpool *pool, size_t where, size_t size) +{ + void *ret; + int error; + + error = vpool_resize(pool, size); + if (error != 0) { + pool->v_lasterr = error; + return (NULL); + } + + /* + * If ``where'' is greater than or equal to offset then + * we are appending data to the end of the buffer. + */ + if (where > pool->v_off) { + where = pool->v_off; + } + + ret = (uint8_t *)pool->v_buf + where; + if (pool->v_off - where > 0) { + memmove(ret + size, ret, pool->v_off - where); + } + pool->v_off += size; + pool->v_lasterr = 0; + + return (ret); +} + +int +vpool_truncate(struct vpool *pool, + size_t where, size_t size, enum vpool_trunc how) +{ + /* Check if caller wants to remove more data than we have */ + if (where >= pool->v_off || + size > pool->v_off || pool->v_off - size < where) { + pool->v_lasterr = ERANGE; + return (pool->v_lasterr); + } + + if (how == VPOOL_EXCLUDE) { + if (where == 0) { + /* + * Optimization. + * Don't move data, just adjust pointer. + */ + pool->v_buf = (uint8_t *)pool->v_buf + size; + } else { + memmove((uint8_t *)pool->v_buf + where, + (uint8_t *)pool->v_buf + where + size, + pool->v_off - size - where); + } + pool->v_off -= size; + } else { + pool->v_buf = (uint8_t *)pool->v_buf + where; + pool->v_off = size; + } + + pool->v_lasterr = 0; + return (0); +} + +void +vpool_export(struct vpool *pool, void **buf, size_t *size) +{ + vpool_shift(pool); + *buf = pool->v_buf; + *size = pool->v_off; + pool->v_basebuf = pool->v_buf = NULL; + pool->v_off = pool->v_size = 0; + pool->v_lasterr = 0; +} diff --git a/src/libdnssec/contrib/vpool.h b/src/libdnssec/contrib/vpool.h new file mode 100644 index 0000000..82e3d66 --- /dev/null +++ b/src/libdnssec/contrib/vpool.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2006, 2008 Alexey Vatchenko <av@bsdua.org> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * VPool: implementation of pool of data with a variable size. + */ +#ifndef _VPOOL_H_ +#define _VPOOL_H_ + +#include <stddef.h> +#include <limits.h> + +struct vpool { + void *v_basebuf; /* pointer returned by (re|m)alloc() */ + void *v_buf; /* actual data starts here */ + size_t v_off; + size_t v_size; + + size_t v_blksize; + size_t v_limit; + int v_lasterr; +}; + +enum vpool_trunc {VPOOL_EXCLUDE, VPOOL_INCLUDE}; +#define VPOOL_TAIL UINT_MAX + +void vpool_init(struct vpool *pool, size_t blksize, size_t limit); +void vpool_final(struct vpool *pool); + +void vpool_reset(struct vpool *pool); +void vpool_wipe(struct vpool *pool); + +void * vpool_insert(struct vpool *pool, + size_t where, void *data, size_t datsize); +void * vpool_expand(struct vpool *pool, size_t where, size_t size); + +int vpool_truncate(struct vpool *pool, + size_t where, size_t size, enum vpool_trunc how); + +#define vpool_is_empty(pool) ((pool)->v_off == 0) +#define vpool_get_buf(pool) ((pool)->v_buf) +#define vpool_get_length(pool) ((pool)->v_off) +#define vpool_get_error(pool) ((pool)->v_lasterr) + +void vpool_export(struct vpool *pool, void **buf, size_t *size); + +#endif /* !_VPOOL_H_ */ diff --git a/src/libdnssec/crypto.c b/src/libdnssec/crypto.c new file mode 100644 index 0000000..f54e20f --- /dev/null +++ b/src/libdnssec/crypto.c @@ -0,0 +1,42 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <gnutls/gnutls.h> +#include <gnutls/pkcs11.h> + +#include "libdnssec/crypto.h" +#include "libdnssec/p11/p11.h" +#include "libdnssec/shared/shared.h" + +_public_ +void dnssec_crypto_init(void) +{ + p11_init(); + gnutls_global_init(); +} + +_public_ +void dnssec_crypto_cleanup(void) +{ + gnutls_global_deinit(); + p11_cleanup(); +} + +_public_ +void dnssec_crypto_reinit(void) +{ + p11_reinit(); +} diff --git a/src/libdnssec/crypto.h b/src/libdnssec/crypto.h new file mode 100644 index 0000000..46541b4 --- /dev/null +++ b/src/libdnssec/crypto.h @@ -0,0 +1,74 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +/*! + * \file + * + * \addtogroup crypto + * + * \brief Cryptographic backend initialization. + * + * For most cryptographic operations, the library requires global + * initialization. Also, if the application creates a subprocess, the + * library has to be reinitialized in the child process after \c fork(). + * + * ~~~~~ {.c} + * int main(void) + * { + * int exit_code = 0; + * + * dnssec_crypto_init(); + * + * pid_t child_pid = fork(); + * if (child_pid < 0) { + * perror("fork"); + * exit_code = 1; + * } else if (child_pid == 0) { + * dnssec_crypto_reinit(); + * exit_code = child(); + * } else { + * exit_code = parent(); + * } + * + * dnssec_crypto_cleanup(); + * return exit_code; + * } + * ~~~~~ + * + * @{ + */ + +#pragma once + +/*! + * Initialize cryptographic backend. + */ +void dnssec_crypto_init(void); + +/*! + * Reinitialize cryptographic backend. + * + * Must be called after fork() by the child. + */ +void dnssec_crypto_reinit(void); + +/*! + * Deinitialize cryptographic backend. + * + * Should be called when terminating the application. + */ +void dnssec_crypto_cleanup(void); + +/*! @} */ diff --git a/src/libdnssec/dnssec.h b/src/libdnssec/dnssec.h new file mode 100644 index 0000000..8383901 --- /dev/null +++ b/src/libdnssec/dnssec.h @@ -0,0 +1,35 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * \file + * + * Convenient header to include all library modules. + */ + +#pragma once + +#include <libdnssec/binary.h> +#include <libdnssec/crypto.h> +#include <libdnssec/error.h> +#include <libdnssec/key.h> +#include <libdnssec/keyid.h> +#include <libdnssec/keystore.h> +#include <libdnssec/keytag.h> +#include <libdnssec/list.h> +#include <libdnssec/nsec.h> +#include <libdnssec/random.h> +#include <libdnssec/sign.h> +#include <libdnssec/tsig.h> diff --git a/src/libdnssec/error.c b/src/libdnssec/error.c new file mode 100644 index 0000000..97f7f8f --- /dev/null +++ b/src/libdnssec/error.c @@ -0,0 +1,84 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <string.h> + +#include "libdnssec/error.h" +#include "libdnssec/shared/shared.h" + +typedef struct error_message_t { + int code; + const char *text; +} error_message_t; + +static const error_message_t ERROR_MESSAGES[] = { + { DNSSEC_EOK, "no error" }, + + { DNSSEC_ENOMEM, "not enough memory" }, + { DNSSEC_EINVAL, "invalid argument" }, + { DNSSEC_ENOENT, "no such file or directory" }, + + { DNSSEC_ERROR, "unspecified error" }, + { DNSSEC_NOT_IMPLEMENTED_ERROR, "not implemented" }, + { DNSSEC_MALFORMED_DATA, "malformed data" }, + { DNSSEC_NOT_FOUND, "not found" }, + + { DNSSEC_PKCS8_IMPORT_ERROR, "PKCS #8 import error" }, + { DNSSEC_KEY_EXPORT_ERROR, "key export error" }, + { DNSSEC_KEY_IMPORT_ERROR, "key import error" }, + { DNSSEC_KEY_GENERATE_ERROR, "key generation error" }, + + { DNSSEC_INVALID_PUBLIC_KEY, "invalid public key" }, + { DNSSEC_INVALID_PRIVATE_KEY, "invalid private key" }, + { DNSSEC_INVALID_KEY_ALGORITHM, "invalid key algorithm" }, + { DNSSEC_INVALID_KEY_SIZE, "invalid key size" }, + { DNSSEC_INVALID_KEY_ID, "invalid key ID" }, + { DNSSEC_INVALID_KEY_NAME, "invalid key name" }, + + { DNSSEC_NO_PUBLIC_KEY, "no public key" }, + { DNSSEC_NO_PRIVATE_KEY, "no private key" }, + { DNSSEC_KEY_ALREADY_PRESENT, "key already present" }, + + { DNSSEC_SIGN_INIT_ERROR, "signing initialization error" }, + { DNSSEC_SIGN_ERROR, "signing error" }, + { DNSSEC_INVALID_SIGNATURE, "invalid signature" }, + + { DNSSEC_INVALID_NSEC3_ALGORITHM, "invalid NSEC3 algorithm" }, + { DNSSEC_NSEC3_HASHING_ERROR, "NSEC3 hashing error" }, + + { DNSSEC_INVALID_DS_ALGORITHM, "invalid DS algorithm" }, + { DNSSEC_DS_HASHING_ERROR, "DS hashing error" }, + + { DNSSEC_KEYSTORE_INVALID_CONFIG, "invalid KASP keystore configuration" }, + + { DNSSEC_P11_FAILED_TO_LOAD_MODULE, "failed to load PKCS #11 module" }, + { DNSSEC_P11_TOO_MANY_MODULES, "too many PKCS #11 modules loaded" }, + { DNSSEC_P11_TOKEN_NOT_AVAILABLE, "PKCS #11 token not available" }, + + { 0 } +}; + +_public_ +const char *dnssec_strerror(int error) +{ + for (const error_message_t *m = ERROR_MESSAGES; m->text; m++) { + if (m->code == error) { + return m->text; + } + } + + return NULL; +} diff --git a/src/libdnssec/error.h b/src/libdnssec/error.h new file mode 100644 index 0000000..eaa6afd --- /dev/null +++ b/src/libdnssec/error.h @@ -0,0 +1,112 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +/*! + * \file + * + * \addtogroup error + * + * \brief Error codes and error reporting. + * + * The module defines all error codes used in the library, and functions + * to convert the error codes to sensible error strings. + * + * ~~~~~ {.c} + * int result; + * + * result = dnssec_key_set_pubkey(NULL, NULL); + * assert(result == DNSSEC_EINVAL); + * + * fprintf(stderr, "Error: %s.\n", dnssec_strerror(result)); + * // Error: Invalid argument. + * ~~~~~ + * + * @{ + */ + +#pragma once + +#include <errno.h> + +/*! + * Library error codes. + */ +enum dnssec_error { + DNSSEC_EOK = 0, + + DNSSEC_ENOMEM = -ENOMEM, + DNSSEC_EINVAL = -EINVAL, + DNSSEC_ENOENT = -ENOENT, + + DNSSEC_ERROR_MIN = -1500, + + DNSSEC_ERROR = DNSSEC_ERROR_MIN, + DNSSEC_NOT_IMPLEMENTED_ERROR, + DNSSEC_MALFORMED_DATA, + DNSSEC_NOT_FOUND, + + DNSSEC_PKCS8_IMPORT_ERROR, + DNSSEC_KEY_EXPORT_ERROR, + DNSSEC_KEY_IMPORT_ERROR, + DNSSEC_KEY_GENERATE_ERROR, + + DNSSEC_INVALID_PUBLIC_KEY, + DNSSEC_INVALID_PRIVATE_KEY, + DNSSEC_INVALID_KEY_ALGORITHM, + DNSSEC_INVALID_KEY_SIZE, + DNSSEC_INVALID_KEY_ID, + DNSSEC_INVALID_KEY_NAME, + + DNSSEC_NO_PUBLIC_KEY, + DNSSEC_NO_PRIVATE_KEY, + DNSSEC_KEY_ALREADY_PRESENT, + + DNSSEC_SIGN_INIT_ERROR, + DNSSEC_SIGN_ERROR, + DNSSEC_INVALID_SIGNATURE, + + DNSSEC_INVALID_NSEC3_ALGORITHM, + DNSSEC_NSEC3_HASHING_ERROR, + + DNSSEC_INVALID_DS_ALGORITHM, + DNSSEC_DS_HASHING_ERROR, + + DNSSEC_KEYSTORE_INVALID_CONFIG, + + DNSSEC_P11_FAILED_TO_LOAD_MODULE, + DNSSEC_P11_TOO_MANY_MODULES, + DNSSEC_P11_TOKEN_NOT_AVAILABLE, + + DNSSEC_ERROR_MAX = -1001 +}; + +/*! + * Translate error code to error message. + * + * \param error Error code. + * + * \return Statically allocated error message string or NULL if unknown. + */ +const char *dnssec_strerror(int error); + +/*! + * Convert errno value to DNSSEC error code. + */ +static inline int dnssec_errno_to_error(int ecode) +{ + return -ecode; +} + +/*! @} */ diff --git a/src/libdnssec/key.h b/src/libdnssec/key.h new file mode 100644 index 0000000..27de778 --- /dev/null +++ b/src/libdnssec/key.h @@ -0,0 +1,318 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +/*! + * \file + * + * \addtogroup key + * + * \brief DNSSEC public and private key manipulation. + * + * The dnssec_key_t is an abstraction for a DNSSEC key pair. If the key + * key is initialized with a public key data only, it can be used only for + * signature verification. In order to use the key for signing, private key + * has to be loaded. If only a private key is loaded into the structure, + * the public key is automatically constructed. + * + * The module interface provides various functions to retrieve information + * about the key. But the key is mostly used by other modules of the library. + * + * The following example shows construction of a key from DNSKEY RDATA: + * + * ~~~~~ {.c} + * + * dnssec_binary_t rdata = // ...; + * + * int result; + * dnssec_key_t *key = NULL; + * + * // create new DNSSEC key + * result = dnssec_key_new(&key); + * if (result != DNSSEC_EOK) { + * return result; + * } + * + * // load the DNSKEY RDATA + * result = dnssec_key_set_rdata(key, &rdata); + * if (result != DNSSEC_EOK) { + * dnssec_key_free(key); + * return result; + * } + * + * // print key tag + * printf("key %s\n", dnssec_key_get_keytag(key)); + * + * // make sure what we can do with the key + * assert(dnssec_key_can_verify(key) == true); + * assert(dnssec_key_can_sign(key) == false); + * + * // ... + * + * // cleanup + * dnssec_key_free(key); + * + * ~~~~~ + * + * @{ + */ + +#pragma once + +#include <stdbool.h> +#include <stdint.h> + +#include <libdnssec/binary.h> + +/*! + * DNSKEY algorithm numbers. + * + * \see https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml + */ +typedef enum dnssec_key_algorithm { + DNSSEC_KEY_ALGORITHM_INVALID = 0, + DNSSEC_KEY_ALGORITHM_RSA_SHA1 = 5, + DNSSEC_KEY_ALGORITHM_RSA_SHA1_NSEC3 = 7, + DNSSEC_KEY_ALGORITHM_RSA_SHA256 = 8, + DNSSEC_KEY_ALGORITHM_RSA_SHA512 = 10, + DNSSEC_KEY_ALGORITHM_ECDSA_P256_SHA256 = 13, + DNSSEC_KEY_ALGORITHM_ECDSA_P384_SHA384 = 14, + DNSSEC_KEY_ALGORITHM_ED25519 = 15, + DNSSEC_KEY_ALGORITHM_ED448 = 16, +} dnssec_key_algorithm_t; + +struct dnssec_key; + +/*! + * DNSSEC key. + */ +typedef struct dnssec_key dnssec_key_t; + +/*! + * Check whether a DNSKEY algorithm is supported. + * + * @note: less secure algorithms may go unsupported on purpose. + */ +bool dnssec_algorithm_key_support(dnssec_key_algorithm_t algo); + +/*! + * Allocate new DNSSEC key. + * + * The protocol field of the key is set to 3 (DNSSEC). + * The flags field of the key is set to 256 (zone key, no SEP). + * + * \return Error code, DNSSEC_EOK if successful. + */ +int dnssec_key_new(dnssec_key_t **key); + +/*! + * Clear the DNSSEC key. + * + * Has the same effect as calling \ref dnssec_key_free and \ref dnssec_key_new. + */ +void dnssec_key_clear(dnssec_key_t *key); + +/*! + * Free the key allocated by \ref dnssec_key_new. + */ +void dnssec_key_free(dnssec_key_t *key); + +/*! + * Create a copy of a DNSSEC key. + * + * Only a public part of the key is copied. + */ +dnssec_key_t *dnssec_key_dup(const dnssec_key_t *key); + +/*! + * Get the key tag of the DNSSEC key. + */ +uint16_t dnssec_key_get_keytag(const dnssec_key_t *key); + +/*! + * Get the domain name of the DNSSEC key. + */ +const uint8_t *dnssec_key_get_dname(const dnssec_key_t *key); + +/*! + * Set the domain name of the DNSSEC key. + */ +int dnssec_key_set_dname(dnssec_key_t *key, const uint8_t *dname); + +/*! + * Get the flags field of the DNSSEC key. + */ +uint16_t dnssec_key_get_flags(const dnssec_key_t *key); + +/*! + * Set the flags field of the DNSSEC key. + */ +int dnssec_key_set_flags(dnssec_key_t *key, uint16_t flags); + +/*! + * Get the protocol field of the DNSSEC key. + */ +uint8_t dnssec_key_get_protocol(const dnssec_key_t *key); + +/*! + * Get the protocol field of the DNSSEC key. + */ +int dnssec_key_set_protocol(dnssec_key_t *key, uint8_t protocol); + +/*! + * Get the algorithm field of the DNSSEC key. + */ +uint8_t dnssec_key_get_algorithm(const dnssec_key_t *key); + +/*! + * Set the algorithm field of the DNSSEC key. + * + * The function will fail if the algorithm is incompatible with the + * loaded key. This means, that the function can be used to set the initial + * algorithm and later, only the hashing algorithm can be changed. + */ +int dnssec_key_set_algorithm(dnssec_key_t *key, uint8_t algorithm); + +/*! + * Get the public key field of the DNSSEC key. + * + * The returned content must not be modified by the caller. A reference + * to internally allocated structure is returned. + */ +int dnssec_key_get_pubkey(const dnssec_key_t *key, dnssec_binary_t *pubkey); + +/*! + * Set the public key field of the DNSSEC key. + * + * A valid algorithm has to be set prior to calling this function. + * + * The function will fail if the key is already loaded in the structure. + */ +int dnssec_key_set_pubkey(dnssec_key_t *key, const dnssec_binary_t *pubkey); + +/*! + * Get the bit size of the cryptographic key used with the DNSSEC key. + */ +unsigned dnssec_key_get_size(const dnssec_key_t *key); + +/*! + * \brief Compute key ID from public key. + * + * \param key Key structure holding the public key. + * \param id Output: key ID in hex. + * + * \return DNSSEC_E* + */ +int dnssec_key_get_keyid(const dnssec_key_t *key, char **id); + +/*! + * Get the RDATA of the DNSSEC key. + * + * The returned content must not be modified by the caller. A reference + * to internally allocated structure is returned. + */ +int dnssec_key_get_rdata(const dnssec_key_t *key, dnssec_binary_t *rdata); + +/*! + * Set the RDATA of the DNSSEC key. + * + * Calling this function has the same effect as setting the individual + * fields of the key step-by-step. The same limitations apply. + */ +int dnssec_key_set_rdata(dnssec_key_t *key, const dnssec_binary_t *rdata); + +/*! + * Load PKCS #8 private key in the unencrypted PEM format. + * + * At least an algorithm must be set prior to calling this function. + * + * The function will create public key, unless it was already set (using + * \ref dnssec_key_set_pubkey or \ref dnssec_key_set_rdata). If the public key + * was set, the function will prevent loading of non-matching private key. + */ +int dnssec_key_load_pkcs8(dnssec_key_t *key, const dnssec_binary_t *pem); + +/*! + * Check if the key can be used for signing. + */ +bool dnssec_key_can_sign(const dnssec_key_t *key); + +/*! + * Check if the key can be used for verification. + */ +bool dnssec_key_can_verify(const dnssec_key_t *key); + +/*! + * Get private key size range for a DNSSEC algorithm. + * + * \param[in] algorithm DNSKEY algorithm. + * \param[out] min Minimal size of the private key (can be NULL). + * \param[out] max Maximal size of the private key (can be NULL). + * + * \return DNSSEC_EOK for valid parameters. + */ +int dnssec_algorithm_key_size_range(dnssec_key_algorithm_t algorithm, + unsigned *min, unsigned *max); + +/*! + * Check if the private key size matches DNSKEY constraints. + * + * \param algorithm DNSKEY algorithm. + * \param bits Private key size. + * + * \return DNSKEY algorithm matches the key size constraints. + */ +bool dnssec_algorithm_key_size_check(dnssec_key_algorithm_t algorithm, + unsigned bits); + +/*! + * Get default key size for given algorithm. + * + * The default size is balance between security and response lengths with + * respect to use in DNS. + */ +int dnssec_algorithm_key_size_default(dnssec_key_algorithm_t algorithm); + +/*! + * DS algorithm numbers. + * + * \see https://www.iana.org/assignments/ds-rr-types/ds-rr-types.xhtml + */ +typedef enum dnssec_key_digest { + DNSSEC_KEY_DIGEST_INVALID = 0, + DNSSEC_KEY_DIGEST_SHA1 = 1, + DNSSEC_KEY_DIGEST_SHA256 = 2, + DNSSEC_KEY_DIGEST_SHA384 = 4, +} dnssec_key_digest_t; + +/*! + * Check whether a DS algorithm is supported. + * + * @note: less secure algorithms may go unsupported on purpose. + */ +bool dnssec_algorithm_digest_support(dnssec_key_digest_t algo); + +/*! + * Create DS (Delgation Signer) RDATA from DNSSEC key. + * + * \param[in] key DNSSEC key. + * \param[in] digest Digest algorithm to be used. + * \param[out] rdata Allocated DS RDATA. + * + * \return Error code, DNSSEC_EOK if successful. + */ +int dnssec_key_create_ds(const dnssec_key_t *key, dnssec_key_digest_t digest, + dnssec_binary_t *rdata); + +/** @} */ diff --git a/src/libdnssec/key/algorithm.c b/src/libdnssec/key/algorithm.c new file mode 100644 index 0000000..84f5386 --- /dev/null +++ b/src/libdnssec/key/algorithm.c @@ -0,0 +1,163 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <gnutls/gnutls.h> + +#include "libdnssec/error.h" +#include "libdnssec/key.h" +#include "libdnssec/key/algorithm.h" +#include "libdnssec/shared/shared.h" + +/* -- internal ------------------------------------------------------------- */ + +struct limits { + unsigned min; + unsigned max; + unsigned def; + bool (*validate)(unsigned bits); +}; + +static const struct limits *get_limits(dnssec_key_algorithm_t algorithm) +{ + static const struct limits RSA = { + .min = 1024, + .max = 4096, + .def = 2048, + }; + + static const struct limits EC256 = { + .min = 256, + .max = 256, + .def = 256, + }; + + static const struct limits EC384 = { + .min = 384, + .max = 384, + .def = 384, + }; + + static const struct limits ED25519 = { + .min = 256, + .max = 256, + .def = 256, + }; + + static const struct limits ED448 = { + .min = 456, + .max = 456, + .def = 456, + }; + + switch (algorithm) { + case DNSSEC_KEY_ALGORITHM_RSA_SHA1: + case DNSSEC_KEY_ALGORITHM_RSA_SHA1_NSEC3: + case DNSSEC_KEY_ALGORITHM_RSA_SHA256: + case DNSSEC_KEY_ALGORITHM_RSA_SHA512: + return &RSA; + case DNSSEC_KEY_ALGORITHM_ECDSA_P256_SHA256: + return &EC256; + case DNSSEC_KEY_ALGORITHM_ECDSA_P384_SHA384: + return &EC384; + case DNSSEC_KEY_ALGORITHM_ED25519: + return &ED25519; + case DNSSEC_KEY_ALGORITHM_ED448: + return &ED448; + default: + return NULL; + } +} + +/* -- internal API --------------------------------------------------------- */ + +gnutls_pk_algorithm_t algorithm_to_gnutls(dnssec_key_algorithm_t dnssec) +{ + switch (dnssec) { + case DNSSEC_KEY_ALGORITHM_RSA_SHA1: + case DNSSEC_KEY_ALGORITHM_RSA_SHA1_NSEC3: + case DNSSEC_KEY_ALGORITHM_RSA_SHA256: + case DNSSEC_KEY_ALGORITHM_RSA_SHA512: + return GNUTLS_PK_RSA; + case DNSSEC_KEY_ALGORITHM_ECDSA_P256_SHA256: + case DNSSEC_KEY_ALGORITHM_ECDSA_P384_SHA384: + return GNUTLS_PK_EC; + case DNSSEC_KEY_ALGORITHM_ED25519: +#ifdef HAVE_ED25519 + return GNUTLS_PK_EDDSA_ED25519; +#endif + case DNSSEC_KEY_ALGORITHM_ED448: + default: + return GNUTLS_PK_UNKNOWN; + } +} + +/* -- public API ----------------------------------------------------------- */ + +_public_ +bool dnssec_algorithm_key_support(dnssec_key_algorithm_t algo) +{ + return algorithm_to_gnutls(algo) != GNUTLS_PK_UNKNOWN; +} + +_public_ +int dnssec_algorithm_key_size_range(dnssec_key_algorithm_t algorithm, + unsigned *min_ptr, unsigned *max_ptr) +{ + if (!min_ptr && !max_ptr) { + return DNSSEC_EINVAL; + } + + const struct limits *limits = get_limits(algorithm); + if (!limits) { + return DNSSEC_INVALID_KEY_ALGORITHM; + } + + if (min_ptr) { + *min_ptr = limits->min; + } + if (max_ptr) { + *max_ptr = limits->max; + } + + return DNSSEC_EOK; +} + +_public_ +bool dnssec_algorithm_key_size_check(dnssec_key_algorithm_t algorithm, + unsigned bits) +{ + const struct limits *limits = get_limits(algorithm); + if (!limits) { + return false; + } + + if (bits < limits->min || bits > limits->max) { + return false; + } + + if (limits->validate && !limits->validate(bits)) { + return false; + } + + return true; +} + +_public_ +int dnssec_algorithm_key_size_default(dnssec_key_algorithm_t algorithm) +{ + const struct limits *limits = get_limits(algorithm); + return limits ? limits->def : 0; +} diff --git a/src/libdnssec/key/algorithm.h b/src/libdnssec/key/algorithm.h new file mode 100644 index 0000000..586682c --- /dev/null +++ b/src/libdnssec/key/algorithm.h @@ -0,0 +1,30 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include <gnutls/gnutls.h> + +#include "libdnssec/key.h" + +/*! + * Convert DNSKEY algorithm identifier to GnuTLS identifier. + * + * \param dnssec DNSSEC DNSKEY algorithm identifier. + * + * \return GnuTLS private key algorithm identifier, GNUTLS_PK_UNKNOWN on error. + */ +gnutls_pk_algorithm_t algorithm_to_gnutls(dnssec_key_algorithm_t dnssec); diff --git a/src/libdnssec/key/convert.c b/src/libdnssec/key/convert.c new file mode 100644 index 0000000..b8ebb7d --- /dev/null +++ b/src/libdnssec/key/convert.c @@ -0,0 +1,375 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <assert.h> +#include <gnutls/abstract.h> +#include <gnutls/gnutls.h> +#include <stdbool.h> +#include <stdint.h> +#include <string.h> + +#include "libdnssec/shared/bignum.h" +#include "libdnssec/binary.h" +#include "libdnssec/error.h" +#include "libdnssec/key.h" +#include "libdnssec/key/algorithm.h" +#include "libdnssec/key/dnskey.h" +#include "libdnssec/shared/shared.h" +#include "libdnssec/shared/binary_wire.h" + +/* -- wrappers for GnuTLS types -------------------------------------------- */ + +static size_t bignum_size_u_datum(const gnutls_datum_t *_bignum) +{ + const dnssec_binary_t bignum = binary_from_datum(_bignum); + return bignum_size_u(&bignum); +} + +static void wire_write_bignum_datum(wire_ctx_t *ctx, size_t width, + const gnutls_datum_t *_bignum) +{ + const dnssec_binary_t bignum = binary_from_datum(_bignum); + bignum_write(ctx, width, &bignum); +} + +static gnutls_datum_t wire_take_datum(wire_ctx_t *ctx, size_t count) +{ + gnutls_datum_t result = { .data = ctx->position, .size = count }; + ctx->position += count; + + return result; +} + +/* -- DNSSEC to crypto ------------------------------------------------------*/ + +/*! + * Convert RSA public key to DNSSEC format. + */ +static int rsa_pubkey_to_rdata(gnutls_pubkey_t key, dnssec_binary_t *rdata) +{ + assert(key); + assert(rdata); + + _cleanup_datum_ gnutls_datum_t modulus = { 0 }; + _cleanup_datum_ gnutls_datum_t exponent = { 0 }; + + int result = gnutls_pubkey_get_pk_rsa_raw(key, &modulus, &exponent); + if (result != GNUTLS_E_SUCCESS) { + return DNSSEC_KEY_EXPORT_ERROR; + } + + size_t exponent_size = bignum_size_u_datum(&exponent); + if (exponent_size > UINT8_MAX) { + return DNSSEC_KEY_EXPORT_ERROR; + } + + size_t modulus_size = bignum_size_u_datum(&modulus); + + result = dnssec_binary_alloc(rdata, 1 + exponent_size + modulus_size); + if (result != DNSSEC_EOK) { + return result; + } + + wire_ctx_t wire = binary_init(rdata); + wire_ctx_write_u8(&wire, exponent_size); + wire_write_bignum_datum(&wire, exponent_size, &exponent); + wire_write_bignum_datum(&wire, modulus_size, &modulus); + assert(wire_ctx_offset(&wire) == rdata->size); + + return DNSSEC_EOK; +} + +/*! + * Get point size for an ECDSA curve. + */ +static size_t ecdsa_curve_point_size(gnutls_ecc_curve_t curve) +{ + switch (curve) { + case GNUTLS_ECC_CURVE_SECP256R1: return 32; + case GNUTLS_ECC_CURVE_SECP384R1: return 48; + default: return 0; + } +} + +#if defined(HAVE_ED25519) || defined(HAVE_ED448) +static size_t eddsa_curve_point_size(gnutls_ecc_curve_t curve) +{ + switch (curve) { +#ifdef HAVE_ED25519 + case GNUTLS_ECC_CURVE_ED25519: return 32; +#endif +#ifdef HAVE_ED448 + case GNUTLS_ECC_CURVE_ED448: return 57; +#endif + default: return 0; + } +} +#endif + +/*! + * Convert ECDSA public key to DNSSEC format. + */ +static int ecdsa_pubkey_to_rdata(gnutls_pubkey_t key, dnssec_binary_t *rdata) +{ + assert(key); + assert(rdata); + + _cleanup_datum_ gnutls_datum_t point_x = { 0 }; + _cleanup_datum_ gnutls_datum_t point_y = { 0 }; + gnutls_ecc_curve_t curve = { 0 }; + + int result = gnutls_pubkey_get_pk_ecc_raw(key, &curve, &point_x, &point_y); + if (result != GNUTLS_E_SUCCESS) { + return DNSSEC_KEY_EXPORT_ERROR; + } + + size_t point_size = ecdsa_curve_point_size(curve); + if (point_size == 0) { + return DNSSEC_INVALID_PUBLIC_KEY; + } + + result = dnssec_binary_alloc(rdata, 2 * point_size); + if (result != DNSSEC_EOK) { + return result; + } + + wire_ctx_t wire = binary_init(rdata); + wire_write_bignum_datum(&wire, point_size, &point_x); + wire_write_bignum_datum(&wire, point_size, &point_y); + assert(wire_ctx_offset(&wire) == rdata->size); + + return DNSSEC_EOK; +} + +/*! + * Convert EDDSA public key to DNSSEC format. + */ +#if defined(HAVE_ED25519) || defined(HAVE_ED448) +static int eddsa_pubkey_to_rdata(gnutls_pubkey_t key, dnssec_binary_t *rdata) +{ + assert(key); + assert(rdata); + + _cleanup_datum_ gnutls_datum_t point_x = { 0 }; + gnutls_ecc_curve_t curve = { 0 }; + + int result = gnutls_pubkey_get_pk_ecc_raw(key, &curve, &point_x, NULL); + if (result != GNUTLS_E_SUCCESS) { + return DNSSEC_KEY_EXPORT_ERROR; + } + + size_t point_size = eddsa_curve_point_size(curve); + if (point_size == 0) { + return DNSSEC_INVALID_PUBLIC_KEY; + } + + result = dnssec_binary_alloc(rdata, point_size); + if (result != DNSSEC_EOK) { + return result; + } + + wire_ctx_t wire = binary_init(rdata); + wire_write_bignum_datum(&wire, point_size, &point_x); + assert(wire_ctx_offset(&wire) == rdata->size); + + return DNSSEC_EOK; +} +#endif + +/* -- crypto to DNSSEC ------------------------------------------------------*/ + +/*! + * Convert RSA key in DNSSEC format to crypto key. + */ +static int rsa_rdata_to_pubkey(const dnssec_binary_t *rdata, gnutls_pubkey_t key) +{ + assert(rdata); + assert(key); + + if (rdata->size == 0) { + return DNSSEC_INVALID_PUBLIC_KEY; + } + + wire_ctx_t ctx = binary_init(rdata); + + // parse public exponent + + uint8_t exponent_size = wire_ctx_read_u8(&ctx); + if (exponent_size == 0 || wire_ctx_available(&ctx) < exponent_size) { + return DNSSEC_INVALID_PUBLIC_KEY; + } + + gnutls_datum_t exponent = wire_take_datum(&ctx, exponent_size); + + // parse modulus + + size_t modulus_size = wire_ctx_available(&ctx); + if (modulus_size == 0) { + return DNSSEC_INVALID_PUBLIC_KEY; + } + + gnutls_datum_t modulus = wire_take_datum(&ctx, modulus_size); + + assert(wire_ctx_offset(&ctx) == rdata->size); + + int result = gnutls_pubkey_import_rsa_raw(key, &modulus, &exponent); + if (result != GNUTLS_E_SUCCESS) { + return DNSSEC_KEY_IMPORT_ERROR; + } + + return DNSSEC_EOK; +} + +/** + * Get ECDSA curve based on DNSKEY RDATA size. + */ +static gnutls_ecc_curve_t ecdsa_curve_from_rdata_size(size_t rdata_size) +{ + switch (rdata_size) { + case 64: return GNUTLS_ECC_CURVE_SECP256R1; + case 96: return GNUTLS_ECC_CURVE_SECP384R1; + default: return GNUTLS_ECC_CURVE_INVALID; + } +} + +/** + * Get EDDSA curve based on DNSKEY RDATA size. + */ +#if defined(HAVE_ED25519) || defined(HAVE_ED448) +static gnutls_ecc_curve_t eddsa_curve_from_rdata_size(size_t rdata_size) +{ + switch (rdata_size) { +#ifdef HAVE_ED25519 + case 32: return GNUTLS_ECC_CURVE_ED25519; +#endif +#ifdef HAVE_ED448 + case 57: return GNUTLS_ECC_CURVE_ED448; +#endif + default: return GNUTLS_ECC_CURVE_INVALID; + } +} +#endif + +/*! + * Convert ECDSA key in DNSSEC format to crypto key. + */ +static int ecdsa_rdata_to_pubkey(const dnssec_binary_t *rdata, gnutls_pubkey_t key) +{ + assert(rdata); + assert(key); + + gnutls_ecc_curve_t curve = ecdsa_curve_from_rdata_size(rdata->size); + if (curve == GNUTLS_ECC_CURVE_INVALID) { + return DNSSEC_INVALID_PUBLIC_KEY; + } + + // parse points + + wire_ctx_t ctx = binary_init(rdata); + + size_t point_size = wire_ctx_available(&ctx) / 2; + gnutls_datum_t point_x = wire_take_datum(&ctx, point_size); + gnutls_datum_t point_y = wire_take_datum(&ctx, point_size); + assert(wire_ctx_offset(&ctx) == rdata->size); + + int result = gnutls_pubkey_import_ecc_raw(key, curve, &point_x, &point_y); + if (result != GNUTLS_E_SUCCESS) { + return DNSSEC_KEY_IMPORT_ERROR; + } + + return DNSSEC_EOK; +} + +/*! + * Convert EDDSA key in DNSSEC format to crypto key. + */ +#if defined(HAVE_ED25519) || defined(HAVE_ED448) +static int eddsa_rdata_to_pubkey(const dnssec_binary_t *rdata, gnutls_pubkey_t key) +{ + assert(rdata); + assert(key); + + gnutls_ecc_curve_t curve = eddsa_curve_from_rdata_size(rdata->size); + if (curve == GNUTLS_ECC_CURVE_INVALID) { + return DNSSEC_INVALID_PUBLIC_KEY; + } + + wire_ctx_t ctx = binary_init(rdata); + + size_t point_size = wire_ctx_available(&ctx); + gnutls_datum_t point_x = wire_take_datum(&ctx, point_size); + assert(wire_ctx_offset(&ctx) == rdata->size); + + int result = gnutls_pubkey_import_ecc_raw(key, curve, &point_x, NULL); + if (result != GNUTLS_E_SUCCESS) { + return DNSSEC_KEY_IMPORT_ERROR; + } + + return DNSSEC_EOK; +} +#endif + +/* -- internal API --------------------------------------------------------- */ + +/*! + * Encode public key to the format used in DNSKEY RDATA. + */ +int convert_pubkey_to_dnskey(gnutls_pubkey_t key, dnssec_binary_t *rdata) +{ + assert(key); + assert(rdata); + + int algorithm = gnutls_pubkey_get_pk_algorithm(key, NULL); + if (algorithm < 0) { + return DNSSEC_INVALID_PUBLIC_KEY; + } + + switch ((gnutls_pk_algorithm_t)algorithm) { + case GNUTLS_PK_RSA: return rsa_pubkey_to_rdata(key, rdata); + case GNUTLS_PK_EC: return ecdsa_pubkey_to_rdata(key, rdata); +#ifdef HAVE_ED25519 + case GNUTLS_PK_EDDSA_ED25519: return eddsa_pubkey_to_rdata(key, rdata); +#endif +#ifdef HAVE_ED448 + case GNUTLS_PK_EDDSA_ED448: return eddsa_pubkey_to_rdata(key, rdata); +#endif + default: return DNSSEC_INVALID_KEY_ALGORITHM; + } +} + +/*! + * Create public key from the format encoded in DNSKEY RDATA. + */ +int convert_dnskey_to_pubkey(uint8_t algorithm, const dnssec_binary_t *rdata, + gnutls_pubkey_t key) +{ + assert(rdata); + assert(key); + + gnutls_pk_algorithm_t gnutls_alg = algorithm_to_gnutls(algorithm); + + switch(gnutls_alg) { + case GNUTLS_PK_RSA: return rsa_rdata_to_pubkey(rdata, key); + case GNUTLS_PK_EC: return ecdsa_rdata_to_pubkey(rdata, key); +#ifdef HAVE_ED25519 + case GNUTLS_PK_EDDSA_ED25519: return eddsa_rdata_to_pubkey(rdata, key); +#endif +#ifdef HAVE_ED448 + case GNUTLS_PK_EDDSA_ED448: return eddsa_rdata_to_pubkey(rdata, key); +#endif + default: return DNSSEC_INVALID_KEY_ALGORITHM; + } +} diff --git a/src/libdnssec/key/convert.h b/src/libdnssec/key/convert.h new file mode 100644 index 0000000..bd2196e --- /dev/null +++ b/src/libdnssec/key/convert.h @@ -0,0 +1,44 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include <gnutls/abstract.h> + +#include "libdnssec/binary.h" +#include "libdnssec/key.h" + +/*! + * Encode public key into the format used in DNSKEY RDATA. + * + * \param[in] key Public key to be encoded. + * \param[out] rdata Encoded key (allocated). + * + * \return Error code, DNSSEC_EOK if successful. + */ +int convert_pubkey_to_dnskey(gnutls_pubkey_t key, dnssec_binary_t *rdata); + +/*! + * Create public key from the format encoded in DNSKEY RDATA. + * + * \param[in] algorithm DNSSEC algorithm identification. + * \param[in] rdata Public key in DNSKEY RDATA format. + * \param[out] key GnuTLS public key (initialized). + * + * \return Error code, DNSSEC_EOK if successful. + */ +int convert_dnskey_to_pubkey(uint8_t algorithm, const dnssec_binary_t *rdata, + gnutls_pubkey_t key); diff --git a/src/libdnssec/key/dnskey.c b/src/libdnssec/key/dnskey.c new file mode 100644 index 0000000..dc89bcd --- /dev/null +++ b/src/libdnssec/key/dnskey.c @@ -0,0 +1,82 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "libdnssec/binary.h" +#include "libdnssec/error.h" +#include "libdnssec/key/dnskey.h" +#include "libdnssec/key/convert.h" +#include "libdnssec/shared/binary_wire.h" + +/* -- internal API --------------------------------------------------------- */ + +/*! + * Update 'Public key' field of DNSKEY RDATA. + */ +int dnskey_rdata_set_pubkey(dnssec_binary_t *rdata, const dnssec_binary_t *pubkey) +{ + assert(rdata); + assert(pubkey); + + size_t new_size = DNSKEY_RDATA_OFFSET_PUBKEY + pubkey->size; + int result = dnssec_binary_resize(rdata, new_size); + if (result != DNSSEC_EOK) { + return result; + } + + wire_ctx_t wire = binary_init(rdata); + wire_ctx_set_offset(&wire, DNSKEY_RDATA_OFFSET_PUBKEY); + binary_write(&wire, pubkey); + assert(wire_ctx_offset(&wire) == rdata->size); + + return DNSSEC_EOK; +} + +/*! + * Create a GnuTLS public key from DNSKEY RDATA. + * + * \param rdata DNSKEY RDATA. + * \param key_ptr Resulting public key. + */ +int dnskey_rdata_to_crypto_key(const dnssec_binary_t *rdata, gnutls_pubkey_t *key_ptr) +{ + assert(rdata); + assert(key_ptr); + + uint8_t algorithm = 0; + dnssec_binary_t rdata_pubkey = { 0 }; + + wire_ctx_t wire = binary_init(rdata); + wire_ctx_set_offset(&wire, DNSKEY_RDATA_OFFSET_ALGORITHM); + algorithm = wire_ctx_read_u8(&wire); + wire_ctx_set_offset(&wire, DNSKEY_RDATA_OFFSET_PUBKEY); + binary_available(&wire, &rdata_pubkey); + + gnutls_pubkey_t key = NULL; + int result = gnutls_pubkey_init(&key); + if (result != GNUTLS_E_SUCCESS) { + return DNSSEC_ENOMEM; + } + + result = convert_dnskey_to_pubkey(algorithm, &rdata_pubkey, key); + if (result != DNSSEC_EOK) { + gnutls_pubkey_deinit(key); + return result; + } + + *key_ptr = key; + + return DNSSEC_EOK; +} diff --git a/src/libdnssec/key/dnskey.h b/src/libdnssec/key/dnskey.h new file mode 100644 index 0000000..de681c8 --- /dev/null +++ b/src/libdnssec/key/dnskey.h @@ -0,0 +1,46 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include <gnutls/abstract.h> + +#include "libdnssec/binary.h" +#include "libdnssec/error.h" + +/*! + * DNSKEY RDATA fields offsets. + * + * \see RFC 4034 (section 2.1) + */ +enum dnskey_rdata_offsets { + DNSKEY_RDATA_OFFSET_FLAGS = 0, + DNSKEY_RDATA_OFFSET_PROTOCOL = 2, + DNSKEY_RDATA_OFFSET_ALGORITHM = 3, + DNSKEY_RDATA_OFFSET_PUBKEY = 4, +}; + +/*! + * Update 'Public key' field of DNSKEY RDATA. + */ +int dnskey_rdata_set_pubkey(dnssec_binary_t *rdata, + const dnssec_binary_t *pubkey); + +/*! + * Create a GnuTLS public key from DNSKEY RDATA. + */ +int dnskey_rdata_to_crypto_key(const dnssec_binary_t *rdata, + gnutls_pubkey_t *key_ptr); diff --git a/src/libdnssec/key/ds.c b/src/libdnssec/key/ds.c new file mode 100644 index 0000000..dcb4afe --- /dev/null +++ b/src/libdnssec/key/ds.c @@ -0,0 +1,115 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "libdnssec/binary.h" +#include "libdnssec/error.h" +#include "libdnssec/key.h" +#include "libdnssec/key/internal.h" +#include "libdnssec/shared/dname.h" +#include "libdnssec/shared/shared.h" +#include "libdnssec/shared/binary_wire.h" + +#include <gnutls/gnutls.h> +#include <gnutls/crypto.h> + +/*! + * Convert DNSSEC DS digest algorithm to GnuTLS digest algorithm. + */ +static gnutls_digest_algorithm_t lookup_algorithm(dnssec_key_digest_t algorithm) +{ + switch (algorithm) { + case DNSSEC_KEY_DIGEST_SHA1: return GNUTLS_DIG_SHA1; + case DNSSEC_KEY_DIGEST_SHA256: return GNUTLS_DIG_SHA256; + case DNSSEC_KEY_DIGEST_SHA384: return GNUTLS_DIG_SHA384; + default: + return GNUTLS_DIG_UNKNOWN; + }; +} + +_public_ +bool dnssec_algorithm_digest_support(dnssec_key_digest_t algo) +{ + return lookup_algorithm(algo) != GNUTLS_DIG_UNKNOWN; +} + +static void wire_write_digest(wire_ctx_t *wire, + gnutls_hash_hd_t digest, int digest_size) +{ + assert(wire_ctx_available(wire) >= digest_size); + gnutls_hash_output(digest, wire->position); + wire->position += digest_size; +} + +_public_ +int dnssec_key_create_ds(const dnssec_key_t *key, + dnssec_key_digest_t ds_algorithm, + dnssec_binary_t *rdata_ptr) +{ + if (!key || !rdata_ptr) { + return DNSSEC_EINVAL; + } + + if (!key->dname) { + return DNSSEC_INVALID_KEY_NAME; + } + + if (!key->public_key){ + return DNSSEC_INVALID_PUBLIC_KEY; + } + + gnutls_digest_algorithm_t algorithm = lookup_algorithm(ds_algorithm); + if (algorithm == GNUTLS_DIG_UNKNOWN) { + return DNSSEC_INVALID_DS_ALGORITHM; + } + + // compute DS hash + + _cleanup_hash_ gnutls_hash_hd_t digest = NULL; + int r = gnutls_hash_init(&digest, algorithm); + if (r < 0) { + return DNSSEC_DS_HASHING_ERROR; + } + + if (gnutls_hash(digest, key->dname, dname_length(key->dname)) != 0 || + gnutls_hash(digest, key->rdata.data, key->rdata.size) != 0 + ) { + return DNSSEC_DS_HASHING_ERROR; + } + + // build DS RDATA + + int digest_size = gnutls_hash_get_len(algorithm); + if (digest_size == 0) { + return DNSSEC_DS_HASHING_ERROR; + } + + dnssec_binary_t rdata = { 0 }; + r = dnssec_binary_alloc(&rdata, 4 + digest_size); + if (r != DNSSEC_EOK) { + return r; + } + + wire_ctx_t wire = binary_init(&rdata); + wire_ctx_write_u16(&wire, dnssec_key_get_keytag(key)); + wire_ctx_write_u8(&wire, dnssec_key_get_algorithm(key)); + wire_ctx_write_u8(&wire, ds_algorithm); + wire_write_digest(&wire, digest, digest_size); + assert(wire_ctx_offset(&wire) == wire.size); + + *rdata_ptr = rdata; + + return DNSSEC_EOK; +} diff --git a/src/libdnssec/key/internal.h b/src/libdnssec/key/internal.h new file mode 100644 index 0000000..10c9c8e --- /dev/null +++ b/src/libdnssec/key/internal.h @@ -0,0 +1,35 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include <gnutls/abstract.h> +#include <stdint.h> + +#include "libdnssec/key.h" +#include "libdnssec/shared/dname.h" + +/*! + * DNSSEC key. + */ +struct dnssec_key { + uint8_t *dname; + dnssec_binary_t rdata; + + gnutls_pubkey_t public_key; + gnutls_privkey_t private_key; + unsigned bits; +}; diff --git a/src/libdnssec/key/key.c b/src/libdnssec/key/key.c new file mode 100644 index 0000000..6f24f87 --- /dev/null +++ b/src/libdnssec/key/key.c @@ -0,0 +1,440 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <assert.h> +#include <gnutls/abstract.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> + +#include "libdnssec/binary.h" +#include "libdnssec/error.h" +#include "libdnssec/key.h" +#include "libdnssec/key/algorithm.h" +#include "libdnssec/key/convert.h" +#include "libdnssec/key/dnskey.h" +#include "libdnssec/key/internal.h" +#include "libdnssec/shared/keyid_gnutls.h" +#include "libdnssec/keystore.h" +#include "libdnssec/keytag.h" +#include "libdnssec/shared/pem.h" +#include "libdnssec/shared/shared.h" +#include "libdnssec/shared/binary_wire.h" +#include "contrib/wire_ctx.h" + +/*! + * Minimal size of DNSKEY RDATA. + */ +#define DNSKEY_RDATA_MIN_SIZE DNSKEY_RDATA_OFFSET_PUBKEY + +/*! + * RDATA template for newly allocated keys. + */ +static const dnssec_binary_t DNSKEY_RDATA_TEMPLATE = { + .size = 4, + .data = (uint8_t []) { 0x01, 0x00, 0x03, 0x00 } +}; + +/* -- key allocation ------------------------------------------------------- */ + +_public_ +int dnssec_key_new(dnssec_key_t **key_ptr) +{ + if (!key_ptr) { + return DNSSEC_EINVAL; + } + + dnssec_key_t *key = calloc(1, sizeof(*key)); + if (!key) { + return DNSSEC_ENOMEM; + } + + int r = dnssec_binary_dup(&DNSKEY_RDATA_TEMPLATE, &key->rdata); + if (r != DNSSEC_EOK) { + free(key); + return DNSSEC_ENOMEM; + } + + *key_ptr = key; + + return DNSSEC_EOK; +} + +/*! + * Clear allocated fields inside the key structure, except RDATA. + */ +static void key_free_internals(dnssec_key_t *key) +{ + assert(key); + + free(key->dname); + key->dname = NULL; + + gnutls_privkey_deinit(key->private_key); + key->private_key = NULL; + + gnutls_pubkey_deinit(key->public_key); + key->public_key = NULL; +} + +_public_ +void dnssec_key_clear(dnssec_key_t *key) +{ + if (!key) { + return; + } + + // reuse RDATA + dnssec_binary_t rdata = key->rdata; + + // clear the structure + key_free_internals(key); + clear_struct(key); + + // restore template RDATA (downsize, no need to realloc) + assert(rdata.size >= DNSKEY_RDATA_MIN_SIZE); + rdata.size = DNSKEY_RDATA_MIN_SIZE; + memmove(rdata.data, DNSKEY_RDATA_TEMPLATE.data, rdata.size); + + key->rdata = rdata; +} + +_public_ +void dnssec_key_free(dnssec_key_t *key) +{ + if (!key) { + return; + } + + key_free_internals(key); + dnssec_binary_free(&key->rdata); + + free(key); +} + +_public_ +dnssec_key_t *dnssec_key_dup(const dnssec_key_t *key) +{ + if (!key) { + return NULL; + } + + dnssec_key_t *dup = NULL; + + if (dnssec_key_new(&dup) != DNSSEC_EOK || + dnssec_key_set_dname(dup, key->dname) != DNSSEC_EOK || + dnssec_key_set_rdata(dup, &key->rdata) != DNSSEC_EOK + ) { + dnssec_key_free(dup); + return NULL; + } + + return dup; +} + +/* -- freely modifiable attributes ----------------------------------------- */ + +_public_ +const uint8_t *dnssec_key_get_dname(const dnssec_key_t *key) +{ + if (!key) { + return NULL; + } + + return key->dname; +} + +_public_ +int dnssec_key_set_dname(dnssec_key_t *key, const uint8_t *dname) +{ + if (!key) { + return DNSSEC_EINVAL; + } + + uint8_t *copy = NULL; + if (dname) { + copy = dname_copy(dname); + if (!copy) { + return DNSSEC_ENOMEM; + } + + dname_normalize(copy); + } + + free(key->dname); + key->dname = copy; + + return DNSSEC_EOK; +} + +_public_ +uint16_t dnssec_key_get_flags(const dnssec_key_t *key) +{ + if (!key) { + return 0; + } + + wire_ctx_t wire = binary_init(&key->rdata); + wire_ctx_set_offset(&wire, DNSKEY_RDATA_OFFSET_FLAGS); + return wire_ctx_read_u16(&wire); +} + +_public_ +int dnssec_key_set_flags(dnssec_key_t *key, uint16_t flags) +{ + if (!key) { + return DNSSEC_EINVAL; + } + + wire_ctx_t wire = binary_init(&key->rdata); + wire_ctx_set_offset(&wire, DNSKEY_RDATA_OFFSET_FLAGS); + wire_ctx_write_u16(&wire, flags); + + return DNSSEC_EOK; +} + +_public_ +uint8_t dnssec_key_get_protocol(const dnssec_key_t *key) +{ + if (!key) { + return 0; + } + + wire_ctx_t wire = binary_init(&key->rdata); + wire_ctx_set_offset(&wire, DNSKEY_RDATA_OFFSET_PROTOCOL); + return wire_ctx_read_u8(&wire); +} + +_public_ +int dnssec_key_set_protocol(dnssec_key_t *key, uint8_t protocol) +{ + if (!key) { + return DNSSEC_EINVAL; + } + + wire_ctx_t wire = binary_init(&key->rdata); + wire_ctx_set_offset(&wire, DNSKEY_RDATA_OFFSET_PROTOCOL); + wire_ctx_write_u8(&wire, protocol); + + return DNSSEC_EOK; +} + +/* -- restricted attributes ------------------------------------------------ */ + +_public_ +uint16_t dnssec_key_get_keytag(const dnssec_key_t *key) +{ + uint16_t keytag = 0; + if (dnssec_key_can_verify(key)) { + dnssec_keytag(&key->rdata, &keytag); + } + + return keytag; +} + +/*! + * Check if current public key algorithm matches with the new algorithm. + */ +static bool can_change_algorithm(dnssec_key_t *key, uint8_t algorithm) +{ + assert(key); + + if (!key->public_key) { + return true; + } + + gnutls_pk_algorithm_t update = algorithm_to_gnutls(algorithm); + if (update == GNUTLS_PK_UNKNOWN) { + return false; + } + + int current = gnutls_pubkey_get_pk_algorithm(key->public_key, NULL); + assert(current >= 0); + + return current == update; +} + +_public_ +uint8_t dnssec_key_get_algorithm(const dnssec_key_t *key) +{ + if (!key) { + return 0; + } + + wire_ctx_t wire = binary_init(&key->rdata); + wire_ctx_set_offset(&wire, DNSKEY_RDATA_OFFSET_ALGORITHM); + return wire_ctx_read_u8(&wire); +} + +_public_ +int dnssec_key_set_algorithm(dnssec_key_t *key, uint8_t algorithm) +{ + if (!key) { + return DNSSEC_EINVAL; + } + + if (!can_change_algorithm(key, algorithm)) { + return DNSSEC_INVALID_KEY_ALGORITHM; + } + + wire_ctx_t wire = binary_init(&key->rdata); + wire_ctx_set_offset(&wire, DNSKEY_RDATA_OFFSET_ALGORITHM); + wire_ctx_write_u8(&wire, algorithm); + + return DNSSEC_EOK; +} + +_public_ +int dnssec_key_get_pubkey(const dnssec_key_t *key, dnssec_binary_t *pubkey) +{ + if (!key || !pubkey) { + return DNSSEC_EINVAL; + } + + wire_ctx_t wire = binary_init(&key->rdata); + wire_ctx_set_offset(&wire, DNSKEY_RDATA_OFFSET_PUBKEY); + binary_available(&wire, pubkey); + + return DNSSEC_EOK; +} + +_public_ +int dnssec_key_set_pubkey(dnssec_key_t *key, const dnssec_binary_t *pubkey) +{ + if (!key || !pubkey || !pubkey->data) { + return DNSSEC_EINVAL; + } + + if (key->public_key) { + return DNSSEC_KEY_ALREADY_PRESENT; + } + + if (dnssec_key_get_algorithm(key) == 0) { + return DNSSEC_INVALID_KEY_ALGORITHM; + } + + int result = dnskey_rdata_set_pubkey(&key->rdata, pubkey); + if (result != DNSSEC_EOK) { + return result; + } + + result = dnskey_rdata_to_crypto_key(&key->rdata, &key->public_key); + if (result != DNSSEC_EOK) { + key->rdata.size = DNSKEY_RDATA_OFFSET_PUBKEY; // downsize + return result; + } + + return DNSSEC_EOK; +} + +_public_ +unsigned dnssec_key_get_size(const dnssec_key_t *key) +{ + if (!key || !key->public_key) { + return 0; + } + + unsigned bits = 0; + uint8_t algorithm = dnssec_key_get_algorithm(key); + switch (algorithm) { + case 13: + bits = 256; + break; + case 14: + bits = 384; + break; + case 15: + bits = 256; + break; + case 16: + bits = 456; + break; + default: + gnutls_pubkey_get_pk_algorithm(key->public_key, &bits); + } + + return bits; +} + +_public_ +int dnssec_key_get_keyid(const dnssec_key_t *key, char **id) +{ + if (!key || !id) { + return DNSSEC_EINVAL; + } + + return keyid_pubkey_hex(key->public_key, id); +} + +_public_ +int dnssec_key_get_rdata(const dnssec_key_t *key, dnssec_binary_t *rdata) +{ + if (!key || !rdata) { + return DNSSEC_EINVAL; + } + + *rdata = key->rdata; + + return DNSSEC_EOK; +} + +_public_ +int dnssec_key_set_rdata(dnssec_key_t *key, const dnssec_binary_t *rdata) +{ + if (!key || !rdata || !rdata->data) { + return DNSSEC_EINVAL; + } + + if (rdata->size < DNSKEY_RDATA_MIN_SIZE) { + return DNSSEC_MALFORMED_DATA; + } + + if (key->public_key) { + return DNSSEC_KEY_ALREADY_PRESENT; + } + + gnutls_pubkey_t new_pubkey = NULL; + int result = dnskey_rdata_to_crypto_key(rdata, &new_pubkey); + if (result != DNSSEC_EOK) { + return result; + } + + result = dnssec_binary_resize(&key->rdata, rdata->size); + if (result != DNSSEC_EOK) { + gnutls_pubkey_deinit(new_pubkey); + return result; + } + + // commit result + memmove(key->rdata.data, rdata->data, rdata->size); + key->public_key = new_pubkey; + + return DNSSEC_EOK; +} + +/* -- key presence checking ------------------------------------------------ */ + +_public_ +bool dnssec_key_can_sign(const dnssec_key_t *key) +{ + return key && key->private_key; +} + +_public_ +bool dnssec_key_can_verify(const dnssec_key_t *key) +{ + return key && key->public_key; +} diff --git a/src/libdnssec/key/keytag.c b/src/libdnssec/key/keytag.c new file mode 100644 index 0000000..edff684 --- /dev/null +++ b/src/libdnssec/key/keytag.c @@ -0,0 +1,88 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <assert.h> +#include <stdint.h> +#include <string.h> + +#include "libdnssec/binary.h" +#include "libdnssec/error.h" +#include "libdnssec/key/dnskey.h" +#include "libdnssec/shared/shared.h" + +/*! + * Compute keytag for RSA/MD5 key. + * + * \see RFC 2537 (section 2), RFC 4034 (appendix B.1) + */ +static uint16_t keytag_compat(const dnssec_binary_t *rdata) +{ + assert(rdata); + assert(rdata->data); + + if (rdata->size < 9) { // in fact, the condition could be stricter + return 0; + } + + uint8_t msb = rdata->data[rdata->size - 3]; + uint8_t lsb = rdata->data[rdata->size - 2]; + + return (msb << 8) + lsb; +} + +/*! + * Compute keytag for other than RSA/MD5 key. + * + * \see RFC 4034 (appendix B) + */ +static uint16_t keytag_current(const dnssec_binary_t *rdata) +{ + assert(rdata); + assert(rdata->data); + + uint32_t ac = 0; + for (int i = 0; i < rdata->size; i++) { + ac += (i & 1) ? rdata->data[i] : rdata->data[i] << 8; + } + + return (ac >> 16) + ac; +} + +/* -- public API ----------------------------------------------------------- */ + +/*! + * Compute keytag for a DNSSEC key. + */ +_public_ +int dnssec_keytag(const dnssec_binary_t *rdata, uint16_t *keytag) +{ + if (!rdata || !keytag) { + return DNSSEC_EINVAL; + } + + if (!rdata->data || rdata->size < DNSKEY_RDATA_OFFSET_PUBKEY) { + return DNSSEC_MALFORMED_DATA; + } + + uint8_t algorithm = rdata->data[DNSKEY_RDATA_OFFSET_ALGORITHM]; + if (algorithm == 1) { + *keytag = keytag_compat(rdata); + } else { + *keytag = keytag_current(rdata); + } + + return DNSSEC_EOK; +} diff --git a/src/libdnssec/key/privkey.c b/src/libdnssec/key/privkey.c new file mode 100644 index 0000000..ec1dcbd --- /dev/null +++ b/src/libdnssec/key/privkey.c @@ -0,0 +1,140 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <gnutls/abstract.h> +#include <gnutls/gnutls.h> + +#include "libdnssec/binary.h" +#include "libdnssec/error.h" +#include "libdnssec/key/algorithm.h" +#include "libdnssec/key/convert.h" +#include "libdnssec/key/dnskey.h" +#include "libdnssec/key/internal.h" +#include "libdnssec/key/privkey.h" +#include "libdnssec/shared/shared.h" +#include "libdnssec/shared/binary_wire.h" + +/* -- internal functions --------------------------------------------------- */ + +/*! + * Check if the algorithm number is valid for given DNSKEY. + */ +static bool valid_algorithm(dnssec_key_t *key, gnutls_privkey_t privkey) +{ + uint8_t current = dnssec_key_get_algorithm(key); + int gnu_algorithm = gnutls_privkey_get_pk_algorithm(privkey, NULL); + + return (gnu_algorithm == algorithm_to_gnutls(current)); +} + +/*! + * Create GnuTLS public key from private key. + */ +static int public_from_private(gnutls_privkey_t privkey, gnutls_pubkey_t *pubkey) +{ + assert(privkey); + assert(pubkey); + + gnutls_pubkey_t new_key = NULL; + int result = gnutls_pubkey_init(&new_key); + if (result != GNUTLS_E_SUCCESS) { + return DNSSEC_ENOMEM; + } + + result = gnutls_pubkey_import_privkey(new_key, privkey, 0, 0); + if (result != GNUTLS_E_SUCCESS) { + gnutls_pubkey_deinit(new_key); + return DNSSEC_KEY_IMPORT_ERROR; + } + + *pubkey = new_key; + + return DNSSEC_EOK; +} + +/*! + * Create public key (GnuTLS and DNSKEY RDATA) from a private key. + */ +static int create_public_key(gnutls_privkey_t privkey, + gnutls_pubkey_t *pubkey_ptr, + dnssec_binary_t *rdata) +{ + assert(privkey); + assert(pubkey_ptr); + assert(rdata); + + // crypto public key + + gnutls_pubkey_t pubkey = NULL; + int result = public_from_private(privkey, &pubkey); + if (result != DNSSEC_EOK) { + return result; + } + + // dnssec public key + + _cleanup_binary_ dnssec_binary_t rdata_pubkey = { 0 }; + result = convert_pubkey_to_dnskey(pubkey, &rdata_pubkey); + if (result != DNSSEC_EOK) { + gnutls_pubkey_deinit(pubkey); + return result; + } + + size_t rdata_size = DNSKEY_RDATA_OFFSET_PUBKEY + rdata_pubkey.size; + result = dnssec_binary_resize(rdata, rdata_size); + if (result != DNSSEC_EOK) { + gnutls_pubkey_deinit(pubkey); + return result; + } + + // updated RDATA + + wire_ctx_t wire = binary_init(rdata); + wire_ctx_set_offset(&wire, DNSKEY_RDATA_OFFSET_PUBKEY); + binary_write(&wire, &rdata_pubkey); + assert(wire_ctx_offset(&wire) == rdata->size); + + *pubkey_ptr = pubkey; + + return DNSSEC_EOK; +} + +/* -- internal API --------------------------------------------------------- */ + +/*! + * Load a private key into a DNSSEC key, create a public part if necessary. + */ +int key_set_private_key(dnssec_key_t *key, gnutls_privkey_t privkey) +{ + assert(key); + assert(privkey); + assert(key->private_key == NULL); + + if (!valid_algorithm(key, privkey)) { + return DNSSEC_INVALID_KEY_ALGORITHM; + } + + if (!key->public_key) { + int r = create_public_key(privkey, &key->public_key, &key->rdata); + if (r != DNSSEC_EOK) { + return r; + } + } + + key->private_key = privkey; + + return DNSSEC_EOK; +} diff --git a/src/libdnssec/key/privkey.h b/src/libdnssec/key/privkey.h new file mode 100644 index 0000000..c0c5bb2 --- /dev/null +++ b/src/libdnssec/key/privkey.h @@ -0,0 +1,35 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include <gnutls/abstract.h> + +#include "libdnssec/key.h" + +/*! + * Load a private key into a DNSSEC key, create a public part if necessary. + * + * If the public key is not loaded, at least an algorithm must be set. + * + * Updates private key, public key, RDATA, and key identifiers. + * + * \param key DNSSEC key to be updated. + * \param privkey Private key to be set. + * + * \return Error code, DNSSEC_EOK if successful. + */ +int key_set_private_key(dnssec_key_t *key, gnutls_privkey_t privkey); diff --git a/src/libdnssec/key/simple.c b/src/libdnssec/key/simple.c new file mode 100644 index 0000000..a2d8ea4 --- /dev/null +++ b/src/libdnssec/key/simple.c @@ -0,0 +1,55 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <gnutls/abstract.h> +#include <gnutls/gnutls.h> + +#include "libdnssec/binary.h" +#include "libdnssec/error.h" +#include "libdnssec/key.h" +#include "libdnssec/key/dnskey.h" +#include "libdnssec/key/internal.h" +#include "libdnssec/key/privkey.h" +#include "libdnssec/shared/pem.h" +#include "libdnssec/shared/shared.h" + +/* -- public API ----------------------------------------------------------- */ + +_public_ +int dnssec_key_load_pkcs8(dnssec_key_t *key, const dnssec_binary_t *pem) +{ + if (!key || !pem || !pem->data) { + return DNSSEC_EINVAL; + } + + if (dnssec_key_get_algorithm(key) == 0) { + return DNSSEC_INVALID_KEY_ALGORITHM; + } + + gnutls_privkey_t privkey = NULL; + int r = pem_privkey(pem, &privkey); + if (r != DNSSEC_EOK) { + return r; + } + + r = key_set_private_key(key, privkey); + if (r != DNSSEC_EOK) { + gnutls_privkey_deinit(privkey); + return r; + } + + return DNSSEC_EOK; +} diff --git a/src/libdnssec/keyid.c b/src/libdnssec/keyid.c new file mode 100644 index 0000000..c99a657 --- /dev/null +++ b/src/libdnssec/keyid.c @@ -0,0 +1,87 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <assert.h> +#include <string.h> + +#include "libdnssec/error.h" +#include "libdnssec/keyid.h" +#include "libdnssec/shared/shared.h" + +#include "contrib/ctype.h" +#include "contrib/tolower.h" + +/* -- public API ----------------------------------------------------------- */ + +_public_ +bool dnssec_keyid_is_valid(const char *id) +{ + if (!id) { + return false; + } + + if (strlen(id) != DNSSEC_KEYID_SIZE) { + return false; + } + + for (int i = 0; i < DNSSEC_KEYID_SIZE; i++) { + if (!is_xdigit(id[i])) { + return false; + } + } + + return true; +} + +_public_ +void dnssec_keyid_normalize(char *id) +{ + if (!id) { + return; + } + + for (size_t i = 0; i < DNSSEC_KEYID_SIZE; i++) { + assert(id[i] != '\0' && is_xdigit(id[i])); + id[i] = knot_tolower(id[i]); + } +} + +_public_ +char *dnssec_keyid_copy(const char *id) +{ + if (!id) { + return NULL; + } + + char *copy = strdup(id); + if (!copy) { + return NULL; + } + + dnssec_keyid_normalize(copy); + + return copy; +} + +_public_ +bool dnssec_keyid_equal(const char *one, const char *two) +{ + if (!one || !two) { + return NULL; + } + + return (strcasecmp(one, two) == 0); +} diff --git a/src/libdnssec/keyid.h b/src/libdnssec/keyid.h new file mode 100644 index 0000000..24e201e --- /dev/null +++ b/src/libdnssec/keyid.h @@ -0,0 +1,80 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +/*! + * \file + * + * \addtogroup keyid + * + * \brief DNSSEC key ID manipulation. + * + * The module contains auxiliary functions for manipulation with key IDs. + * + * Example: + * + * ~~~~~ {.c} + * + * char *key_id = "ef26672cafede0732dd18fba6488fa390b5589af"; + * assert(dnssec_keyid_is_valid(key_id)); + * + * char copy[DNSSEC_KEY_ID_SIZE + 1] = { 0 }; + * memcpy(copy, key_id, sizeof(copy)); + * for (int i = 0; i < DNSSEC_KEY_ID_SIZE; i++) { + * copy[i] = toupper((unsigned char)copy[i]); + * } + * + * assert(dnssec_keyid_equal(key_id, copy)); + * + * ~~~~~ + * + * @{ + */ + +#pragma once + +#include <stdint.h> +#include <stdbool.h> + +/*! + * Length of the key ID in presentation form (ASCII). + */ +#define DNSSEC_KEYID_SIZE 40 + +/*! + * Length of the key ID in internal form (binary). + */ +#define DNSSEC_KEYID_BINARY_SIZE 20 + +/*! + * Check if a provided string is a valid key ID string. + */ +bool dnssec_keyid_is_valid(const char *id); + +/*! + * Normalize the key ID string. + */ +void dnssec_keyid_normalize(char *id); + +/*! + * Create a normalized copy if the key ID. + */ +char *dnssec_keyid_copy(const char *id); + +/*! + * Check if two key IDs are equal. + */ +bool dnssec_keyid_equal(const char *one, const char *two); + +/*! @} */ diff --git a/src/libdnssec/keystore.h b/src/libdnssec/keystore.h new file mode 100644 index 0000000..2382518 --- /dev/null +++ b/src/libdnssec/keystore.h @@ -0,0 +1,297 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +/*! + * \file + * + * \addtogroup keystore + * + * \brief Private key store access. + * + * The module provides abstraction for private key store. Basically, PKCS #8 + * and PKCS #11 interfaces are supported. + * + * PKCS #8 uses unencrypted PEM, and allows implementation of custom stores. + * + * PKCS #11 provides access Hardware Security Modules. + * + * Example of using default PKCS #8 and to generate an RSA key: + * + * ~~~~~ {.c} + * + * int result; + * dnssec_keystore_t *store = NULL; + * + * // create key store access context + * dnssec_keystore_init_pkcs8_dir(&store); + * + * // open the key store + * result = dnssec_keystore_open(&store, "/path/to/keydb"); + * if (result != DNSSEC_EOK) { + * return result; + * } + * + * // generate new private key in the key store + * int algorithm = DNSSEC_KEY_ALGORITHM_RSA_SHA256; + * unsigned bits = 2048; + * char *id = NULL; + * int dnssec_keystore_generate_key(store, algorithm, bits, &key_id); + * if (result != DNSSEC_EOK) { + * dnssec_keystore_close(store); + * return result; + * } + * printf("ID of the new key: %s\n", key_id); + * + * // create new signing key + * dnssec_key_t *key = NULL; + * result = dnssec_key_new(&key); + * if (result != DNSSEC_EOK) { + * free(key_id); + * dnssec_keystore_close(store); + * return result; + * } + * + * // import the key from the key store + * result = dnssec_key_import_keystore(key, store, key_id, algorithm); + * if (result != DNSSEC_EOK) { + * free(key_id); + * dnssec_key_free(key); + * dnssec_keystore_close(store); + * return result; + * } + * + * // use the key for signing ... + * + * // cleanup + * free(key_id); + * dnssec_key_free(key); + * dnssec_keystore_close(store); + * dnssec_keystore_deinit(store); + * + * ~~~~~ + * @{ + */ + +#pragma once + +#include <libdnssec/binary.h> +#include <libdnssec/key.h> +#include <libdnssec/list.h> + +struct dnssec_keystore; + +/*! + * DNSSEC private keys store. + */ +typedef struct dnssec_keystore dnssec_keystore_t; + +/*! + * PKCS #8 key store callback functions for custom providers. + */ +typedef struct dnssec_keystore_pkcs8_functions { + /*! + * Callback to allocate key store handle. + * + * \param[out] handle_ptr Allocated key store handle. + */ + int (*handle_new)(void **handle_ptr); + + /*! + * Callback to deallocate key store handle. + * + * \param handle Key store handle. + */ + int (*handle_free)(void *handle); + + /*! + * Callback to initialize the key store. + * + * \param handle Key store handle. + * \param config Configuration string. + */ + int (*init)(void *handle, const char *config); + + /*! + * Callback to open the key store. + * + * \param[out] handle Key store handle. + * \param[in] config Configuration string. + */ + int (*open)(void *handle, const char *config); + + /*! + * Callback to close the key store. + * + * \param handle Key store handle. + */ + int (*close)(void *handle); + + /*! + * Callback to read a PEM key. + * + * \param[in] handle Key store handle. + * \param[in] id Key ID of the key to be retrieved (ASCII form). + * \param[out] pem Key material in uncencrypted PEM format. + */ + int (*read)(void *handle, const char *id, dnssec_binary_t *pem); + + /*! + * Callback to write a PEM key. + * + * \param handle Key store handle. + * \param id Key ID of the key to be saved (ASCII form). + * \param pem Key material in unencrypted PEM format. + */ + int (*write)(void *handle, const char *id, const dnssec_binary_t *pem); + + /*! + * Callback to get a list of all PEM key IDs. + * + * \param[in] handle Key store handle. + * \param[out] list Allocated list of key IDs. + */ + int (*list)(void *handle, dnssec_list_t **list); + + /*! + * Callback to remove a PEM key. + * + * \param handle Key store handle. + * \param id Key ID of the key to be removed (ASCII form). + */ + int (*remove)(void *handle, const char *id); +} dnssec_keystore_pkcs8_functions_t; + +/*! + * Create default PKCS #8 private key store context. + * + * The default store maintains the private keys in one directory on the file + * system. The private keys are stored in unencrypted PEM format, named + * key-id.pem. The configuration string is a path to the directory. + * + * \param[out] store Opened key store. + * + * \return Error code, DNSSEC_EOK if successful. + */ +int dnssec_keystore_init_pkcs8_dir(dnssec_keystore_t **store); + +/*! + * Create custom PKCS #8 private key store context. + * + * \param[out] store Opened key store. + * \param[in] impl Implementation of the key store provider. + * + * \return Error code, DNSSEC_EOK if successful. + */ +int dnssec_keystore_init_pkcs8_custom(dnssec_keystore_t **store, + const dnssec_keystore_pkcs8_functions_t *impl); + +/*! + * Crate new PKCS #11 private key store context. + * + * \param[out] store Opened key store. + * + * \return Error code, DNSSEC_EOK if successful. + */ +int dnssec_keystore_init_pkcs11(dnssec_keystore_t **store); + +/*! + * Deinitialize private key store context. + * + * \param store Key store to be deinitialized. + */ +int dnssec_keystore_deinit(dnssec_keystore_t *store); + +/*! + * Initialize new private key store. + */ +int dnssec_keystore_init(dnssec_keystore_t *store, const char *config); + +/*! + * Open private key store. + */ +int dnssec_keystore_open(dnssec_keystore_t *store, const char *config); + +/*! + * Close private key store. + * + * \param store Key store to be closed. + * + * \return Error code, DNSSEC_EOK if successful. + */ +int dnssec_keystore_close(dnssec_keystore_t *store); + +/*! + * Get a list of key IDs stored in the key store. + * + * \todo Not implemented. + * + * \param[in] store Key store. + * \param[out] list Resulting list of key IDs. + * + * \return Error code, DNSSEC_EOK if successful. + */ +int dnssec_keystore_list_keys(dnssec_keystore_t *store, dnssec_list_t **list); + +/*! + * Generate a new key in the key store. + * + * \param[in] store Key store. + * \param[in] algorithm Algorithm. + * \param[in] bits Bit length of the key to be generated. + * \param[out] id_ptr ID of the generated key. Must be freed by the caller. + * + * \return Error code, DNSSEC_EOK if successful. + */ +int dnssec_keystore_generate_key(dnssec_keystore_t *store, + dnssec_key_algorithm_t algorithm, + unsigned bits, char **id_ptr); + +/*! + * Import an existing key into the key store. + * + * \param[in] store Key store. + * \param[in] pem Private key material in PEM format. + * \param[out] id_ptr ID of the imported key. Must be freed by the caller. + * + * \return Error code, DNSSEC_EOK if successful. + */ +int dnssec_keystore_import(dnssec_keystore_t *store, const dnssec_binary_t *pem, + char **id_ptr); + +/*! + * Remove a private key from the key store. + * + * \param store Key store. + * \param id ID of the private key to be deleted. + * + * \return Error code, DNSSEC_EOK if successful. + */ +int dnssec_keystore_remove_key(dnssec_keystore_t *store, const char *id); + +/*! + * Import public and/or private key from the key store into a DNSSEC key. + * + * The key algorithm has to be set before calling this function. + * + * \param key DNSSEC key to be initialized. + * \param keystore Private key store. + * \param id ID of the key. + * + * \return Error code, DNSSEC_EOK if successful. + */ +int dnssec_key_import_keystore(dnssec_key_t *key, dnssec_keystore_t *keystore, + const char *id); + +/*! @} */ diff --git a/src/libdnssec/keystore/internal.h b/src/libdnssec/keystore/internal.h new file mode 100644 index 0000000..a87dd03 --- /dev/null +++ b/src/libdnssec/keystore/internal.h @@ -0,0 +1,52 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include <gnutls/gnutls.h> +#include <gnutls/abstract.h> + +#include "libdnssec/binary.h" +#include "libdnssec/key.h" +#include "libdnssec/keystore.h" +#include "libdnssec/list.h" + +typedef struct keystore_functions { + // construction of internal context + int (*ctx_new)(void **ctx_ptr, void *data); + int (*ctx_free)(void *ctx); + // keystore init/open/close + int (*init)(void *ctx, const char *config); + int (*open)(void *ctx, const char *config); + int (*close)(void *ctx); + // keystore access + int (*list_keys)(void *ctx, dnssec_list_t **list); + int (*generate_key)(void *ctx, gnutls_pk_algorithm_t algorithm, + unsigned bits, char **id_ptr); + int (*import_key)(void *ctx, const dnssec_binary_t *pem, char **id_ptr); + int (*remove_key)(void *ctx, const char *id); + // private key access + int (*get_private)(void *ctx, const char *id, gnutls_privkey_t *key_ptr); +} keystore_functions_t; + +struct dnssec_keystore { + const keystore_functions_t *functions; + void *ctx; +}; + +int keystore_create(dnssec_keystore_t **store_ptr, + const keystore_functions_t *functions, + void *ctx_custom_data); diff --git a/src/libdnssec/keystore/keystore.c b/src/libdnssec/keystore/keystore.c new file mode 100644 index 0000000..203441a --- /dev/null +++ b/src/libdnssec/keystore/keystore.c @@ -0,0 +1,183 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <assert.h> +#include <stdlib.h> + +#include "libdnssec/error.h" +#include "libdnssec/key.h" +#include "libdnssec/key/algorithm.h" +#include "libdnssec/key/dnskey.h" +#include "libdnssec/key/internal.h" +#include "libdnssec/key/privkey.h" +#include "libdnssec/keyid.h" +#include "libdnssec/keystore.h" +#include "libdnssec/keystore/internal.h" +#include "libdnssec/shared/shared.h" + +/* -- internal API --------------------------------------------------------- */ + +int keystore_create(dnssec_keystore_t **store_ptr, + const keystore_functions_t *functions, + void *ctx_custom_data) +{ + assert(store_ptr); + assert(functions); + + dnssec_keystore_t *store = calloc(1, sizeof(*store)); + if (!store) { + return DNSSEC_ENOMEM; + } + + store->functions = functions; + + int result = functions->ctx_new(&store->ctx, ctx_custom_data); + if (result != DNSSEC_EOK) { + free(store); + return DNSSEC_ENOMEM; + } + + *store_ptr = store; + return DNSSEC_EOK; +} + +/* -- public API ----------------------------------------------------------- */ + +_public_ +int dnssec_keystore_deinit(dnssec_keystore_t *store) +{ + if (!store) { + return DNSSEC_EINVAL; + } + + dnssec_keystore_close(store); + store->functions->ctx_free(store->ctx); + + free(store); + + return DNSSEC_EOK; +} + +_public_ +int dnssec_keystore_init(dnssec_keystore_t *store, const char *config) +{ + if (!store) { + return DNSSEC_EINVAL; + } + + return store->functions->init(store->ctx, config); +} + +_public_ +int dnssec_keystore_open(dnssec_keystore_t *store, const char *config) +{ + if (!store) { + return DNSSEC_EINVAL; + } + + return store->functions->open(store->ctx, config); +} + +_public_ +int dnssec_keystore_close(dnssec_keystore_t *store) +{ + if (!store) { + return DNSSEC_EINVAL; + } + + return store->functions->close(store->ctx); +} + +_public_ +int dnssec_keystore_list_keys(dnssec_keystore_t *store, dnssec_list_t **list) +{ + if (!store || !list) { + return DNSSEC_EINVAL; + } + + return store->functions->list_keys(store->ctx, list); +} + +_public_ +int dnssec_keystore_generate_key(dnssec_keystore_t *store, + dnssec_key_algorithm_t _algorithm, + unsigned bits, char **id_ptr) +{ + if (!store || !_algorithm || !id_ptr) { + return DNSSEC_EINVAL; + } + + // prepare parameters + + gnutls_pk_algorithm_t algorithm = algorithm_to_gnutls(_algorithm); + if (algorithm == GNUTLS_PK_UNKNOWN) { + return DNSSEC_INVALID_KEY_ALGORITHM; + } + + if (!dnssec_algorithm_key_size_check(_algorithm, bits)) { + return DNSSEC_INVALID_KEY_SIZE; + } + + return store->functions->generate_key(store->ctx, algorithm, bits, id_ptr); +} + +_public_ +int dnssec_keystore_import(dnssec_keystore_t *store, const dnssec_binary_t *pem, + char **id_ptr) +{ + if (!store || !pem || !id_ptr) { + return DNSSEC_EINVAL; + } + + return store->functions->import_key(store->ctx, pem, id_ptr); +} + +_public_ +int dnssec_keystore_remove_key(dnssec_keystore_t *store, const char *key_id) +{ + if (!store || !key_id) { + return DNSSEC_EINVAL; + } + + return store->functions->remove_key(store->ctx, key_id); +} + +_public_ +int dnssec_key_import_keystore(dnssec_key_t *key, dnssec_keystore_t *store, + const char *id) +{ + if (!key || !store || !id || dnssec_key_get_algorithm(key) == 0) { + return DNSSEC_EINVAL; + } + + if (key->private_key) { + return DNSSEC_KEY_ALREADY_PRESENT; + } + + gnutls_privkey_t privkey = NULL; + int r = store->functions->get_private(store->ctx, id, &privkey); + if (r != DNSSEC_EOK) { + return r; + } + + r = key_set_private_key(key, privkey); + if (r != DNSSEC_EOK) { + gnutls_privkey_deinit(privkey); + return r; + } + + return DNSSEC_EOK; +} diff --git a/src/libdnssec/keystore/pkcs11.c b/src/libdnssec/keystore/pkcs11.c new file mode 100644 index 0000000..dbe1a37 --- /dev/null +++ b/src/libdnssec/keystore/pkcs11.c @@ -0,0 +1,440 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <gnutls/gnutls.h> +#include <pthread.h> + +#include "libdnssec/error.h" +#include "libdnssec/shared/hex.h" +#include "libdnssec/keyid.h" +#include "libdnssec/shared/keyid_gnutls.h" +#include "libdnssec/keystore.h" +#include "libdnssec/keystore/internal.h" +#include "libdnssec/p11/p11.h" +#include "libdnssec/shared/pem.h" +#include "libdnssec/shared/shared.h" + +#ifdef ENABLE_PKCS11 + +struct pkcs11_ctx { + char *url; +}; + +typedef struct pkcs11_ctx pkcs11_ctx_t; + +/*! + * Flags used when generating/import key into the token. + */ +static const int TOKEN_ADD_FLAGS = GNUTLS_PKCS11_OBJ_FLAG_MARK_SENSITIVE + | GNUTLS_PKCS11_OBJ_FLAG_MARK_PRIVATE; + +static int key_url(const char *token_uri, const char *key_id, char **url_ptr) +{ + assert(token_uri); + assert(key_id); + assert(url_ptr); + + if (!dnssec_keyid_is_valid(key_id)) { + return DNSSEC_INVALID_KEY_ID; + } + + size_t token_len = strlen(token_uri); + size_t id_len = strlen(key_id); + + // url: <token-url>;id=%aa%bb%cc.. + + size_t len = token_len + 4 + (id_len / 2 * 3); + char *url = malloc(len + 1); + if (!url) { + return DNSSEC_ENOMEM; + } + + size_t prefix = snprintf(url, len, "%s;id=", token_uri); + if (prefix != token_len + 4) { + free(url); + return DNSSEC_ENOMEM; + } + + assert(id_len % 2 == 0); + char *pos = url + prefix; + for (int i = 0; i < id_len; i += 2, pos += 3) { + pos[0] = '%'; + pos[1] = key_id[i]; + pos[2] = key_id[i+1]; + } + assert(url + len == pos); + url[len] = '\0'; + + *url_ptr = url; + return DNSSEC_EOK; +} + +/** + * Parse configuration string. Accepted format: "<pkcs11-url> <module-path>" + */ +static int parse_config(const char *config, char **uri_ptr, char **module_ptr) +{ + const char *space = strchr(config, ' '); + if (!space) { + return DNSSEC_KEYSTORE_INVALID_CONFIG; + } + + char *url = strndup(config, space - config); + char *module = strdup(space + 1); + + if (!url || !module) { + free(url); + free(module); + return DNSSEC_ENOMEM; + } + + *uri_ptr = url; + *module_ptr = module; + + return DNSSEC_EOK; +} + +/*! + * Load PKCS #11 module and check if the token is available. + */ +static int safe_open(const char *config, char **url_ptr) +{ + char *url = NULL; + char *module = NULL; + + int r = parse_config(config, &url, &module); + if (r != DNSSEC_EOK) { + return r; + } + + r = p11_load_module(module); + free(module); + if (r != GNUTLS_E_SUCCESS) { + free(url); + return DNSSEC_P11_FAILED_TO_LOAD_MODULE; + } + + unsigned int flags = 0; + r = gnutls_pkcs11_token_get_flags(url, &flags); + if (r != GNUTLS_E_SUCCESS) { + free(url); + return DNSSEC_P11_TOKEN_NOT_AVAILABLE; + } + + *url_ptr = url; + + return DNSSEC_EOK; +} + +/* -- internal API --------------------------------------------------------- */ + +static void disable_pkcs11_callbacks(void) +{ + gnutls_pkcs11_set_pin_function(NULL, NULL); + gnutls_pkcs11_set_token_function(NULL, NULL); +} + +static int pkcs11_ctx_new(void **ctx_ptr, _unused_ void *data) +{ + static pthread_once_t once = PTHREAD_ONCE_INIT; + pthread_once(&once, disable_pkcs11_callbacks); + + pkcs11_ctx_t *ctx = calloc(1, sizeof(*ctx)); + if (!ctx) { + return DNSSEC_ENOMEM; + } + + *ctx_ptr = ctx; + + return DNSSEC_EOK; +} + +static int pkcs11_ctx_free(void *ctx) +{ + if (ctx) { + free(ctx); + } + + return DNSSEC_EOK; +} + +static int pkcs11_init(void *ctx, const char *config) +{ + /* + * Current keystore initialization is idempotent. We don't really + * initialize the token because don't want to wipe the data. We just + * check that the token is available the same way pkcs11_open() does. + */ + + _cleanup_free_ char *url = NULL; + + return safe_open(config, &url); +} + +static int pkcs11_open(void *_ctx, const char *config) +{ + pkcs11_ctx_t *ctx = _ctx; + + return safe_open(config, &ctx->url); +} + +static int pkcs11_close(void *_ctx) +{ + pkcs11_ctx_t *ctx = _ctx; + + free(ctx->url); + clear_struct(ctx); + + return DNSSEC_EOK; +} + +static char *get_object_id(gnutls_pkcs11_obj_t object) +{ + assert(object); + + uint8_t buffer[DNSSEC_KEYID_BINARY_SIZE] = { 0 }; + size_t size = sizeof(buffer); + + int r = gnutls_pkcs11_obj_get_info(object, GNUTLS_PKCS11_OBJ_ID, buffer, &size); + if (r != GNUTLS_E_SUCCESS || size != sizeof(buffer)) { + return NULL; + } + + const dnssec_binary_t bin = { .data = buffer, .size = sizeof(buffer) }; + char *id = NULL; + if (bin_to_hex(&bin, &id) != DNSSEC_EOK) { + return NULL; + } + + assert(strlen(id) == DNSSEC_KEYID_SIZE); + + return id; +} + +static int pkcs11_list_keys(void *_ctx, dnssec_list_t **list) +{ + pkcs11_ctx_t *ctx = _ctx; + + dnssec_list_t *ids = dnssec_list_new(); + if (!ids) { + return DNSSEC_ENOMEM; + } + + gnutls_pkcs11_obj_t *objects = NULL; + unsigned count = 0; + + int flags = GNUTLS_PKCS11_OBJ_FLAG_PRIVKEY | + GNUTLS_PKCS11_OBJ_FLAG_LOGIN; + + int r = gnutls_pkcs11_obj_list_import_url4(&objects, &count, ctx->url, flags); + if (r != GNUTLS_E_SUCCESS) { + dnssec_list_free(ids); + return DNSSEC_P11_TOKEN_NOT_AVAILABLE; + } + + for (unsigned i = 0; i < count; i++) { + gnutls_pkcs11_obj_t object = objects[i]; + char *id = get_object_id(object); + dnssec_list_append(ids, id); + gnutls_pkcs11_obj_deinit(object); + } + + gnutls_free(objects); + + *list = ids; + return DNSSEC_EOK; +} + +static int pkcs11_generate_key(void *_ctx, gnutls_pk_algorithm_t algorithm, + unsigned bits, char **id_ptr) +{ + pkcs11_ctx_t *ctx = _ctx; + + uint8_t buf[20] = { 0 }; + gnutls_rnd(GNUTLS_RND_RANDOM, buf, sizeof(buf)); + dnssec_binary_t cka_id = { .data = buf, .size = sizeof(buf) }; + + int flags = TOKEN_ADD_FLAGS | GNUTLS_PKCS11_OBJ_FLAG_LOGIN; + gnutls_datum_t gt_cka_id = binary_to_datum(&cka_id); + int r = gnutls_pkcs11_privkey_generate3(ctx->url, algorithm, bits, NULL, >_cka_id, 0, NULL, 0, flags); + if (r != GNUTLS_E_SUCCESS) { + return DNSSEC_KEY_GENERATE_ERROR; + } + + char *id = NULL; + r = bin_to_hex(&cka_id, &id); + if (r != DNSSEC_EOK) { + return DNSSEC_ENOMEM; + } + + *id_ptr = id; + + return DNSSEC_EOK; +} + +static int import_pem(const dnssec_binary_t *pem, + gnutls_x509_privkey_t *key_ptr, + gnutls_pubkey_t *pubkey_ptr) +{ + gnutls_x509_privkey_t x509_key = NULL; + gnutls_privkey_t key = NULL; + gnutls_pubkey_t pubkey = NULL; + + int r = pem_x509(pem, &x509_key); + if (r != DNSSEC_EOK) { + goto fail; + } + + if (gnutls_privkey_init(&key) != GNUTLS_E_SUCCESS || + gnutls_pubkey_init(&pubkey) != GNUTLS_E_SUCCESS + ) { + r = DNSSEC_ENOMEM; + goto fail; + } + + if (gnutls_privkey_import_x509(key, x509_key, 0) != GNUTLS_E_SUCCESS || + gnutls_pubkey_import_privkey(pubkey, key, 0, 0) != GNUTLS_E_SUCCESS + ) { + r = DNSSEC_KEY_IMPORT_ERROR; + goto fail; + } + +fail: + gnutls_privkey_deinit(key); + + if (r == DNSSEC_EOK) { + *key_ptr = x509_key; + *pubkey_ptr = pubkey; + } else { + gnutls_x509_privkey_deinit(x509_key); + gnutls_pubkey_deinit(pubkey); + } + + return r; +} + +static int pkcs11_import_key(void *_ctx, const dnssec_binary_t *pem, char **id_ptr) +{ + pkcs11_ctx_t *ctx = _ctx; + + _cleanup_x509_privkey_ gnutls_x509_privkey_t key = NULL; + _cleanup_pubkey_ gnutls_pubkey_t pubkey = NULL; + + int r = import_pem(pem, &key, &pubkey); + if (r != DNSSEC_EOK) { + return r; + } + + _cleanup_binary_ dnssec_binary_t id = { 0 }; + r = keyid_x509(key, &id); + if (r != DNSSEC_EOK) { + return r; + } + + int flags = TOKEN_ADD_FLAGS | GNUTLS_PKCS11_OBJ_FLAG_LOGIN; + gnutls_datum_t gid = binary_to_datum(&id); + + r = gnutls_pkcs11_copy_x509_privkey2(ctx->url, key, NULL, &gid, 0, flags); + if (r != GNUTLS_E_SUCCESS) { + return DNSSEC_KEY_IMPORT_ERROR; + } + + r = gnutls_pkcs11_copy_pubkey(ctx->url, pubkey, NULL, &gid, 0, flags); + if (r != GNUTLS_E_SUCCESS) { + // note, we result with dangling private key in the token + return DNSSEC_KEY_IMPORT_ERROR; + } + + return bin_to_hex(&id, id_ptr); +} + +static int pkcs11_remove_key(void *_ctx, const char *id) +{ + pkcs11_ctx_t *ctx = _ctx; + + _cleanup_free_ char *url = NULL; + int r = key_url(ctx->url, id, &url); + if (r != DNSSEC_EOK) { + return r; + } + + r = gnutls_pkcs11_delete_url(url, GNUTLS_PKCS11_OBJ_FLAG_LOGIN); + if (r < 0) { + return DNSSEC_ERROR; + } else if (r == 0) { + return DNSSEC_ENOENT; + } + + return DNSSEC_EOK; +} + +static int pkcs11_get_private(void *_ctx, const char *id, gnutls_privkey_t *key_ptr) +{ + pkcs11_ctx_t *ctx = _ctx; + + _cleanup_free_ char *url = NULL; + int r = key_url(ctx->url, id, &url); + if (r != DNSSEC_EOK) { + return r; + } + + gnutls_privkey_t key = NULL; + r = gnutls_privkey_init(&key); + if (r != GNUTLS_E_SUCCESS) { + return DNSSEC_ENOMEM; + } + + r = gnutls_privkey_import_pkcs11_url(key, url); + if (r != GNUTLS_E_SUCCESS) { + gnutls_privkey_deinit(key); + return DNSSEC_NOT_FOUND; + } + + *key_ptr = key; + + return DNSSEC_EOK; +} + +/* -- public API ----------------------------------------------------------- */ + +_public_ +int dnssec_keystore_init_pkcs11(dnssec_keystore_t **store_ptr) +{ + static const keystore_functions_t IMPLEMENTATION = { + .ctx_new = pkcs11_ctx_new, + .ctx_free = pkcs11_ctx_free, + .init = pkcs11_init, + .open = pkcs11_open, + .close = pkcs11_close, + .list_keys = pkcs11_list_keys, + .generate_key = pkcs11_generate_key, + .import_key = pkcs11_import_key, + .remove_key = pkcs11_remove_key, + .get_private = pkcs11_get_private, + }; + + return keystore_create(store_ptr, &IMPLEMENTATION, NULL); +} + +#else // !ENABLE_PKCS11 + +_public_ +int dnssec_keystore_init_pkcs11(dnssec_keystore_t **store_ptr) +{ + return DNSSEC_NOT_IMPLEMENTED_ERROR; +} + +#endif diff --git a/src/libdnssec/keystore/pkcs8.c b/src/libdnssec/keystore/pkcs8.c new file mode 100644 index 0000000..f05eda0 --- /dev/null +++ b/src/libdnssec/keystore/pkcs8.c @@ -0,0 +1,211 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <assert.h> + +#include "libdnssec/error.h" +#include "libdnssec/key/algorithm.h" +#include "libdnssec/keyid.h" +#include "libdnssec/keystore.h" +#include "libdnssec/keystore/internal.h" +#include "libdnssec/shared/pem.h" +#include "libdnssec/shared/shared.h" + +/*! + * PKCS #8 key store context. + */ +typedef struct pkcs8_ctx { + /*! Storage implementation callbacks. */ + const dnssec_keystore_pkcs8_functions_t *functions; + /*! Implementation specific context data. */ + void *data; +} pkcs8_ctx_t; + +/* -- internal API --------------------------------------------------------- */ + +static int pkcs8_ctx_new(void **ctx_ptr, void *_functions) +{ + assert(ctx_ptr); + assert(_functions); + + pkcs8_ctx_t *ctx = calloc(1, sizeof(*ctx)); + if (!ctx) { + return DNSSEC_ENOMEM; + } + + ctx->functions = _functions; + + int r = ctx->functions->handle_new(&ctx->data); + if (r != DNSSEC_EOK) { + free(ctx); + return r; + } + + *ctx_ptr = ctx; + + return DNSSEC_EOK; +} + +static int pkcs8_ctx_free(void *_ctx) +{ + pkcs8_ctx_t *ctx = _ctx; + int r = ctx->functions->handle_free(ctx->data); + + free(ctx); + + return r; +} + +static int pkcs8_init(void *_ctx, const char *config) +{ + pkcs8_ctx_t *ctx = _ctx; + return ctx->functions->init(ctx->data, config); +} + +static int pkcs8_open(void *_ctx, const char *config) +{ + pkcs8_ctx_t *ctx = _ctx; + return ctx->functions->open(ctx->data, config); +} + +static int pkcs8_close(void *_ctx) +{ + pkcs8_ctx_t *ctx = _ctx; + return ctx->functions->close(ctx->data); +} + +static int pkcs8_list_keys(void *_ctx, dnssec_list_t **list) +{ + pkcs8_ctx_t *ctx = _ctx; + return ctx->functions->list(ctx->data, list); +} + +static int pkcs8_generate_key(void *_ctx, gnutls_pk_algorithm_t algorithm, + unsigned bits, char **id_ptr) +{ + assert(_ctx); + assert(id_ptr); + + pkcs8_ctx_t *ctx = _ctx; + + // generate key + + char *new_id = NULL; + _cleanup_binary_ dnssec_binary_t data = { 0 }; + int r = pem_generate(algorithm, bits, &data, &new_id); + if (r != DNSSEC_EOK) { + return r; + } + + // save key + + r = ctx->functions->write(ctx->data, new_id, &data); + if (r != DNSSEC_EOK) { + return r; + } + + // finish + + *id_ptr = new_id; + + return DNSSEC_EOK; +} + +static int pkcs8_import_key(void *_ctx, const dnssec_binary_t *pem, char **id_ptr) +{ + pkcs8_ctx_t *ctx = _ctx; + + // retrieve key ID + + char *id = NULL; + int r = pem_keyid(pem, &id); + if (r != DNSSEC_EOK) { + return r; + } + + // save the key + + r = ctx->functions->write(ctx->data, id, pem); + if (r != DNSSEC_EOK) { + free(id); + return r; + } + + *id_ptr = id; + + return DNSSEC_EOK; +} + +static int pkcs8_remove_key(void *_ctx, const char *id) +{ + pkcs8_ctx_t *ctx = _ctx; + return ctx->functions->remove(ctx->data, id); +} + +static int pkcs8_get_private(void *_ctx, const char *id, gnutls_privkey_t *key_ptr) +{ + assert(_ctx); + assert(id); + assert(key_ptr); + + pkcs8_ctx_t *ctx = _ctx; + + // load private key data + + _cleanup_binary_ dnssec_binary_t pem = { 0 }; + int r = ctx->functions->read(ctx->data, id, &pem); + if (r != DNSSEC_EOK) { + return r; + } + + // construct the key + + gnutls_privkey_t key = NULL; + r = pem_privkey(&pem, &key); + if (r != DNSSEC_EOK) { + return r; + } + + *key_ptr = key; + + return DNSSEC_EOK; +} + +static const keystore_functions_t PKCS8_FUNCTIONS = { + .ctx_new = pkcs8_ctx_new, + .ctx_free = pkcs8_ctx_free, + .init = pkcs8_init, + .open = pkcs8_open, + .close = pkcs8_close, + .list_keys = pkcs8_list_keys, + .generate_key = pkcs8_generate_key, + .import_key = pkcs8_import_key, + .remove_key = pkcs8_remove_key, + .get_private = pkcs8_get_private, +}; + +/* -- public API ----------------------------------------------------------- */ + +_public_ +int dnssec_keystore_init_pkcs8_custom(dnssec_keystore_t **store_ptr, + const dnssec_keystore_pkcs8_functions_t *store_functions) +{ + if (!store_ptr || !store_functions) { + return DNSSEC_EINVAL; + } + + return keystore_create(store_ptr, &PKCS8_FUNCTIONS, (void *)store_functions); +} diff --git a/src/libdnssec/keystore/pkcs8_dir.c b/src/libdnssec/keystore/pkcs8_dir.c new file mode 100644 index 0000000..bc67ad5 --- /dev/null +++ b/src/libdnssec/keystore/pkcs8_dir.c @@ -0,0 +1,379 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <assert.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include "libdnssec/binary.h" +#include "libdnssec/error.h" +#include "libdnssec/shared/fs.h" +#include "libdnssec/key.h" +#include "libdnssec/keystore.h" +#include "libdnssec/keystore/internal.h" +#include "libdnssec/shared/shared.h" + +#define DIR_INIT_MODE 0750 + +/*! + * Context for PKCS #8 key directory. + */ +typedef struct pkcs8_dir_handle { + char *dir_name; +} pkcs8_dir_handle_t; + +/* -- internal functions --------------------------------------------------- */ + +/*! + * Get path to a private key in PKCS #8 PEM format. + */ +static char *key_path(const char *dir, const char *id) +{ + char *strp = NULL; + + int ret = asprintf(&strp, "%s/%s.pem", dir, id); + if (ret < 0) { + return NULL; + } + return strp; +} + +/*! + * Get size of the file and reset the position to the beginning of the file. + */ +static int file_size(int fd, size_t *size) +{ + off_t offset = lseek(fd, 0, SEEK_END); + if (offset == -1) { + return dnssec_errno_to_error(errno); + } + + if (lseek(fd, 0, SEEK_SET) == -1) { + return dnssec_errno_to_error(errno); + } + + assert(offset >= 0); + *size = offset; + + return DNSSEC_EOK; +} + +/*! + * Open a key file and get the descriptor. + */ +static int key_open(const char *dir_name, const char *id, int flags, + mode_t mode, int *fd_ptr) +{ + assert(dir_name); + assert(id); + assert(fd_ptr); + + _cleanup_free_ char *filename = key_path(dir_name, id); + if (!filename) { + return DNSSEC_ENOMEM; + } + + int fd = open(filename, flags, mode); + if (fd == -1) { + return dnssec_errno_to_error(errno); + } + + *fd_ptr = fd; + + return DNSSEC_EOK; +} + +static int key_open_read(const char *dir_name, const char *id, int *fd_ptr) +{ + return key_open(dir_name, id, O_RDONLY, 0, fd_ptr); +} + +static int key_open_write(const char *dir_name, const char *id, int *fd_ptr) +{ + return key_open(dir_name, id, O_WRONLY|O_CREAT|O_EXCL, + S_IRUSR|S_IWUSR|S_IRGRP, fd_ptr); +} + +/*! + * Strip '.pem' suffix, basename has to be valid key ID. + */ +static char *filename_to_keyid(const char *filename) +{ + assert(filename); + + size_t len = strlen(filename); + + const char ext[] = ".pem"; + const size_t ext_len = sizeof(ext) - 1; + + if (len < ext_len || strcmp(filename + len - ext_len, ext) != 0) { + return NULL; + } + + return strndup(filename, len - ext_len); +} + +/* -- PKCS #8 dir access API ----------------------------------------------- */ + +static int pkcs8_dir_handle_new(void **handle_ptr) +{ + if (!handle_ptr) { + return DNSSEC_EINVAL; + } + + pkcs8_dir_handle_t *handle = calloc(1, sizeof(*handle)); + if (!handle) { + return DNSSEC_ENOMEM; + } + + *handle_ptr = handle; + + return DNSSEC_EOK; +} + +static int pkcs8_dir_handle_free(void *handle) +{ + free(handle); + + return DNSSEC_EOK; +} + +static int pkcs8_dir_init(void *handle, const char *path) +{ + if (!handle || !path) { + return DNSSEC_EINVAL; + } + + return fs_mkdir(path, DIR_INIT_MODE, true); +} + +static int pkcs8_dir_open(void *_handle, const char *config) +{ + if (!_handle || !config) { + return DNSSEC_EINVAL; + } + + pkcs8_dir_handle_t *handle = _handle; + + char *path = realpath(config, NULL); + if (!path) { + return DNSSEC_NOT_FOUND; + } + + handle->dir_name = path; + + return DNSSEC_EOK; +} + +static int pkcs8_dir_close(void *_handle) +{ + if (!_handle) { + return DNSSEC_EINVAL; + } + + pkcs8_dir_handle_t *handle = _handle; + + free(handle->dir_name); + memset(handle, 0, sizeof(*handle)); + + return DNSSEC_EOK; +} + +static int pkcs8_dir_read(void *_handle, const char *id, dnssec_binary_t *pem) +{ + if (!_handle || !id || !pem) { + return DNSSEC_EINVAL; + } + + pkcs8_dir_handle_t *handle = _handle; + + // open file and get it's size + + _cleanup_close_ int file = 0; + int result = key_open_read(handle->dir_name, id, &file); + if (result != DNSSEC_EOK) { + return result; + } + + size_t size = 0; + result = file_size(file, &size); + if (result != DNSSEC_EOK) { + return result; + } + + if (size == 0) { + return DNSSEC_MALFORMED_DATA; + } + + // read the stored data + + dnssec_binary_t read_pem = { 0 }; + result = dnssec_binary_alloc(&read_pem, size); + if (result != DNSSEC_EOK) { + return result; + } + + ssize_t read_count = read(file, read_pem.data, read_pem.size); + if (read_count == -1) { + dnssec_binary_free(&read_pem); + return dnssec_errno_to_error(errno); + } + + assert(read_count == read_pem.size); + *pem = read_pem; + + return DNSSEC_EOK; +} + +static bool key_is_duplicate(int open_error, pkcs8_dir_handle_t *handle, + const char *id, const dnssec_binary_t *pem) +{ + assert(handle); + assert(id); + assert(pem); + + if (open_error != dnssec_errno_to_error(EEXIST)) { + return false; + } + + _cleanup_binary_ dnssec_binary_t old = { 0 }; + int r = pkcs8_dir_read(handle, id, &old); + if (r != DNSSEC_EOK) { + return false; + } + + return dnssec_binary_cmp(&old, pem) == 0; +} + +static int pkcs8_dir_write(void *_handle, const char *id, const dnssec_binary_t *pem) +{ + if (!_handle || !id || !pem) { + return DNSSEC_EINVAL; + } + + if (pem->size == 0 || pem->data == NULL) { + return DNSSEC_MALFORMED_DATA; + } + + pkcs8_dir_handle_t *handle = _handle; + + // create the file + + _cleanup_close_ int file = 0; + int result = key_open_write(handle->dir_name, id, &file); + if (result != DNSSEC_EOK) { + if (key_is_duplicate(result, handle, id, pem)) { + return DNSSEC_EOK; + } + return result; + } + + // write the data + + ssize_t wrote_count = write(file, pem->data, pem->size); + if (wrote_count == -1) { + return dnssec_errno_to_error(errno); + } + + assert(wrote_count == pem->size); + + return DNSSEC_EOK; +} + +static int pkcs8_dir_list(void *_handle, dnssec_list_t **list_ptr) +{ + if (!_handle || !list_ptr) { + return DNSSEC_EINVAL; + } + + pkcs8_dir_handle_t *handle = _handle; + + _cleanup_closedir_ DIR *dir = opendir(handle->dir_name); + if (!dir) { + return DNSSEC_NOT_FOUND; + } + + dnssec_list_t *list = dnssec_list_new(); + if (!list) { + return DNSSEC_ENOMEM; + } + + errno = 0; + struct dirent *result; + while ((result = readdir(dir)) != NULL) { + char *keyid = filename_to_keyid(result->d_name); + if (keyid) { + dnssec_list_append(list, keyid); + } + } + + if (errno != 0) { + dnssec_list_free_full(list, NULL, NULL); + return dnssec_errno_to_error(errno); + } + + *list_ptr = list; + + return DNSSEC_EOK; +} + +static int pkcs8_dir_remove(void *_handle, const char *id) +{ + if (!_handle || !id) { + return DNSSEC_EINVAL; + } + + pkcs8_dir_handle_t *handle = _handle; + + _cleanup_free_ char *filename = key_path(handle->dir_name, id); + if (!filename) { + return DNSSEC_ENOMEM; + } + + if (unlink(filename) == -1) { + return dnssec_errno_to_error(errno); + } + + return DNSSEC_EOK; +} + +const dnssec_keystore_pkcs8_functions_t PKCS8_DIR_FUNCTIONS = { + .handle_new = pkcs8_dir_handle_new, + .handle_free = pkcs8_dir_handle_free, + .init = pkcs8_dir_init, + .open = pkcs8_dir_open, + .close = pkcs8_dir_close, + .read = pkcs8_dir_read, + .write = pkcs8_dir_write, + .list = pkcs8_dir_list, + .remove = pkcs8_dir_remove, +}; + +/* -- public API ----------------------------------------------------------- */ + +_public_ +int dnssec_keystore_init_pkcs8_dir(dnssec_keystore_t **store_ptr) +{ + if (!store_ptr) { + return DNSSEC_EINVAL; + } + + return dnssec_keystore_init_pkcs8_custom(store_ptr, &PKCS8_DIR_FUNCTIONS); +} diff --git a/src/libdnssec/keytag.h b/src/libdnssec/keytag.h new file mode 100644 index 0000000..5a8424e --- /dev/null +++ b/src/libdnssec/keytag.h @@ -0,0 +1,63 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +/*! + * \file + * + * \addtogroup keytag + * + * \brief Low-level key tag computation API. + * + * The module provides simple interface for DNSKEY key id computation. + * + * Example: + * + * ~~~~~ {.c} + * + * dnssec_binary_t dnskey_rdata = // ... ; + * + * int result; + * uint16_t keytag = 0; + * + * result = dnssec_keytag(&dnskey_rdata, &keytag); + * if (result != DNSSEC_EOK) { + * return result; + * } + * + * printf("keytag: %s\n", keytag); + * + * ~~~~~ + * + * @{ + */ + +#pragma once + +#include <stdint.h> +#include <libdnssec/binary.h> + +/*! + * Compute a key tag for a DNSSEC key. + * + * \param[in] rdata DNSKEY RDATA. + * \param[out] keytag Computed keytag. + * + * \return Error code, DNSSEC_EOK of successful. + */ +int dnssec_keytag(const dnssec_binary_t *rdata, uint16_t *keytag); + +/*! + * @} + */ diff --git a/src/libdnssec/list.h b/src/libdnssec/list.h new file mode 100644 index 0000000..838c0b5 --- /dev/null +++ b/src/libdnssec/list.h @@ -0,0 +1,85 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +/*! + * \file + * + * \addtogroup list + * + * \brief DNSSEC generic lists. + * + * @{ + */ + +#pragma once + +#include <stdbool.h> +#include <stdlib.h> + +struct dnssec_list; +typedef struct dnssec_list dnssec_list_t; + +struct dnssec_item; +typedef struct dnssec_item dnssec_item_t; + +void *dnssec_item_get(const dnssec_item_t *item); +void dnssec_item_set(dnssec_item_t *item, void *data); + +dnssec_list_t *dnssec_list_new(void); +void dnssec_list_free(dnssec_list_t *list); + +typedef void (*dnssec_item_free_cb)(void *data, void *ctx); + +/*! + * Free the list including contained items. + * + * If \c free_cb is NULL, standard libc \c free will used to free the items. + * + * \param list List to be freed. + * \param free_cb Free function called for each item in the list. + * \param free_ctx Context passed to item free. + */ +void dnssec_list_free_full(dnssec_list_t *list, dnssec_item_free_cb free_cb, + void *free_ctx); + +void dnssec_list_clear(dnssec_list_t *list); +void dnssec_list_clear_full(dnssec_list_t *list, dnssec_item_free_cb free_cb, + void *free_ctx); + +dnssec_item_t *dnssec_list_head(dnssec_list_t *list); +dnssec_item_t *dnssec_list_tail(dnssec_list_t *list); +dnssec_item_t *dnssec_list_next(dnssec_list_t *list, dnssec_item_t *item); +dnssec_item_t *dnssec_list_prev(dnssec_list_t *list, dnssec_item_t *item); +dnssec_item_t *dnssec_list_nth(dnssec_list_t *list, size_t position); + +bool dnssec_list_is_empty(dnssec_list_t *list); +size_t dnssec_list_size(dnssec_list_t *list); + +int dnssec_list_insert_after(dnssec_item_t *item, void *data); +int dnssec_list_insert_before(dnssec_item_t *item, void *data); +int dnssec_list_append(dnssec_list_t *list, void *data); +int dnssec_list_prepend(dnssec_list_t *list, void *data); + +void dnssec_list_remove(dnssec_item_t *item); + +dnssec_item_t *dnssec_list_search(dnssec_list_t *list, void *data); +bool dnssec_list_contains(dnssec_list_t *list, void *data); + +#define dnssec_list_foreach(var, list) \ + for (dnssec_item_t *__tmp, *var = dnssec_list_head(list); \ + __tmp = dnssec_list_next(list, var), var != NULL; \ + var = __tmp) + +/*! @} */ diff --git a/src/libdnssec/list/list.c b/src/libdnssec/list/list.c new file mode 100644 index 0000000..5d9aded --- /dev/null +++ b/src/libdnssec/list/list.c @@ -0,0 +1,298 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stdlib.h> + +#include "libdnssec/list.h" +#include "libdnssec/list/ucw_clists.h" +#include "libdnssec/error.h" +#include "libdnssec/shared/shared.h" + +struct dnssec_list { + clist list; +}; + +struct dnssec_item { + cnode node; + void *data; +}; + +/*! + * Allocate new list item and set item data. + */ +static dnssec_item_t *item_new(void *data) +{ + dnssec_item_t *item = malloc(sizeof(*item)); + if (item) { + clear_struct(item); + item->data = data; + } + + return item; +} + +/*! + * Wrapper around libc free with unused context. + */ +static void wrap_free(void *ptr, void *ctx _unused_) +{ + free(ptr); +} + +/* -- public API ----------------------------------------------------------- */ + +_public_ +void *dnssec_item_get(const dnssec_item_t *item) +{ + return item ? item->data : NULL; +} + +_public_ +void dnssec_item_set(dnssec_item_t *item, void *data) +{ + if (item) { + item->data = data; + } +} + +_public_ +dnssec_list_t *dnssec_list_new(void) +{ + dnssec_list_t *list = malloc(sizeof(*list)); + if (list) { + clist_init(&list->list); + } + + return list; +} + +_public_ +void dnssec_list_clear(dnssec_list_t *list) +{ + if (!list) { + return; + } + + dnssec_list_foreach(item, list) { + free(item); + } +} + +_public_ +void dnssec_list_clear_full(dnssec_list_t *list, dnssec_item_free_cb free_cb, + void *free_ctx) +{ + if (!list) { + return; + } + + if (!free_cb) { + free_cb = wrap_free; + } + + dnssec_list_foreach(item, list) { + free_cb(item->data, free_ctx); + free(item); + } +} + +_public_ +void dnssec_list_free(dnssec_list_t *list) +{ + if (!list) { + return; + } + + dnssec_list_clear(list); + free(list); +} + +_public_ +void dnssec_list_free_full(dnssec_list_t *list, dnssec_item_free_cb free_cb, + void *free_ctx) +{ + if (!list) { + return; + } + + dnssec_list_clear_full(list, free_cb, free_ctx); + free(list); +} + +_public_ +dnssec_item_t *dnssec_list_head(dnssec_list_t *list) +{ + if (!list) { + return NULL; + } + + return clist_head(&list->list); +} + +_public_ +dnssec_item_t *dnssec_list_tail(dnssec_list_t *list) +{ + if (!list) { + return NULL; + } + + return clist_tail(&list->list); +} + +_public_ +dnssec_item_t *dnssec_list_next(dnssec_list_t *list, dnssec_item_t *item) +{ + if (!list || !item) { + return NULL; + } + + return clist_next(&list->list, &item->node); +} + +_public_ +dnssec_item_t *dnssec_list_prev(dnssec_list_t *list, dnssec_item_t *item) +{ + if (!list || !item) { + return NULL; + } + + return clist_prev(&list->list, &item->node); +} + +_public_ +dnssec_item_t *dnssec_list_nth(dnssec_list_t *list, size_t position) +{ + size_t index = 0; + dnssec_item_t *item = dnssec_list_head(list); + + while (item) { + if (index == position) { + return item; + } else { + item = dnssec_list_next(list, item); + index += 1; + } + } + + return NULL; +} + +_public_ +bool dnssec_list_is_empty(dnssec_list_t *list) +{ + return !list || clist_empty(&list->list); +} + +_public_ +size_t dnssec_list_size(dnssec_list_t *list) +{ + return list ? clist_size(&list->list) : 0; +} + +_public_ +int dnssec_list_insert_before(dnssec_item_t *item, void *data) +{ + if (!item) { + return DNSSEC_EINVAL; + } + + dnssec_item_t *add = item_new(data); + if (!add) { + return DNSSEC_ENOMEM; + } + + clist_insert_before(&add->node, &item->node); + + return DNSSEC_EOK; +} + +_public_ +int dnssec_list_insert_after(dnssec_item_t *item, void *data) +{ + if (!item) { + return DNSSEC_EINVAL; + } + + dnssec_item_t *add = item_new(data); + if (!add) { + return DNSSEC_ENOMEM; + } + + clist_insert_after(&add->node, &item->node); + + return DNSSEC_EOK; +} + +_public_ +int dnssec_list_append(dnssec_list_t *list, void *data) +{ + if (!list) { + return DNSSEC_EINVAL; + } + + dnssec_item_t *add = item_new(data); + if (!add) { + return DNSSEC_ENOMEM; + } + + clist_add_tail(&list->list , &add->node); + + return DNSSEC_EOK; +} + +_public_ +int dnssec_list_prepend(dnssec_list_t *list, void *data) +{ + if (!list) { + return DNSSEC_EINVAL; + } + + dnssec_item_t *add = item_new(data); + if (!add) { + return DNSSEC_ENOMEM; + } + + clist_add_head(&list->list , &add->node); + + return DNSSEC_EOK; +} + +_public_ +void dnssec_list_remove(dnssec_item_t *item) +{ + if (item) { + clist_remove(&item->node); + free(item); + } +} + +_public_ +dnssec_item_t *dnssec_list_search(dnssec_list_t *list, void *data) +{ + dnssec_list_foreach(item, list) { + if (item->data == data) { + return item; + } + } + + return NULL; +} + +_public_ +bool dnssec_list_contains(dnssec_list_t *list, void *data) +{ + return dnssec_list_search(list, data) != NULL; +} diff --git a/src/libdnssec/list/ucw_clists.h b/src/libdnssec/list/ucw_clists.h new file mode 100644 index 0000000..080d01e --- /dev/null +++ b/src/libdnssec/list/ucw_clists.h @@ -0,0 +1,260 @@ +/* + * UCW Library -- Circular Linked Lists + * + * (c) 2003--2010 Martin Mares <mj@ucw.cz> + * + * This software may be freely distributed and used according to the terms + * of the GNU Lesser General Public License. + */ + +#ifndef _UCW_CLISTS_H +#define _UCW_CLISTS_H + +/** + * Common header for list nodes. + **/ +typedef struct cnode { + struct cnode *next, *prev; +} cnode; + +/** + * Circular doubly linked list. + **/ +typedef struct clist { + struct cnode head; +} clist; + +/** + * Initialize a new circular linked list. Must be called before any other function. + **/ +static inline void clist_init(clist *l) +{ + cnode *head = &l->head; + head->next = head->prev = head; +} + +/** + * Return the first node on \p l or NULL if \p l is empty. + **/ +static inline void *clist_head(clist *l) +{ + return (l->head.next != &l->head) ? l->head.next : NULL; +} + +/** + * Return the last node on \p l or NULL if \p l is empty. + **/ +static inline void *clist_tail(clist *l) +{ + return (l->head.prev != &l->head) ? l->head.prev : NULL; +} + +/** + * Find the next node to \p n or NULL if \p n is the last one. + **/ +static inline void *clist_next(clist *l, cnode *n) +{ + return (n->next != &l->head) ? (void *) n->next : NULL; +} + +/** + * Find the previous node to \p n or NULL if \p n is the first one. + **/ +static inline void *clist_prev(clist *l, cnode *n) +{ + return (n->prev != &l->head) ? (void *) n->prev : NULL; +} + +/** + * Return a non-zero value iff \p l is empty. + **/ +static inline int clist_empty(clist *l) +{ + return (l->head.next == &l->head); +} + +/** + * Loop over all nodes in the \ref list and perform the next C statement on them. The current node is stored in \p n which must be defined before as pointer to any type. + * The list should not be changed during this loop command. + **/ +#define CLIST_WALK(n,list) for(n=(void*)(list).head.next; (cnode*)(n) != &(list).head; n=(void*)((cnode*)(n))->next) + +/** + * Same as \ref CLIST_WALK(), but allows removal of the current node. This macro requires one more variable to store some temporary pointers. + **/ +#define CLIST_WALK_DELSAFE(n,list,tmp) for(n=(void*)(list).head.next; tmp=(void*)((cnode*)(n))->next, (cnode*)(n) != &(list).head; n=(void*)tmp) + +/** + * Same as \ref CLIST_WALK(), but it defines the variable for the current node in place. \p type should be a pointer type. + **/ +#define CLIST_FOR_EACH(type,n,list) for(type n=(void*)(list).head.next; (cnode*)(n) != &(list).head; n=(void*)((cnode*)(n))->next) + +/** + * Same as \ref CLIST_WALK_DELSAFE(), but it defines the variable for the current node in place. \p type should be a pointer type. The temporary variable must be still known before. + **/ +#define CLIST_FOR_EACH_DELSAFE(type,n,list,tmp) for(type n=(void*)(list).head.next; tmp=(void*)((cnode*)(n))->next, (cnode*)(n) != &(list).head; n=(void*)tmp) + +/** + * Reversed version of \ref CLIST_FOR_EACH(). + **/ +#define CLIST_FOR_EACH_BACKWARDS(type,n,list) for(type n=(void*)(list).head.prev; (cnode*)(n) != &(list).head; n=(void*)((cnode*)(n))->prev) + +/** + * Insert a new node just after the node \p after. To insert at the head of the list, use \ref clist_add_head() instead. + **/ +static inline void clist_insert_after(cnode *what, cnode *after) +{ + cnode *before = after->next; + what->next = before; + what->prev = after; + before->prev = what; + after->next = what; +} + +/** + * Insert a new node just before the node \p before. To insert at the tail of the list, use \ref clist_add_tail() instead. + **/ +static inline void clist_insert_before(cnode *what, cnode *before) +{ + cnode *after = before->prev; + what->next = before; + what->prev = after; + before->prev = what; + after->next = what; +} + +/** + * Insert a new node in front of all other nodes. + **/ +static inline void clist_add_head(clist *l, cnode *n) +{ + clist_insert_after(n, &l->head); +} + +/** + * Insert a new node after all other nodes. + **/ +static inline void clist_add_tail(clist *l, cnode *n) +{ + clist_insert_before(n, &l->head); +} + +/** + * Remove node \p n. + **/ +static inline void clist_remove(cnode *n) +{ + cnode *before = n->prev; + cnode *after = n->next; + before->next = after; + after->prev = before; +} + +/** + * Remove the first node in \p l, if it exists. Return the pointer to that node or NULL. + **/ +static inline void *clist_remove_head(clist *l) +{ + cnode *n = clist_head(l); + if (n) + clist_remove(n); + return n; +} + +/** + * Remove the last node in \p l, if it exists. Return the pointer to that node or NULL. + **/ +static inline void *clist_remove_tail(clist *l) +{ + cnode *n = clist_tail(l); + if (n) + clist_remove(n); + return n; +} + +/** + * Merge two lists by inserting the list \p what just after the node \p after in a different list. + * The first list is then cleared. + **/ +static inline void clist_insert_list_after(clist *what, cnode *after) +{ + if (!clist_empty(what)) + { + cnode *w = &what->head; + w->prev->next = after->next; + after->next->prev = w->prev; + w->next->prev = after; + after->next = w->next; + clist_init(what); + } +} + +/** + * Move all items from a source list to a destination list. The source list + * becomes empty, the original contents of the destination list are destroyed. + **/ +static inline void clist_move(clist *to, clist *from) +{ + clist_init(to); + clist_insert_list_after(from, &to->head); + clist_init(from); +} + +/** + * Compute the number of nodes in \p l. Beware of linear time complexity. + **/ +static inline unsigned int clist_size(clist *l) +{ + unsigned int i = 0; + CLIST_FOR_EACH(cnode *, n, *l) + i++; + return i; +} + +/** + * Remove a node \p n and mark it as unlinked by setting the previous and next pointers to NULL. + **/ +static inline void clist_unlink(cnode *n) +{ + clist_remove(n); + n->prev = n->next = NULL; +} + +/** + * Remove the first node on \p l and mark it as unlinked. + * Return the pointer to that node or NULL. + **/ +static inline void *clist_unlink_head(clist *l) +{ + cnode *n = clist_head(l); + if (n) + clist_unlink(n); + return n; +} + +/** + * Remove the last node on \p l and mark it as unlinked. + * Return the pointer to that node or NULL. + **/ +static inline void *clist_unlink_tail(clist *l) +{ + cnode *n = clist_tail(l); + if (n) + clist_unlink(n); + return n; +} + +/** + * Check if a node is linked a list. Unlinked nodes are recognized by having their + * previous and next pointers equal to NULL. Returns 0 or 1. + * + * Nodes initialized to all zeroes are unlinked, inserting a node anywhere in a list + * makes it linked. Normal removal functions like \ref clist_remove() do not mark nodes + * as unlinked, you need to call \ref clist_unlink() instead. + **/ +static inline int clist_is_linked(cnode *n) +{ + return !!n->next; +} + +#endif diff --git a/src/libdnssec/nsec.h b/src/libdnssec/nsec.h new file mode 100644 index 0000000..ce69752 --- /dev/null +++ b/src/libdnssec/nsec.h @@ -0,0 +1,214 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +/*! + * \file + * + * \addtogroup nsec + * + * \brief NSEC bitmap and NSEC3 hash computation API. + * + * The module provides interface for computation of NSEC3 hashes and for + * construction of bit maps used in NSEC and NSEC3 records. + * + * Example of NSEC3 hash computation: + * + * ~~~~~ {.c} + * + * int result; + * + * // NSEC3 parameters for hashing + * nssec_nsec3_params_t params = { + * .algorithm = DNSSEC_NSEC3_ALGORITHM_SHA1, + * .flags = 0, + * .iterations = 10, + * .salt = { + * .size = 4, + * .data = (uint8_t *){ 0xc0, 0x1d, 0xca, 0xfe } + * } + * }; + * + * // domain name (in wire format) + * uint8_t *dname = "\0x08""knot-dns""\0x02""cz"; + * + * // resulting hash + * dnssec_binary_t hash = { 0 }; + * + * result = dnssec_nsec3_hash(&dname, ¶ms, &hash); + * if (result != DNSSEC_EOK) { + * return result; + * } + * + * assert(hash.size == 20); + * // hash.data contains binary data, which encoded in Base32 would be: + * // 7PTVGE7QV67EM61ROS9238P5RAKR2DM7 + * + * dnssec_binary_free(&hash); + * + * ~~~~~ + * + * Example of NSEC/NSEC3 bitmap construction. + * + * ~~~~~ {.c} + * + * int result; + * dnssec_nsec_bitmap_t *ctx; + * dnssec_binary_t bitmap; + * + * // create encoding context + * ctx = dnssec_nsec_bitmap_new(); + * if (ctx == NULL) { + * return KNOT_ENOMEM; + * } + * + * // add resource records into the bitmap + * dnssec_nsec_bitmap_add(ctx, 1); // A RR type + * dnssec_nsec_bitmap_add(ctx, 28); // AAAA RR type + * + * // allocate space for the encoded bitmap + * size_t size = dnssec_nsec_bitmap_size(ctx); + * result = dnssec_binary_alloc(&bitmap, size); + * if (result != DNSSEC_EOK) { + * dnssec_nsec_bitmap_free(ctx); + * return result; + * } + * + * // write the encoded bitmap and free the context + * dnssec_nsec_bitmap_write(ctx, &bitmap); + * dnssec_nsec_bitmap_free(ctx); + * + * // use the bitmap ... + * + * dnssec_binary_free(&bitmap); + * ~~~~~ + * + * @{ + */ + +#pragma once + +#include <stddef.h> +#include <stdint.h> +#include <stdbool.h> + +#include <libdnssec/binary.h> + +/*! + * DNSSEC NSEC3 algorithm numbers. + */ +typedef enum dnssec_nsec_algorithm { + DNSSEC_NSEC3_ALGORITHM_UNKNOWN = 0, + DNSSEC_NSEC3_ALGORITHM_SHA1 = 1, +} dnssec_nsec3_algorithm_t; + +/*! + * DNSSEC NSEC3 parameters. + */ +typedef struct dnssec_nsec3_params { + dnssec_nsec3_algorithm_t algorithm; /*!< NSEC3 algorithm. */ + uint8_t flags; /*!< NSEC3 flags. */ + uint16_t iterations; /*!< NSEC3 iterations count. */ + dnssec_binary_t salt; /*!< NSEC3 salt. */ +} dnssec_nsec3_params_t; + +/*! + * Free NSEC3 parameters. + */ +void dnssec_nsec3_params_free(dnssec_nsec3_params_t *params); + +/*! + * Parse NSEC3 parameters from NSEC3PARAM RDATA. + * + * \param params Output parameters. + * \param rdata NSEC3PARAM RDATA. + * + * \return Error code, DNSSEC_EOK if successful. + */ +int dnssec_nsec3_params_from_rdata(dnssec_nsec3_params_t *params, + const dnssec_binary_t *rdata); + +/*! + * Check whether a given NSEC bitmap contains a given RR type. + * + * \param bitmap Bitmap of an NSEC record. + * \param size Size of the bitmap. + * \param type RR type to check for. + * + * \return true if bitmap contains type, false otherwise. + */ +bool dnssec_nsec_bitmap_contains(const uint8_t *bitmap, uint16_t size, uint16_t type); + +/*! + * Compute NSEC3 hash for given data. + * + * \todo Input data must be converted to lowercase! + * + * \param[in] data Data to be hashed (usually domain name). + * \param[in] params NSEC3 parameters. + * \param[out] hash Computed hash (will be allocated or resized). + * + * \return Error code, DNSSEC_EOK if successful. + */ +int dnssec_nsec3_hash(const dnssec_binary_t *data, + const dnssec_nsec3_params_t *params, + dnssec_binary_t *hash); + +/*! + * Get length of raw NSEC3 hash for a given algorithm. + * + * \param algorithm NSEC3 algorithm number. + * + * \return Length of raw NSEC3 hash, zero on error. + */ +size_t dnssec_nsec3_hash_length(dnssec_nsec3_algorithm_t algorithm); + +struct dnssec_nsec_bitmap; + +/*! + * Context for encoding of RR types bitmap used in NSEC/NSEC3. + */ +typedef struct dnssec_nsec_bitmap dnssec_nsec_bitmap_t; + +/*! + * Allocate new bit map encoding context. + */ +dnssec_nsec_bitmap_t *dnssec_nsec_bitmap_new(void); + +/*! + * Clear existing bit map encoding context. + */ +void dnssec_nsec_bitmap_clear(dnssec_nsec_bitmap_t *bitmap); + +/*! + * Free bit map encoding context. + */ +void dnssec_nsec_bitmap_free(dnssec_nsec_bitmap_t *bitmap); + +/*! + * Add one RR type into the bitmap. + */ +void dnssec_nsec_bitmap_add(dnssec_nsec_bitmap_t *bitmap, uint16_t type); + +/*! + * Compute the size of the encoded bitmap. + */ +size_t dnssec_nsec_bitmap_size(const dnssec_nsec_bitmap_t *bitmap); + +/*! + * Write encoded bitmap into the given buffer. + */ +void dnssec_nsec_bitmap_write(const dnssec_nsec_bitmap_t *bitmap, uint8_t *output); + +/*! @} */ diff --git a/src/libdnssec/nsec/bitmap.c b/src/libdnssec/nsec/bitmap.c new file mode 100644 index 0000000..001613a --- /dev/null +++ b/src/libdnssec/nsec/bitmap.c @@ -0,0 +1,142 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <limits.h> +#include <stdint.h> +#include <string.h> + +#include "libdnssec/nsec.h" +#include "libdnssec/shared/shared.h" + +#define BITMAP_WINDOW_SIZE 256 +#define BITMAP_WINDOW_BYTES (BITMAP_WINDOW_SIZE/CHAR_BIT) +#define BITMAP_WINDOW_COUNT 256 + +/*! + * One window of an NSEC bitmap. + */ +typedef struct window { + uint8_t used; + uint8_t data[BITMAP_WINDOW_BYTES]; +} window_t; + +struct dnssec_nsec_bitmap { + int used; + window_t windows[BITMAP_WINDOW_COUNT]; +}; + +/* -- public API ----------------------------------------------------------- */ + +/*! + * Allocate new bit map encoding context. + */ +_public_ +dnssec_nsec_bitmap_t *dnssec_nsec_bitmap_new(void) +{ + dnssec_nsec_bitmap_t *bitmap = malloc(sizeof(*bitmap)); + if (!bitmap) { + return NULL; + } + + dnssec_nsec_bitmap_clear(bitmap); + + return bitmap; +} + +/*! + * Clear existing bit map encoding context. + */ +_public_ +void dnssec_nsec_bitmap_clear(dnssec_nsec_bitmap_t *bitmap) +{ + clear_struct(bitmap); +} + +/*! + * Free bit map encoding context. + */ +_public_ +void dnssec_nsec_bitmap_free(dnssec_nsec_bitmap_t *bitmap) +{ + free(bitmap); +} + +/*! + * Add one RR type into the bitmap. + */ +_public_ +void dnssec_nsec_bitmap_add(dnssec_nsec_bitmap_t *bitmap, uint16_t type) +{ + int win = type / BITMAP_WINDOW_SIZE; + int bit = type % BITMAP_WINDOW_SIZE; + + if (bitmap->used <= win) { + bitmap->used = win + 1; + } + + int win_byte = bit / CHAR_BIT; + int win_bit = bit % CHAR_BIT; + + window_t *window = &bitmap->windows[win]; + window->data[win_byte] |= 0x80 >> win_bit; + if (window->used <= win_byte) { + window->used = win_byte + 1; + } +} + +/*! + * Compute the size of the encoded bitmap. + */ +_public_ +size_t dnssec_nsec_bitmap_size(const dnssec_nsec_bitmap_t *bitmap) +{ + size_t result = 0; + + for (int i = 0; i < bitmap->used; i++) { + int used = bitmap->windows[i].used; + if (used == 0) { + continue; + } + + result += 2 + used; // windows number, window size, data + } + + return result; +} + +/*! + * Write encoded bitmap into the given buffer. + */ +_public_ +void dnssec_nsec_bitmap_write(const dnssec_nsec_bitmap_t *bitmap, uint8_t *output) +{ + uint8_t *write_ptr = output; + for (int win = 0; win < bitmap->used; win++) { + int used = bitmap->windows[win].used; + if (used == 0) { + continue; + } + + *write_ptr = (uint8_t)win; + write_ptr += 1; + + *write_ptr = (uint8_t)used; + write_ptr += 1; + + memmove(write_ptr, bitmap->windows[win].data, used); + write_ptr += used; + } +} diff --git a/src/libdnssec/nsec/hash.c b/src/libdnssec/nsec/hash.c new file mode 100644 index 0000000..897320f --- /dev/null +++ b/src/libdnssec/nsec/hash.c @@ -0,0 +1,125 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <assert.h> +#include <gnutls/gnutls.h> +#include <gnutls/crypto.h> +#include <string.h> + +#include "libdnssec/error.h" +#include "libdnssec/nsec.h" +#include "libdnssec/shared/shared.h" + +/*! + * Compute NSEC3 hash for given data and algorithm. + * + * \see RFC 5155 + * + * \todo Input data should be converted to lowercase. + */ +static int nsec3_hash(gnutls_digest_algorithm_t algorithm, int iterations, + const dnssec_binary_t *salt, const dnssec_binary_t *data, + dnssec_binary_t *hash) +{ + assert(salt); + assert(data); + assert(hash); + + int hash_size = gnutls_hash_get_len(algorithm); + if (hash_size <= 0) { + return DNSSEC_NSEC3_HASHING_ERROR; + } + + int result = dnssec_binary_resize(hash, hash_size); + if (result != DNSSEC_EOK) { + return result; + } + + _cleanup_hash_ gnutls_hash_hd_t digest = NULL; + result = gnutls_hash_init(&digest, algorithm); + if (result < 0) { + return DNSSEC_NSEC3_HASHING_ERROR; + } + + const uint8_t *in = data->data; + size_t in_size = data->size; + + for (int i = 0; i <= iterations; i++) { + result = gnutls_hash(digest, in, in_size); + if (result < 0) { + return DNSSEC_NSEC3_HASHING_ERROR; + } + + result = gnutls_hash(digest, salt->data, salt->size); + if (result < 0) { + return DNSSEC_NSEC3_HASHING_ERROR; + } + + gnutls_hash_output(digest, hash->data); + + in = hash->data; + in_size = hash->size; + } + + return DNSSEC_EOK; +} + +/*! + * Get GnuTLS digest algorithm from DNSSEC algorithm number. + */ +static gnutls_digest_algorithm_t algorithm_d2g(dnssec_nsec3_algorithm_t dnssec) +{ + switch (dnssec) { + case DNSSEC_NSEC3_ALGORITHM_SHA1: return GNUTLS_DIG_SHA1; + default: return GNUTLS_DIG_UNKNOWN; + } +} + +/* -- public API ----------------------------------------------------------- */ + +/*! + * Compute NSEC3 hash for given data. + */ +_public_ +int dnssec_nsec3_hash(const dnssec_binary_t *data, + const dnssec_nsec3_params_t *params, + dnssec_binary_t *hash) +{ + if (!data || !params || !hash) { + return DNSSEC_EINVAL; + } + + gnutls_digest_algorithm_t algorithm = algorithm_d2g(params->algorithm); + if (algorithm == GNUTLS_DIG_UNKNOWN) { + return DNSSEC_INVALID_NSEC3_ALGORITHM; + } + + return nsec3_hash(algorithm, params->iterations, ¶ms->salt, data, hash); +} + +/*! + * Get length of raw NSEC3 hash for a given algorithm. + */ +_public_ +size_t dnssec_nsec3_hash_length(dnssec_nsec3_algorithm_t algorithm) +{ + gnutls_digest_algorithm_t gnutls = algorithm_d2g(algorithm); + if (gnutls == GNUTLS_DIG_UNKNOWN) { + return 0; + } + + return gnutls_hash_get_len(gnutls); +} diff --git a/src/libdnssec/nsec/nsec.c b/src/libdnssec/nsec/nsec.c new file mode 100644 index 0000000..bb6084a --- /dev/null +++ b/src/libdnssec/nsec/nsec.c @@ -0,0 +1,116 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "libdnssec/nsec.h" +#include "libdnssec/shared/shared.h" +#include "libdnssec/shared/binary_wire.h" + +#include "libdnssec/binary.h" +#include "libdnssec/error.h" + +/*! + * Free NSEC3 parameters. + */ +_public_ +void dnssec_nsec3_params_free(dnssec_nsec3_params_t *params) +{ + if (!params) { + return; + } + + dnssec_binary_free(¶ms->salt); + clear_struct(params); +} + +/*! + * Parse NSEC3 parameters from NSEC3PARAM RDATA. + * + * \see RFC 5155 (section 4.2) + */ +_public_ +int dnssec_nsec3_params_from_rdata(dnssec_nsec3_params_t *params, + const dnssec_binary_t *rdata) +{ + if (!params || !rdata || !rdata->data) { + return DNSSEC_EINVAL; + } + + dnssec_nsec3_params_t new_params = { 0 }; + + wire_ctx_t wire = binary_init(rdata); + + if (wire_ctx_available(&wire) < 5) { + return DNSSEC_MALFORMED_DATA; + } + + new_params.algorithm = wire_ctx_read_u8(&wire); + new_params.flags = wire_ctx_read_u8(&wire); + new_params.iterations = wire_ctx_read_u16(&wire); + new_params.salt.size = wire_ctx_read_u8(&wire); + + if (wire_ctx_available(&wire) != new_params.salt.size) { + return DNSSEC_MALFORMED_DATA; + } + + new_params.salt.data = malloc(new_params.salt.size); + if (new_params.salt.data == NULL) { + return DNSSEC_ENOMEM; + } + + binary_read(&wire, &new_params.salt); + assert(wire_ctx_offset(&wire) == rdata->size); + + *params = new_params; + + return DNSSEC_EOK; +} + +_public_ +bool dnssec_nsec_bitmap_contains(const uint8_t *bitmap, uint16_t size, uint16_t type) +{ + if (!bitmap || size == 0) { + return false; + } + + const uint8_t type_hi = (type >> 8); // Which window block contains type. + const uint8_t type_lo = (type & 0xff); + const uint8_t bitmap_idx = (type_lo >> 3); // Which byte in the window block contains type. + const uint8_t bit_mask = 1 << (7 - (type_lo & 0x07)); // Which bit in the byte represents type. + + size_t bitmap_pos = 0; + while (bitmap_pos + 3 <= size) { + uint8_t block_idx = bitmap[bitmap_pos++]; // Skip window block No. + uint8_t block_size = bitmap[bitmap_pos++]; // Skip window block size. + + // Size checks. + if (block_size == 0 || bitmap_pos + block_size > size) { + return false; + } + + // Check whether we found the correct window block. + if (block_idx == type_hi) { + if (bitmap_idx < block_size) { + // Check if the bit for type is set. + return bitmap[bitmap_pos + bitmap_idx] & bit_mask; + } + return false; + } else { + bitmap_pos += block_size; + } + } + + return false; +} diff --git a/src/libdnssec/p11/p11.c b/src/libdnssec/p11/p11.c new file mode 100644 index 0000000..07f34f2 --- /dev/null +++ b/src/libdnssec/p11/p11.c @@ -0,0 +1,113 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> +#include <gnutls/pkcs11.h> +#include <stdlib.h> +#include <string.h> + +#include "libdnssec/p11/p11.h" +#include "libdnssec/error.h" + +#ifdef ENABLE_PKCS11 + +#define PKCS11_MODULES_MAX 16 + +static char *pkcs11_modules[PKCS11_MODULES_MAX] = { 0 }; +static int pkcs11_modules_count = 0; + +static int map_result(int gnutls_result) +{ + return gnutls_result == GNUTLS_E_SUCCESS ? DNSSEC_EOK : DNSSEC_ERROR; +} + +int p11_init(void) +{ + int r = gnutls_pkcs11_init(GNUTLS_PKCS11_FLAG_MANUAL, NULL); + return map_result(r); +} + +int p11_reinit(void) +{ + int r = gnutls_pkcs11_reinit(); + return map_result(r); +} + +int p11_load_module(const char *module) +{ + for (int i = 0; i < pkcs11_modules_count; i++) { + if (strcmp(pkcs11_modules[i], module) == 0) { + return DNSSEC_EOK; + } + } + + assert(pkcs11_modules_count <= PKCS11_MODULES_MAX); + if (pkcs11_modules_count == PKCS11_MODULES_MAX) { + return DNSSEC_P11_TOO_MANY_MODULES; + } + + char *copy = strdup(module); + if (!copy) { + return DNSSEC_ENOMEM; + } + + int r = gnutls_pkcs11_add_provider(module, NULL); + if (r != GNUTLS_E_SUCCESS) { + free(copy); + return DNSSEC_P11_FAILED_TO_LOAD_MODULE; + } + + pkcs11_modules[pkcs11_modules_count] = copy; + pkcs11_modules_count += 1; + + return DNSSEC_EOK; +} + +void p11_cleanup(void) +{ + for (int i = 0; i < pkcs11_modules_count; i++) { + free(pkcs11_modules[i]); + pkcs11_modules[i] = NULL; + } + + pkcs11_modules_count = 0; + + gnutls_pkcs11_deinit(); +} + +#else + +int p11_init(void) +{ + return DNSSEC_EOK; +} + +int p11_reinit(void) +{ + return DNSSEC_EOK; +} + +int p11_load_module(const char *module) +{ + return DNSSEC_NOT_IMPLEMENTED_ERROR; +} + +void p11_cleanup(void) +{ + // this function intentionally left blank +} + +#endif diff --git a/src/libdnssec/p11/p11.h b/src/libdnssec/p11/p11.h new file mode 100644 index 0000000..a0f9969 --- /dev/null +++ b/src/libdnssec/p11/p11.h @@ -0,0 +1,41 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +/*! + * Initialize PKCS11 global context. + */ +int p11_init(void); + +/*! + * Reinitialize PKCS11 global context after fork(). + */ +int p11_reinit(void); + +/*! + * Load PKCS11 module unless the module was already loaded. + * + * Duplicates are detected based on the module path. + */ +int p11_load_module(const char *name); + +/*! + * Clenaup PKCS11 global context. + * + * Should be called when the library is deinitialized to prevent memory leaks. + */ +void p11_cleanup(void); diff --git a/src/libdnssec/random.c b/src/libdnssec/random.c new file mode 100644 index 0000000..30b0c2d --- /dev/null +++ b/src/libdnssec/random.c @@ -0,0 +1,53 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <assert.h> +#include <gnutls/gnutls.h> +#include <gnutls/crypto.h> +#include <stddef.h> +#include <stdint.h> + +#include "libdnssec/error.h" +#include "libdnssec/random.h" +#include "libdnssec/shared/shared.h" + +/* -- public API ----------------------------------------------------------- */ + +_public_ +int dnssec_random_buffer(uint8_t *data, size_t size) +{ + if (!data) { + return DNSSEC_EINVAL; + } + + int result = gnutls_rnd(GNUTLS_RND_RANDOM, data, size); + if (result != 0) { + assert_unreachable(); + return DNSSEC_ERROR; + } + + return DNSSEC_EOK; +} + +_public_ +int dnssec_random_binary(dnssec_binary_t *binary) +{ + if (!binary) { + return DNSSEC_EINVAL; + } + + return dnssec_random_buffer(binary->data, binary->size); +} diff --git a/src/libdnssec/random.h b/src/libdnssec/random.h new file mode 100644 index 0000000..0886e51 --- /dev/null +++ b/src/libdnssec/random.h @@ -0,0 +1,85 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +/*! + * \file + * + * \addtogroup random + * + * \brief Pseudo-random number generating API. + * + * The module provides generating of pseudo-random numbers and buffers. + * + * Example: + * + * ~~~ + * + * uint16_t transaction_id = dnssec_random_uint16_t(); + * + * ~~~ + * + * @{ + */ + +#pragma once + +#include <stdint.h> +#include <libdnssec/binary.h> + +/*! + * Fill a buffer with pseudo-random data. + * + * \param data Pointer to the output buffer. + * \param size Size of the output buffer. + * + * \return Error code, DNSEC_EOK if successful. + */ +int dnssec_random_buffer(uint8_t *data, size_t size); + +/*! + * Fill a binary structure with random data. + * + * \param data Preallocated binary structure to be filled. + * + * \return Error code, DNSEC_EOK if successful. + */ +int dnssec_random_binary(dnssec_binary_t *data); + +/*! + * Declare function dnssec_random_<type>(). + */ +#define dnssec_register_random_type(type) \ + static inline type dnssec_random_##type(void) { \ + type value; \ + dnssec_random_buffer((uint8_t *)&value, sizeof(value)); \ + return value; \ + } + +/*! + * Generate pseudo-random 16-bit number. + */ +static inline uint16_t dnssec_random_uint16_t(void); + +/*! + * Generate pseudo-random 32-bit number. + */ +static inline uint32_t dnssec_random_uint32_t(void); + +/*! \cond */ +dnssec_register_random_type(uint16_t); +dnssec_register_random_type(uint32_t); +/*! \endcond */ + +/*! @} */ diff --git a/src/libdnssec/shared/bignum.c b/src/libdnssec/shared/bignum.c new file mode 100644 index 0000000..921a1e6 --- /dev/null +++ b/src/libdnssec/shared/bignum.c @@ -0,0 +1,64 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <assert.h> +#include <string.h> + +#include "libdnssec/shared/bignum.h" + +static void skip_leading_zeroes(dnssec_binary_t *value) +{ + while (value->size > 0 && value->data[0] == 0) { + value->data += 1; + value->size -= 1; + } +} + +size_t bignum_size_u(const dnssec_binary_t *_value) +{ + dnssec_binary_t value = *_value; + skip_leading_zeroes(&value); + + if (value.size == 0) { + return value.size + 1; + } else { + return value.size; + } +} + +size_t bignum_size_s(const dnssec_binary_t *_value) +{ + dnssec_binary_t value = *_value; + skip_leading_zeroes(&value); + + if (value.size == 0 || value.data[0] & 0x80) { + return value.size + 1; + } else { + return value.size; + } +} + +void bignum_write(wire_ctx_t *ctx, size_t width, const dnssec_binary_t *_value) +{ + dnssec_binary_t value = *_value; + skip_leading_zeroes(&value); + + size_t padding_len = width - value.size; + if (padding_len > 0) { + wire_ctx_clear(ctx, padding_len); + } + wire_ctx_write(ctx, value.data, value.size); +} diff --git a/src/libdnssec/shared/bignum.h b/src/libdnssec/shared/bignum.h new file mode 100644 index 0000000..4186a63 --- /dev/null +++ b/src/libdnssec/shared/bignum.h @@ -0,0 +1,41 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include <stdlib.h> + +#include "libdnssec/binary.h" +#include "contrib/wire_ctx.h" + +/*! + * Size needed to write unsigned number in unsigned encoding. + */ +size_t bignum_size_u(const dnssec_binary_t *value); + +/*! + * Size needed to write unsigned number in signed encoding. + * + * Signed encoding expects the MSB to be zero. + */ +size_t bignum_size_s(const dnssec_binary_t *value); + +/*! + * Write unsigned number on a fixed width in a big-endian byte order. + * + * The destination size has to be set properly to accommodate used encoding. + */ +void bignum_write(wire_ctx_t *ctx, size_t width, const dnssec_binary_t *value); diff --git a/src/libdnssec/shared/binary_wire.h b/src/libdnssec/shared/binary_wire.h new file mode 100644 index 0000000..78ccff0 --- /dev/null +++ b/src/libdnssec/shared/binary_wire.h @@ -0,0 +1,53 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include <stdlib.h> + +#include "contrib/wire_ctx.h" +#include "libdnssec/binary.h" + +static inline wire_ctx_t binary_init(const dnssec_binary_t *binary) +{ + assert(binary); + + return wire_ctx_init(binary->data, binary->size); +} + +static inline void binary_read(wire_ctx_t *ctx, dnssec_binary_t *data) +{ + assert(data); + + wire_ctx_read(ctx, data->data, data->size); +} + +static inline void binary_available(wire_ctx_t *ctx, dnssec_binary_t *data) +{ + assert(ctx); + assert(data); + + data->data = ctx->position; + data->size = wire_ctx_available(ctx); +} + +static inline void binary_write(wire_ctx_t *ctx, const dnssec_binary_t *data) +{ + assert(ctx); + assert(data); + + wire_ctx_write(ctx, data->data, data->size); +} diff --git a/src/libdnssec/shared/dname.c b/src/libdnssec/shared/dname.c new file mode 100644 index 0000000..51605f6 --- /dev/null +++ b/src/libdnssec/shared/dname.c @@ -0,0 +1,165 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <assert.h> +#include <stdbool.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> + +#include "libdnssec/shared/dname.h" +#include "libdnssec/shared/shared.h" +#include "contrib/tolower.h" + +/*! + * Get length of a domain name in wire format. + */ +size_t dname_length(const uint8_t *dname) +{ + if (!dname) { + return 0; + } + + const uint8_t *scan = dname; + uint8_t label_len; + do { + label_len = *scan; + scan += 1 + label_len; + } while (label_len > 0); + assert(scan > dname); + + size_t length = scan - dname; + if (length > DNAME_MAX_LENGTH) { + return 0; + } + + return length; +} + +/*! + * Copy domain name in wire format. + */ +uint8_t *dname_copy(const uint8_t *dname) +{ + if (!dname) { + return NULL; + } + + size_t length = dname_length(dname); + if (length == 0) { + return NULL; + } + + uint8_t *copy = malloc(length); + if (!copy) { + return NULL; + } + + memmove(copy, dname, length); + return copy; +} + +/*! + * Normalize dname label in-place. + * + * \return Number of processed bytes, 0 if we encounter the last label. + */ +static uint8_t normalize_label(uint8_t *label) +{ + assert(label); + + uint8_t len = *label; + if (len == 0 || len > DNAME_MAX_LABEL_LENGTH) { + return 0; + } + + for (uint8_t *scan = label + 1, *end = scan + len; scan < end; scan++) { + *scan = knot_tolower(*scan); + } + + return len + 1; +} + +/*! + * Normalize domain name in wire format. + */ +void dname_normalize(uint8_t *dname) +{ + if (!dname) { + return; + } + + uint8_t read, *scan = dname; + do { + read = normalize_label(scan); + scan += read; + } while (read > 0); +} + +/*! + * Compare dname labels case insensitively. + */ +static int label_casecmp(const uint8_t *a, const uint8_t *b, uint8_t len) +{ + assert(a); + assert(b); + + for (const uint8_t *a_end = a + len; a < a_end; a++, b++) { + if (knot_tolower(*a) != knot_tolower(*b)) { + return false; + } + } + + return true; +} + +/*! + * Check if two dnames are equal. + */ +bool dname_equal(const uint8_t *one, const uint8_t *two) +{ + if (!one || !two) { + return false; + } + + const uint8_t *scan_one = one; + const uint8_t *scan_two = two; + + for (;;) { + if (*scan_one != *scan_two) { + return false; + } + + uint8_t len = *scan_one; + if (len == 0) { + return true; + } else if (len > DNAME_MAX_LABEL_LENGTH) { + return false; + } + + scan_one += 1; + scan_two += 1; + + if (!label_casecmp(scan_one, scan_two, len)) { + return false; + } + + scan_one += len; + scan_two += len; + } + + return true; +} diff --git a/src/libdnssec/shared/dname.h b/src/libdnssec/shared/dname.h new file mode 100644 index 0000000..82adeb7 --- /dev/null +++ b/src/libdnssec/shared/dname.h @@ -0,0 +1,57 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> + +/*! + * Maximal length of domain name including labels and length bytes. + * \see RFC 1035 + */ +#define DNAME_MAX_LENGTH 255 + +/*! + * Maximal length of the domain name label, excluding the label size. + * \see RFC 1035 + */ +#define DNAME_MAX_LABEL_LENGTH 63 + +/*! + * Get length of a domain name in wire format. + */ +size_t dname_length(const uint8_t *dname); + +/*! + * Copy domain name in wire format. + */ +uint8_t *dname_copy(const uint8_t *dname); + +/*! + * Normalize domain name in wire format. + * + * Currently converts all letters to lowercase. + */ +void dname_normalize(uint8_t *dname); + +/*! + * Check if two dnames are equal. + * + * Case insensitive. + */ +bool dname_equal(const uint8_t *one, const uint8_t *two); diff --git a/src/libdnssec/shared/fs.c b/src/libdnssec/shared/fs.c new file mode 100644 index 0000000..10c25d9 --- /dev/null +++ b/src/libdnssec/shared/fs.c @@ -0,0 +1,47 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <assert.h> +#include <stdbool.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include "libdnssec/error.h" + +int fs_mkdir(const char *path, mode_t mode, bool ignore_existing) +{ + if (mkdir(path, mode) == 0) { + return DNSSEC_EOK; + } + + if (!ignore_existing || errno != EEXIST) { + return dnssec_errno_to_error(errno); + } + + assert(errno == EEXIST); + + struct stat st = { 0 }; + if (stat(path, &st) != 0) { + return dnssec_errno_to_error(errno); + } + + if (!S_ISDIR(st.st_mode)) { + return dnssec_errno_to_error(ENOTDIR); + } + + return DNSSEC_EOK; +} diff --git a/src/libdnssec/shared/fs.h b/src/libdnssec/shared/fs.h new file mode 100644 index 0000000..542c87b --- /dev/null +++ b/src/libdnssec/shared/fs.h @@ -0,0 +1,25 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include <stdbool.h> +#include <sys/types.h> + +/*! + * Equivalent to mkdir(2), can succeed if the directory already exists. + */ +int fs_mkdir(const char *path, mode_t mode, bool ignore_existing); diff --git a/src/libdnssec/shared/hex.c b/src/libdnssec/shared/hex.c new file mode 100644 index 0000000..03c1491 --- /dev/null +++ b/src/libdnssec/shared/hex.c @@ -0,0 +1,167 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <assert.h> +#include <string.h> +#include <stdbool.h> + +#include "libdnssec/binary.h" +#include "libdnssec/error.h" + +#include "contrib/ctype.h" + +/* -- binary to hex -------------------------------------------------------- */ + +static const char BIN_TO_HEX[] = { '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + +int bin_to_hex_static(const dnssec_binary_t *bin, dnssec_binary_t *hex) +{ + if (!bin || !hex) { + return DNSSEC_EINVAL; + } + + if (bin->size * 2 != hex->size) { + return DNSSEC_EINVAL; + } + + for (size_t i = 0; i < bin->size; i++) { + hex->data[2*i] = BIN_TO_HEX[bin->data[i] >> 4]; + hex->data[2*i+1] = BIN_TO_HEX[bin->data[i] & 0x0f]; + } + + return DNSSEC_EOK; +} + +int bin_to_hex(const dnssec_binary_t *bin, char **hex_ptr) +{ + if (!bin || !hex_ptr) { + return DNSSEC_EINVAL; + } + + size_t hex_size = bin->size * 2; + char *hex = malloc(hex_size + 1); + if (!hex) { + return DNSSEC_ENOMEM; + } + + dnssec_binary_t hex_bin = { .data = (uint8_t *)hex, .size = hex_size }; + bin_to_hex_static(bin, &hex_bin); + hex[hex_size] = '\0'; + + *hex_ptr = hex; + + return DNSSEC_EOK; +} + +/* -- hex to binary -------------------------------------------------------- */ + +/*! + * Convert HEX character to numeric value (assumes valid and lowercase input). + */ +static uint8_t hex_to_number(const char hex) +{ + if (hex >= '0' && hex <= '9') { + return hex - '0'; + } else if (hex >= 'a' && hex <= 'f') { + return hex - 'a' + 10; + } else { + assert(hex >= 'A' && hex <= 'F'); + return hex - 'A' + 10; + } +} + +/*! + * Check if the input string has valid size and contains valid characters. + */ +static bool hex_valid_input(const dnssec_binary_t *hex) +{ + assert(hex); + + if (hex->size % 2 != 0) { + return false; + } + + for (int i = 0; i < hex->size; i++) { + if (!is_xdigit(hex->data[i])) { + return false; + } + } + + return true; +} + +/*! + * Perform hex to bin conversion without checking the validity. + */ +static void hex_to_bin_convert(const dnssec_binary_t *hex, dnssec_binary_t *bin) +{ + assert(hex); + assert(bin); + + for (size_t i = 0; i < bin->size; i++) { + uint8_t high = hex_to_number(hex->data[2 * i]); + uint8_t low = hex_to_number(hex->data[2 * i + 1]); + bin->data[i] = high << 4 | low; + } +} + +int hex_to_bin_static(const dnssec_binary_t *hex, dnssec_binary_t *bin) +{ + if (!hex || !bin) { + return DNSSEC_EINVAL; + } + + if (hex->size / 2 != bin->size) { + return DNSSEC_EINVAL; + } + + if (!hex_valid_input(hex)) { + return DNSSEC_MALFORMED_DATA; + } + + hex_to_bin_convert(hex, bin); + + return DNSSEC_EOK; +} + +int hex_to_bin(const char *hex_str, dnssec_binary_t *bin) +{ + if (!hex_str || !bin) { + return DNSSEC_EINVAL; + } + + dnssec_binary_t hex = { .data = (uint8_t *)hex_str, .size = strlen(hex_str) }; + if (!hex_valid_input(&hex)) { + return DNSSEC_MALFORMED_DATA; + } + + size_t bin_size = hex.size / 2; + if (bin_size == 0) { + bin->size = 0; + bin->data = NULL; + return DNSSEC_EOK; + } + + int result = dnssec_binary_alloc(bin, bin_size); + if (result != DNSSEC_EOK) { + return result; + } + + hex_to_bin_static(&hex, bin); + + return DNSSEC_EOK; +} diff --git a/src/libdnssec/shared/hex.h b/src/libdnssec/shared/hex.h new file mode 100644 index 0000000..5a47cf4 --- /dev/null +++ b/src/libdnssec/shared/hex.h @@ -0,0 +1,39 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include "libdnssec/binary.h" + +/*! + * Convert binary data to preallocated hexadecimal string. + */ +int bin_to_hex_static(const dnssec_binary_t *bin, dnssec_binary_t *hex); + +/** + * Convert binary data to hexadecimal string. + */ +int bin_to_hex(const dnssec_binary_t *bin, char **hex_ptr); + +/*! + * Convert hex encoded string to preallocated binary data. + */ +int hex_to_bin_static(const dnssec_binary_t *hex, dnssec_binary_t *bin); + +/*! + * Convert hex encoded string to binary data. + */ +int hex_to_bin(const char *hex, dnssec_binary_t *bin); diff --git a/src/libdnssec/shared/keyid_gnutls.c b/src/libdnssec/shared/keyid_gnutls.c new file mode 100644 index 0000000..4fde08e --- /dev/null +++ b/src/libdnssec/shared/keyid_gnutls.c @@ -0,0 +1,94 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <assert.h> +#include <gnutls/abstract.h> +#include <gnutls/gnutls.h> +#include <gnutls/x509.h> +#include <string.h> + +#include "libdnssec/binary.h" +#include "libdnssec/error.h" +#include "libdnssec/keyid.h" +#include "libdnssec/shared/keyid_gnutls.h" +#include "libdnssec/shared/shared.h" +#include "libdnssec/shared/hex.h" + +/*! + * Get binary key ID from a key (public or private). + */ +static int keyid_bin(gnutls_x509_privkey_t key, gnutls_pubkey_t pubkey, dnssec_binary_t *id) +{ + assert(key || pubkey); + assert(id); + + // Flags can be used to enable SHA-2 since GnuTLS 3.4.7. + + int flags = 0; + uint8_t *buffer = alloca(DNSSEC_KEYID_BINARY_SIZE); + size_t size = DNSSEC_KEYID_SIZE; + + int r = key ? gnutls_x509_privkey_get_key_id(key, flags, buffer, &size) + : gnutls_pubkey_get_key_id(pubkey, flags, buffer, &size); + + if (r != GNUTLS_E_SUCCESS || size != DNSSEC_KEYID_BINARY_SIZE) { + return DNSSEC_INVALID_KEY_ID; + } + + assert(size == DNSSEC_KEYID_BINARY_SIZE); + r = dnssec_binary_resize(id, size); + if (r != DNSSEC_EOK) { + return r; + } + + memcpy(id->data, buffer, size); + + return DNSSEC_EOK; +} + +/*! + * Get hexadecimal key ID from a key (public or private). + */ +static int keyid_hex(gnutls_x509_privkey_t key, gnutls_pubkey_t pubkey, char **id) +{ + _cleanup_binary_ dnssec_binary_t bin = { 0 }; + int r = keyid_bin(key, pubkey, &bin); + if (r != DNSSEC_EOK) { + return r; + } + + return bin_to_hex(&bin, id); +} + +int keyid_x509(gnutls_x509_privkey_t key, dnssec_binary_t *id) +{ + return keyid_bin(key, NULL, id); +} + +int keyid_x509_hex(gnutls_x509_privkey_t key, char **id) +{ + return keyid_hex(key, NULL, id); +} + +int keyid_pubkey(gnutls_pubkey_t pubkey, dnssec_binary_t *id) +{ + return keyid_bin(NULL, pubkey, id); +} + +int keyid_pubkey_hex(gnutls_pubkey_t pubkey, char **id) +{ + return keyid_hex(NULL, pubkey, id); +} diff --git a/src/libdnssec/shared/keyid_gnutls.h b/src/libdnssec/shared/keyid_gnutls.h new file mode 100644 index 0000000..27ee4cd --- /dev/null +++ b/src/libdnssec/shared/keyid_gnutls.h @@ -0,0 +1,30 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include <gnutls/abstract.h> +#include <gnutls/gnutls.h> + +#include "libdnssec/binary.h" + +int keyid_x509(gnutls_x509_privkey_t key, dnssec_binary_t *id); + +int keyid_x509_hex(gnutls_x509_privkey_t key, char **id); + +int keyid_pubkey(gnutls_pubkey_t pubkey, dnssec_binary_t *id); + +int keyid_pubkey_hex(gnutls_pubkey_t pubkey, char **id); diff --git a/src/libdnssec/shared/pem.c b/src/libdnssec/shared/pem.c new file mode 100644 index 0000000..0e5ba00 --- /dev/null +++ b/src/libdnssec/shared/pem.c @@ -0,0 +1,198 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <assert.h> +#include <gnutls/abstract.h> +#include <gnutls/gnutls.h> +#include <gnutls/x509.h> + +#include "libdnssec/binary.h" +#include "libdnssec/error.h" +#include "libdnssec/key.h" +#include "libdnssec/keyid.h" +#include "libdnssec/shared/keyid_gnutls.h" +#include "libdnssec/shared/pem.h" +#include "libdnssec/shared/shared.h" + +/* -- internal API --------------------------------------------------------- */ + +/*! + * Create GnuTLS X.509 private key from unencrypted PEM data. + */ +int pem_x509(const dnssec_binary_t *pem, gnutls_x509_privkey_t *key) +{ + assert(pem); + assert(key); + + gnutls_datum_t data = binary_to_datum(pem); + + gnutls_x509_privkey_t _key = NULL; + int r = gnutls_x509_privkey_init(&_key); + if (r != GNUTLS_E_SUCCESS) { + return DNSSEC_ENOMEM; + } + + int format = GNUTLS_X509_FMT_PEM; + char *password = NULL; + int flags = GNUTLS_PKCS_PLAIN; + r = gnutls_x509_privkey_import_pkcs8(_key, &data, format, password, flags); + if (r != GNUTLS_E_SUCCESS) { + gnutls_x509_privkey_deinit(_key); + return DNSSEC_PKCS8_IMPORT_ERROR; + } + + *key = _key; + + return DNSSEC_EOK; +} + +/*! + * Create GnuTLS private key from unencrypted PEM data. + */ +int pem_privkey(const dnssec_binary_t *pem, gnutls_privkey_t *key) +{ + assert(pem); + assert(key); + + gnutls_x509_privkey_t key_x509 = NULL; + int r = pem_x509(pem, &key_x509); + if (r != DNSSEC_EOK) { + return r; + } + + gnutls_privkey_t key_abs = NULL; + r = gnutls_privkey_init(&key_abs); + if (r != GNUTLS_E_SUCCESS) { + gnutls_x509_privkey_deinit(key_x509); + return DNSSEC_ENOMEM; + } + + int flags = GNUTLS_PRIVKEY_IMPORT_AUTO_RELEASE; + r = gnutls_privkey_import_x509(key_abs, key_x509, flags); + if (r != GNUTLS_E_SUCCESS) { + gnutls_x509_privkey_deinit(key_x509); + gnutls_privkey_deinit(key_abs); + return DNSSEC_ENOMEM; + } + + *key = key_abs; + + return DNSSEC_EOK; +} + +/*! + * Generate new key and export it in the PEM format. + */ +int pem_generate(gnutls_pk_algorithm_t algorithm, unsigned bits, + dnssec_binary_t *pem, char **id) +{ + assert(pem); + assert(id); + + // generate key + + _cleanup_x509_privkey_ gnutls_x509_privkey_t key = NULL; + int r = gnutls_x509_privkey_init(&key); + if (r != GNUTLS_E_SUCCESS) { + return DNSSEC_ENOMEM; + } + + r = gnutls_x509_privkey_generate(key, algorithm, bits, 0); + if (r != GNUTLS_E_SUCCESS) { + return DNSSEC_KEY_GENERATE_ERROR; + } + + // convert to PEM and export the ID + + dnssec_binary_t _pem = { 0 }; + r = pem_from_x509(key, &_pem); + if (r != DNSSEC_EOK) { + return r; + } + + // export key ID + + char *_id = NULL; + r = keyid_x509_hex(key, &_id); + if (r != DNSSEC_EOK) { + dnssec_binary_free(&_pem); + return r; + } + + *id = _id; + *pem = _pem; + + return DNSSEC_EOK; +} + +static int try_export_pem(gnutls_x509_privkey_t key, dnssec_binary_t *pem) +{ + assert(key); + + gnutls_x509_crt_fmt_t format = GNUTLS_X509_FMT_PEM; + char *password = NULL; + int flags = GNUTLS_PKCS_PLAIN; + + return gnutls_x509_privkey_export_pkcs8(key, format, password, flags, + pem->data, &pem->size); +} + +/*! + * Export GnuTLS X.509 private key to PEM binary. + */ +int pem_from_x509(gnutls_x509_privkey_t key, dnssec_binary_t *pem) +{ + assert(key); + assert(pem); + + dnssec_binary_t _pem = { 0 }; + int r = try_export_pem(key, &_pem); + if (r != GNUTLS_E_SHORT_MEMORY_BUFFER || _pem.size == 0) { + return DNSSEC_KEY_EXPORT_ERROR; + } + + r = dnssec_binary_alloc(&_pem, _pem.size); + if (r != DNSSEC_EOK) { + return r; + } + + r = try_export_pem(key, &_pem); + if (r != GNUTLS_E_SUCCESS) { + dnssec_binary_free(&_pem); + return DNSSEC_KEY_EXPORT_ERROR; + } + + *pem = _pem; + + return DNSSEC_EOK; +} + +/*! + * Get key ID of a private key in PEM format. + */ +int pem_keyid(const dnssec_binary_t *pem, char **id) +{ + assert(pem && pem->size > 0 && pem->data); + assert(id); + + _cleanup_x509_privkey_ gnutls_x509_privkey_t key = NULL; + int r = pem_x509(pem, &key); + if (r != DNSSEC_EOK) { + return r; + } + + return keyid_x509_hex(key, id); +} diff --git a/src/libdnssec/shared/pem.h b/src/libdnssec/shared/pem.h new file mode 100644 index 0000000..c96065b --- /dev/null +++ b/src/libdnssec/shared/pem.h @@ -0,0 +1,74 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include <gnutls/gnutls.h> + +#include "libdnssec/binary.h" + +/*! + * Create GnuTLS X.509 private key from unencrypted PEM data. + * + * \param[in] pem PEM binary data. + * \param[out] key Resulting private key. + * + * \return Error code, DNSSEC_EOK if successful. + */ +int pem_x509(const dnssec_binary_t *pem, gnutls_x509_privkey_t *key); + +/*! + * Create GnuTLS private key from unencrypted PEM data. + * + * \param[in] pem PEM binary data. + * \param[out] key Resulting private key. + * + * \return Error code, DNSSEC_EOK if successful. + */ +int pem_privkey(const dnssec_binary_t *pem, gnutls_privkey_t *key); + +/*! + * Generate a private key and export it in the PEM format. + * + * \param[in] algorithm Algorithm to be used. + * \param[in] bits Size of the key to be generated. + * \param[out] pem Generated key in unencrypted PEM format. + * \param[out] id Key ID of the generated key. + * + * \return Error code, DNSSEC_EOK if successful. + */ +int pem_generate(gnutls_pk_algorithm_t algorithm, unsigned bits, + dnssec_binary_t *pem, char **id); + +/*! + * Export GnuTLS X.509 private key to PEM binary. + * + * \param[in] key Key to be exported. + * \param[out] pem Generated key in unencrypted PEM format. + * + * \return Error code, DNSSEC_EOK if successful. + */ +int pem_from_x509(gnutls_x509_privkey_t key, dnssec_binary_t *pem); + +/*! + * Get key ID of a private key in PEM format. + * + * \param[in] pem Key in unencrypted PEM format. + * \param[out] id ID of the key. + * + * \return Error code, DNSSEC_EOK if successful. + */ +int pem_keyid(const dnssec_binary_t *pem, char **id); diff --git a/src/libdnssec/shared/shared.h b/src/libdnssec/shared/shared.h new file mode 100644 index 0000000..1f55033 --- /dev/null +++ b/src/libdnssec/shared/shared.h @@ -0,0 +1,129 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include <assert.h> +#include <dirent.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <gnutls/abstract.h> +#include <gnutls/crypto.h> +#include <gnutls/gnutls.h> +#include <gnutls/x509.h> + +#include "libdnssec/binary.h" + +#define _public_ __attribute__((visibility("default"))) +#define _hidden_ __attribute__((visibility("hidden"))) + +#define _unused_ __attribute__((unused)) + +/** + * Macro to clear a structure of known size. + * + * \param pointer Pointer to the structure. + */ +#define clear_struct(pointer) memset((pointer), '\0', sizeof(*(pointer))) + +#define streq(one, two) (strcmp((one), (two)) == 0) + +/* -- cleanup macros ------------------------------------------------------- */ + +#define _cleanup_(var) __attribute__((cleanup(var))) + +static inline void free_ptr(void *ptr) +{ + free(*(void **)ptr); +} + +static inline void close_ptr(int *ptr) +{ + if (*ptr != -1) { + close(*ptr); + } +} + +static inline void fclose_ptr(FILE **ptr) +{ + if (*ptr) { + fclose(*ptr); + } +} + +static inline void closedir_ptr(DIR **ptr) +{ + if (*ptr) { + closedir(*ptr); + } +} + +static inline void free_gnutls_datum_ptr(gnutls_datum_t *ptr) +{ + gnutls_free(ptr->data); +} + +static inline void free_x509_privkey_ptr(gnutls_x509_privkey_t *ptr) +{ + if (*ptr) { + gnutls_x509_privkey_deinit(*ptr); + } +} + +static inline void free_pubkey_ptr(gnutls_pubkey_t *ptr) +{ + if (*ptr) { + gnutls_pubkey_deinit(*ptr); + } +} + +static inline void free_gnutls_hash_ptr(gnutls_hash_hd_t *ptr) +{ + if (*ptr) { + gnutls_hash_deinit(*ptr, NULL); + } +} + +#define _cleanup_free_ _cleanup_(free_ptr) +#define _cleanup_close_ _cleanup_(close_ptr) +#define _cleanup_fclose_ _cleanup_(fclose_ptr) +#define _cleanup_closedir_ _cleanup_(closedir_ptr) +#define _cleanup_binary_ _cleanup_(dnssec_binary_free) +#define _cleanup_datum_ _cleanup_(free_gnutls_datum_ptr) +#define _cleanup_x509_privkey_ _cleanup_(free_x509_privkey_ptr) +#define _cleanup_pubkey_ _cleanup_(free_pubkey_ptr) +#define _cleanup_hash_ _cleanup_(free_gnutls_hash_ptr) + +/* -- assertions ----------------------------------------------------------- */ + +#define assert_unreachable() assert(0) + +/* -- crypto helpers ------------------------------------------------------- */ + +static inline gnutls_datum_t binary_to_datum(const dnssec_binary_t *from) +{ + gnutls_datum_t to = { .size = from->size, .data = from->data }; + return to; +} + +static inline dnssec_binary_t binary_from_datum(const gnutls_datum_t *from) +{ + dnssec_binary_t to = { .size = from->size, .data = from->data }; + return to; +} diff --git a/src/libdnssec/sign.h b/src/libdnssec/sign.h new file mode 100644 index 0000000..4313533 --- /dev/null +++ b/src/libdnssec/sign.h @@ -0,0 +1,137 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +/*! + * \file + * + * \addtogroup sign + * + * \brief DNSSEC signing API + * + * The module provides the low level DNSSEC signing and verification. + * + * Example of signature validation: + * + * ~~~~~ {.c} + * + * dnssec_key_t *dnskey = // ... ; + * dnssec_binary_t *rrsig_header = // ... ; + * dnssec_binary_t *covered_rdata = // ... ; + * dnssec_binary_t *signature = // ... ; + * + * int result; + * dnssec_sign_ctx_t *ctx = NULL; + * + * result = dnssec_sign_new(&ctx, dnskey); + * if (result != DNSSEC_EOK) { + * return result; + * } + * + * dnssec_sign_add(ctx, rrsig_header); + * dnssec_sign_add(ctx, covered_rdata); + * + * result = dnssec_sign_verify(ctx, signature); + * if (result == DNSSEC_EOK) { + * // valid signature + * } else if (result == DNSSEC_INVALID_SIGNATURE) { + * // invalid signature + * } else { + * // error + * } + * + * dnssec_sign_free(ctx); + * + * ~~~~~ + * + * + * @{ + */ + +#pragma once + +#include <stdint.h> +#include <stdlib.h> + +#include <libdnssec/binary.h> +#include <libdnssec/key.h> + +struct dnssec_sign_ctx; + +/*! + * DNSSEC signing context. + */ +typedef struct dnssec_sign_ctx dnssec_sign_ctx_t; + +/*! + * Create new DNSSEC signing context. + * + * \note \ref dnssec_sign_init is called as a part of this function. + * + * \param ctx_ptr Pointer to context to be allocated. + * \param key DNSSEC key to be used. + * + * \return Error code, DNSSEC_EOK if successful. + */ +int dnssec_sign_new(dnssec_sign_ctx_t **ctx_ptr, const dnssec_key_t *key); + +/*! + * Free DNSSEC signing context. + * + * \param ctx Signing context to be freed. + */ +void dnssec_sign_free(dnssec_sign_ctx_t *ctx); + +/*! + * Reinitialize DNSSEC signing context to start a new operation. + * + * \param ctx Signing context. + * + * \return Error code, DNSSEC_EOK if successful. + */ +int dnssec_sign_init(dnssec_sign_ctx_t *ctx); + +/*! + * Add data to be covered by DNSSEC signature. + * + * \param ctx Signing context. + * \param data Data to be signed. + * + * \return Error code, DNSSEC_EOK if successful. + */ +int dnssec_sign_add(dnssec_sign_ctx_t *ctx, const dnssec_binary_t *data); + +/*! + * Write down the DNSSEC signature. + * + * \param ctx Signing context. + * \param signature Signature to be allocated and written. + * + * \return Error code, DNSSEC_EOK if successful. + */ +int dnssec_sign_write(dnssec_sign_ctx_t *ctx, dnssec_binary_t *signature); + +/*! + * Verify DNSSEC signature. + * + * \param ctx Signing context. + * \param signature Signature to be verified. + * + * \return Error code. + * \retval DNSSEC_EOK Validation successful, valid signature. + * \retval DNSSEC_INVALID_SIGNATURE Validation successful, invalid signature. + */ +int dnssec_sign_verify(dnssec_sign_ctx_t *ctx, const dnssec_binary_t *signature); + +/** @} */ diff --git a/src/libdnssec/sign/der.c b/src/libdnssec/sign/der.c new file mode 100644 index 0000000..4d79876 --- /dev/null +++ b/src/libdnssec/sign/der.c @@ -0,0 +1,229 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stdbool.h> + +#include "libdnssec/shared/bignum.h" +#include "libdnssec/binary.h" +#include "libdnssec/error.h" +#include "libdnssec/sign/der.h" +#include "libdnssec/shared/binary_wire.h" + +/* + * In fact, this is a very tiny subset of ASN.1 encoding format implementation, + * which is necessary for the purpose of DNSSEC. + * + * References: RFC 3279 (X.509 PKI), X.690, RFC 6605 (ECDSA), RFC8080 (EDDSA) + * + * Dss-Sig-Value ::= SEQUENCE { r INTEGER, s INTEGER } + */ + +#define ASN1_TYPE_SEQUENCE 0x30 +#define ASN1_TYPE_INTEGER 0x02 + +#define ASN1_MAX_SIZE 127 + +/*! + * Check if the next object has a given type. + */ +static bool asn1_expect_type(wire_ctx_t *wire, uint8_t type) +{ + assert(wire); + return (wire_ctx_available(wire) >= 1 && wire_ctx_read_u8(wire) == type); +} + +/*! + * Decode the size of the object (only short format is supported). + */ +static int asn1_decode_size(wire_ctx_t *wire, size_t *size) +{ + assert(wire); + assert(size); + + if (wire_ctx_available(wire) < 1) { + return DNSSEC_MALFORMED_DATA; + } + + uint8_t byte = wire_ctx_read_u8(wire); + if (byte & 0x80) { + // long form, we do not need it for DNSSEC + return DNSSEC_NOT_IMPLEMENTED_ERROR; + } + + *size = byte; + + return DNSSEC_EOK; +} + +/*! + * Decode an unsigned integer object. + */ +static int asn1_decode_integer(wire_ctx_t *wire, dnssec_binary_t *_value) +{ + assert(wire); + assert(_value); + + if (!asn1_expect_type(wire, ASN1_TYPE_INTEGER)) { + return DNSSEC_MALFORMED_DATA; + } + + size_t size; + int result = asn1_decode_size(wire, &size); + if (result != DNSSEC_EOK) { + return result; + } + + if (size == 0 || size > wire_ctx_available(wire)) { + return DNSSEC_MALFORMED_DATA; + } + + dnssec_binary_t value = { .data = wire->position, .size = size }; + wire->position += size; + + // skip leading zeroes (unless equal to zero) + while (value.size > 1 && value.data[0] == 0) { + value.data += 1; + value.size -= 1; + } + + *_value = value; + + return DNSSEC_EOK; +} + +/*! + * Encode object header (type and length). + */ +static void asn1_write_header(wire_ctx_t *wire, uint8_t type, size_t length) +{ + assert(wire); + assert(length < ASN1_MAX_SIZE); + + wire_ctx_write_u8(wire, type); + wire_ctx_write_u8(wire, length); +} + +/*! + * Encode unsigned integer object. + */ +static void asn1_write_integer(wire_ctx_t *wire, size_t integer_size, + const dnssec_binary_t *integer) +{ + assert(wire); + assert(integer); + assert(integer->data); + + asn1_write_header(wire, ASN1_TYPE_INTEGER, integer_size); + bignum_write(wire, integer_size, integer); +} + +/*! + * Decode signature parameters from X.509 ECDSA signature. + */ +int dss_sig_value_decode(const dnssec_binary_t *der, + dnssec_binary_t *r, dnssec_binary_t *s) +{ + if (!der || !der->data || !r || !s) { + return DNSSEC_EINVAL; + } + + wire_ctx_t wire = binary_init(der); + + size_t size; + int result; + + // decode the sequence + + if (!asn1_expect_type(&wire, ASN1_TYPE_SEQUENCE)) { + return DNSSEC_MALFORMED_DATA; + } + + result = asn1_decode_size(&wire, &size); + if (result != DNSSEC_EOK) { + return result; + } + + if (size != wire_ctx_available(&wire)) { + return DNSSEC_MALFORMED_DATA; + } + + // decode the 'r' and 's' values + + dnssec_binary_t der_r; + result = asn1_decode_integer(&wire, &der_r); + if (result != DNSSEC_EOK) { + return result; + } + + dnssec_binary_t der_s; + result = asn1_decode_integer(&wire, &der_s); + if (result != DNSSEC_EOK) { + return result; + } + + if (wire_ctx_available(&wire) != 0) { + return DNSSEC_MALFORMED_DATA; + } + + *r = der_r; + *s = der_s; + + return DNSSEC_EOK; +} + +/*! + * Encode signature parameters from X.509 ECDSA signature. + */ +int dss_sig_value_encode(const dnssec_binary_t *r, const dnssec_binary_t *s, + dnssec_binary_t *der) +{ + if (!r || !r->data || !s || !s->data || !der) { + return DNSSEC_EINVAL; + } + + size_t r_size = bignum_size_s(r); + size_t s_size = bignum_size_s(s); + + // check supported inputs range + + if (r_size > ASN1_MAX_SIZE || s_size > ASN1_MAX_SIZE) { + return DNSSEC_NOT_IMPLEMENTED_ERROR; + } + + size_t seq_size = 2 + r_size + 2 + s_size; + if (seq_size > ASN1_MAX_SIZE) { + return DNSSEC_NOT_IMPLEMENTED_ERROR; + } + + // encode result + + size_t total_size = 2 + seq_size; + + dnssec_binary_t _der = { 0 }; + if (dnssec_binary_alloc(&_der, total_size)) { + return DNSSEC_ENOMEM; + } + + wire_ctx_t wire = binary_init(&_der); + asn1_write_header(&wire, ASN1_TYPE_SEQUENCE, seq_size); + asn1_write_integer(&wire, r_size, r); + asn1_write_integer(&wire, s_size, s); + assert(wire_ctx_available(&wire) == 0); + + *der = _der; + + return DNSSEC_EOK; +} diff --git a/src/libdnssec/sign/der.h b/src/libdnssec/sign/der.h new file mode 100644 index 0000000..db8e910 --- /dev/null +++ b/src/libdnssec/sign/der.h @@ -0,0 +1,56 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include "libdnssec/binary.h" + +/* + * The ECDSA signatures in DNSSEC are encoded differently than in X.509 + * (PKCS #1). The cryptographic libraries usually produce the signatures in + * X.509 format, which uses Dss-Sig-Value to encapsulate 'r' and 's' values + * of the signature. + * + * This module provides decoding and encoding of this format. + * + * The 'r' and 's' values are treated as unsigned values: The leading zeroes + * are stripped on decoding; an extra leading zero is added on encoding in case + * the value starts with a set bit. + */ + +/*! + * Decode signature parameters from X.509 ECDSA signature. + * + * \param[in] der X.509 encoded signature. + * \param[out] s Value 's' of the signature, will point to the data in DER. + * \param[out] r Value 'r' of the signature, will point to the data in DER. + * + * \return Error code, DNSSEC_EOK if successful. + */ +int dss_sig_value_decode(const dnssec_binary_t *der, + dnssec_binary_t *r, dnssec_binary_t *s); + +/*! + * Encode signature parameters from X.509 ECDSA signature. + * + * \param[in] s Value 's' of the signature. + * \param[in] r Value 'r' of the signature. + * \param[out] der X.509 signature, the content will be allocated. + * + * \return Error code, DNSSEC_EOK if successful. + */ +int dss_sig_value_encode(const dnssec_binary_t *r, const dnssec_binary_t *s, + dnssec_binary_t *der); diff --git a/src/libdnssec/sign/sign.c b/src/libdnssec/sign/sign.c new file mode 100644 index 0000000..16b4e64 --- /dev/null +++ b/src/libdnssec/sign/sign.c @@ -0,0 +1,408 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <assert.h> +#include <stdlib.h> +#include <string.h> +#include <gnutls/gnutls.h> +#include <gnutls/crypto.h> + +#include "contrib/macros.h" +#include "libdnssec/shared/bignum.h" +#include "libdnssec/error.h" +#include "libdnssec/key.h" +#include "libdnssec/key/internal.h" +#include "libdnssec/shared/shared.h" +#include "libdnssec/sign.h" +#include "libdnssec/sign/der.h" +#include "libdnssec/shared/binary_wire.h" +#include "libdnssec/contrib/vpool.h" + +/*! + * Signature format conversion callback. + * + * \param ctx DNSSEC signing context. + * \param from Data in source format. + * \param to Allocated data in target format. + * + * \return Error code, DNSSEC_EOK if successful. + */ +typedef int (*signature_convert_cb)(dnssec_sign_ctx_t *ctx, + const dnssec_binary_t *from, + dnssec_binary_t *to); + +/*! + * Algorithm specific callbacks. + */ +typedef struct algorithm_functions { + //! Convert X.509 signature to DNSSEC format. + signature_convert_cb x509_to_dnssec; + //! Convert DNSSEC signature to X.509 format. + signature_convert_cb dnssec_to_x509; +} algorithm_functions_t; + +typedef struct dnssec_buffer { + uint8_t *allocd; //!< Pointer to allocated data. + uint8_t *data; //!< API: pointer to data to copy from. + size_t max_length; + size_t length; //!< API: current length. +} dnssec_buffer_t; + +/*! + * DNSSEC signing context. + */ +struct dnssec_sign_ctx { + const dnssec_key_t *key; //!< Signing key. + const algorithm_functions_t *functions; //!< Implementation specific. + + gnutls_sign_algorithm_t sign_algorithm; //!< Used algorithm for signing. + struct vpool buffer; //!< Buffer for the data to be signed. +}; + +/* -- signature format conversions ----------------------------------------- */ + +/*! + * Conversion of RSA signature between X.509 and DNSSEC format is a NOOP. + * + * \note Described in RFC 3110. + */ +static int rsa_copy_signature(dnssec_sign_ctx_t *ctx, + const dnssec_binary_t *from, + dnssec_binary_t *to) +{ + assert(ctx); + assert(from); + assert(to); + + return dnssec_binary_dup(from, to); +} + +static const algorithm_functions_t rsa_functions = { + .x509_to_dnssec = rsa_copy_signature, + .dnssec_to_x509 = rsa_copy_signature, +}; + +static size_t ecdsa_sign_integer_size(dnssec_sign_ctx_t *ctx) +{ + assert(ctx); + + switch (ctx->sign_algorithm) { + case GNUTLS_SIGN_ECDSA_SHA256: return 32; + case GNUTLS_SIGN_ECDSA_SHA384: return 48; + default: return 0; + }; +} + +/*! + * Convert ECDSA signature to DNSSEC format. + * + * \note Described in RFC 6605. + */ +static int ecdsa_x509_to_dnssec(dnssec_sign_ctx_t *ctx, + const dnssec_binary_t *x509, + dnssec_binary_t *dnssec) +{ + assert(ctx); + assert(x509); + assert(dnssec); + + dnssec_binary_t value_r = { 0 }; + dnssec_binary_t value_s = { 0 }; + + int result = dss_sig_value_decode(x509, &value_r, &value_s); + if (result != DNSSEC_EOK) { + return result; + } + + size_t int_size = ecdsa_sign_integer_size(ctx); + size_t r_size = bignum_size_u(&value_r); + size_t s_size = bignum_size_u(&value_s); + + if (r_size > int_size || s_size > int_size) { + return DNSSEC_MALFORMED_DATA; + } + + result = dnssec_binary_alloc(dnssec, 2 * int_size); + if (result != DNSSEC_EOK) { + return result; + } + + wire_ctx_t wire = binary_init(dnssec); + bignum_write(&wire, int_size, &value_r); + bignum_write(&wire, int_size, &value_s); + assert(wire_ctx_offset(&wire) == dnssec->size); + + return DNSSEC_EOK; +} + +static int ecdsa_dnssec_to_x509(dnssec_sign_ctx_t *ctx, + const dnssec_binary_t *dnssec, + dnssec_binary_t *x509) +{ + assert(ctx); + assert(x509); + assert(dnssec); + + size_t int_size = ecdsa_sign_integer_size(ctx); + + if (dnssec->size != 2 * int_size) { + return DNSSEC_INVALID_SIGNATURE; + } + + const dnssec_binary_t value_r = { .size = int_size, .data = dnssec->data }; + const dnssec_binary_t value_s = { .size = int_size, .data = dnssec->data + int_size }; + + return dss_sig_value_encode(&value_r, &value_s, x509); +} + +static const algorithm_functions_t ecdsa_functions = { + .x509_to_dnssec = ecdsa_x509_to_dnssec, + .dnssec_to_x509 = ecdsa_dnssec_to_x509, +}; + +#define eddsa_copy_signature rsa_copy_signature +static const algorithm_functions_t eddsa_functions = { + .x509_to_dnssec = eddsa_copy_signature, + .dnssec_to_x509 = eddsa_copy_signature, +}; + +/* -- crypto helper functions --------------------------------------------- */ + +static const algorithm_functions_t *get_functions(const dnssec_key_t *key) +{ + uint8_t algorithm = dnssec_key_get_algorithm(key); + + switch ((dnssec_key_algorithm_t)algorithm) { + case DNSSEC_KEY_ALGORITHM_RSA_SHA1: + case DNSSEC_KEY_ALGORITHM_RSA_SHA1_NSEC3: + case DNSSEC_KEY_ALGORITHM_RSA_SHA256: + case DNSSEC_KEY_ALGORITHM_RSA_SHA512: + return &rsa_functions; + case DNSSEC_KEY_ALGORITHM_ECDSA_P256_SHA256: + case DNSSEC_KEY_ALGORITHM_ECDSA_P384_SHA384: + return &ecdsa_functions; + case DNSSEC_KEY_ALGORITHM_ED25519: + case DNSSEC_KEY_ALGORITHM_ED448: + return &eddsa_functions; + default: + return NULL; + } +} + +#ifndef HAVE_SIGN_DATA2 +/** + * Get digest algorithm used with a given key. + */ +static gnutls_digest_algorithm_t get_digest_algorithm(const dnssec_key_t *key) +{ + uint8_t algorithm = dnssec_key_get_algorithm(key); + + switch ((dnssec_key_algorithm_t)algorithm) { + case DNSSEC_KEY_ALGORITHM_RSA_SHA1: + case DNSSEC_KEY_ALGORITHM_RSA_SHA1_NSEC3: + return GNUTLS_DIG_SHA1; + case DNSSEC_KEY_ALGORITHM_RSA_SHA256: + case DNSSEC_KEY_ALGORITHM_ECDSA_P256_SHA256: + return GNUTLS_DIG_SHA256; + case DNSSEC_KEY_ALGORITHM_RSA_SHA512: + return GNUTLS_DIG_SHA512; + case DNSSEC_KEY_ALGORITHM_ECDSA_P384_SHA384: + return GNUTLS_DIG_SHA384; + case DNSSEC_KEY_ALGORITHM_ED25519: + return GNUTLS_DIG_SHA512; + case DNSSEC_KEY_ALGORITHM_ED448: + default: + return GNUTLS_DIG_UNKNOWN; + } +} +#endif + +static gnutls_sign_algorithm_t get_sign_algorithm(const dnssec_key_t *key) +{ + uint8_t algorithm = dnssec_key_get_algorithm(key); + + switch ((dnssec_key_algorithm_t)algorithm) { + case DNSSEC_KEY_ALGORITHM_RSA_SHA1: + case DNSSEC_KEY_ALGORITHM_RSA_SHA1_NSEC3: + return GNUTLS_SIGN_RSA_SHA1; + case DNSSEC_KEY_ALGORITHM_RSA_SHA256: + return GNUTLS_SIGN_RSA_SHA256; + case DNSSEC_KEY_ALGORITHM_ECDSA_P256_SHA256: + return GNUTLS_SIGN_ECDSA_SHA256; + case DNSSEC_KEY_ALGORITHM_RSA_SHA512: + return GNUTLS_SIGN_RSA_SHA512; + case DNSSEC_KEY_ALGORITHM_ECDSA_P384_SHA384: + return GNUTLS_SIGN_ECDSA_SHA384; + case DNSSEC_KEY_ALGORITHM_ED25519: +#ifdef HAVE_ED25519 + return GNUTLS_SIGN_EDDSA_ED25519; +#endif + case DNSSEC_KEY_ALGORITHM_ED448: +#ifdef HAVE_ED448 + return GNUTLS_SIGN_EDDSA_ED448; +#endif + default: + return GNUTLS_SIGN_UNKNOWN; + } +} + +/* -- public API ---------------------------------------------------------- */ + +_public_ +int dnssec_sign_new(dnssec_sign_ctx_t **ctx_ptr, const dnssec_key_t *key) +{ + if (!ctx_ptr) { + return DNSSEC_EINVAL; + } + + dnssec_sign_ctx_t *ctx = calloc(1, sizeof(*ctx)); + + ctx->key = key; + + ctx->functions = get_functions(key); + if (ctx->functions == NULL) { + free(ctx); + return DNSSEC_INVALID_KEY_ALGORITHM; + } + + ctx->sign_algorithm = get_sign_algorithm(key); + int result = dnssec_sign_init(ctx); + if (result != DNSSEC_EOK) { + free(ctx); + return result; + } + + *ctx_ptr = ctx; + + return DNSSEC_EOK; +} + +_public_ +void dnssec_sign_free(dnssec_sign_ctx_t *ctx) +{ + if (!ctx) { + return; + } + + vpool_reset(&ctx->buffer); + + free(ctx); +} + +_public_ +int dnssec_sign_init(dnssec_sign_ctx_t *ctx) +{ + if (!ctx) { + return DNSSEC_EINVAL; + } + + if (vpool_get_buf(&ctx->buffer) != NULL) { + vpool_wipe(&ctx->buffer); + } else { + vpool_init(&ctx->buffer, 1024, 0); + } + + return DNSSEC_EOK; +} + +_public_ +int dnssec_sign_add(dnssec_sign_ctx_t *ctx, const dnssec_binary_t *data) +{ + if (!ctx || !data || !data->data) { + return DNSSEC_EINVAL; + } + + void *result = vpool_insert(&ctx->buffer, vpool_get_length(&ctx->buffer), data->data, data->size); + if (result == NULL) { + return DNSSEC_SIGN_ERROR; + } + + return DNSSEC_EOK; +} + +_public_ +int dnssec_sign_write(dnssec_sign_ctx_t *ctx, dnssec_binary_t *signature) +{ + if (!ctx || !signature) { + return DNSSEC_EINVAL; + } + + if (!dnssec_key_can_sign(ctx->key)) { + return DNSSEC_NO_PRIVATE_KEY; + } + + gnutls_datum_t data = { + .data = vpool_get_buf(&ctx->buffer), + .size = vpool_get_length(&ctx->buffer) + }; + + assert(ctx->key->private_key); + _cleanup_datum_ gnutls_datum_t raw = { 0 }; +#ifdef HAVE_SIGN_DATA2 + int result = gnutls_privkey_sign_data2(ctx->key->private_key, + ctx->sign_algorithm, + 0, &data, &raw); +#else + gnutls_digest_algorithm_t digest_algorithm = get_digest_algorithm(ctx->key); + int result = gnutls_privkey_sign_data(ctx->key->private_key, + digest_algorithm, + 0, &data, &raw); +#endif + if (result < 0) { + return DNSSEC_SIGN_ERROR; + } + + dnssec_binary_t bin_raw = binary_from_datum(&raw); + + return ctx->functions->x509_to_dnssec(ctx, &bin_raw, signature); +} + +_public_ +int dnssec_sign_verify(dnssec_sign_ctx_t *ctx, const dnssec_binary_t *signature) +{ + if (!ctx || !signature) { + return DNSSEC_EINVAL; + } + + if (!dnssec_key_can_verify(ctx->key)) { + return DNSSEC_NO_PUBLIC_KEY; + } + + gnutls_datum_t data = { + .data = vpool_get_buf(&ctx->buffer), + .size = vpool_get_length(&ctx->buffer) + }; + + _cleanup_binary_ dnssec_binary_t bin_raw = { 0 }; + int result = ctx->functions->dnssec_to_x509(ctx, signature, &bin_raw); + if (result != DNSSEC_EOK) { + return result; + } + + gnutls_datum_t raw = binary_to_datum(&bin_raw); + + assert(ctx->key->public_key); + result = gnutls_pubkey_verify_data2(ctx->key->public_key, + ctx->sign_algorithm, + 0, &data, &raw); + if (result == GNUTLS_E_PK_SIG_VERIFY_FAILED) { + return DNSSEC_INVALID_SIGNATURE; + } else if (result < 0) { + return DNSSEC_ERROR; + } + + return DNSSEC_EOK; +} diff --git a/src/libdnssec/tsig.c b/src/libdnssec/tsig.c new file mode 100644 index 0000000..3edc8a3 --- /dev/null +++ b/src/libdnssec/tsig.c @@ -0,0 +1,242 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <assert.h> +#include <gnutls/gnutls.h> +#include <gnutls/crypto.h> +#include <limits.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +#include "libdnssec/shared/dname.h" +#include "libdnssec/error.h" +#include "libdnssec/shared/shared.h" +#include "libdnssec/tsig.h" + +struct dnssec_tsig_ctx { + gnutls_mac_algorithm_t algorithm; + gnutls_hmac_hd_t hash; +}; + +/*! + * TSIG algorithm indentifiers. + */ +typedef struct { + dnssec_tsig_algorithm_t id; + gnutls_mac_algorithm_t gnutls_id; + const char *name; + const char *dname; +} algorithm_id_t; + +/*! + * DNAME to algorithm conversion table. + */ +static const algorithm_id_t ALGORITHM_ID_TABLE[] = { + // RFC 4635 + { DNSSEC_TSIG_HMAC_SHA1, GNUTLS_MAC_SHA1, "hmac-sha1", "\x9hmac-sha1" }, + { DNSSEC_TSIG_HMAC_SHA224, GNUTLS_MAC_SHA224, "hmac-sha224", "\xbhmac-sha224" }, + { DNSSEC_TSIG_HMAC_SHA256, GNUTLS_MAC_SHA256, "hmac-sha256", "\xbhmac-sha256" }, + { DNSSEC_TSIG_HMAC_SHA384, GNUTLS_MAC_SHA384, "hmac-sha384", "\xbhmac-sha384" }, + { DNSSEC_TSIG_HMAC_SHA512, GNUTLS_MAC_SHA512, "hmac-sha512", "\xbhmac-sha512" }, + // RFC 2845 + { DNSSEC_TSIG_HMAC_MD5, GNUTLS_MAC_MD5, "hmac-md5", "\x8hmac-md5\x7sig-alg\x3reg\x3int" }, + { 0 } +}; + +/*! + * Algorithm match callback prototype. + */ +typedef bool (*algorithm_match_cb)(const algorithm_id_t *m, const void *data); + +/*! + * Lookup an algorithm in the algorithm table. + */ +static const algorithm_id_t *lookup_algorithm(algorithm_match_cb match, + const void *data) +{ + assert(match); + + for (const algorithm_id_t *a = ALGORITHM_ID_TABLE; a->id; a++) { + if (match(a, data)) { + return a; + } + } + + return NULL; +} + +static bool match_dname(const algorithm_id_t *algorithm, const void *data) +{ + const uint8_t *search = data; + return dname_equal(search, (uint8_t *)algorithm->dname); +} + +static bool match_name(const algorithm_id_t *algorithm, const void *data) +{ + const char *search = data; + return strcasecmp(search, algorithm->name) == 0; +} + +static bool match_id(const algorithm_id_t *algorithm, const void *data) +{ + dnssec_tsig_algorithm_t search = (dnssec_tsig_algorithm_t)data; + return algorithm->id == search; +} + +/*! + * Convert TSIG algorithm identifier to GnuTLS identifier. + */ +static gnutls_mac_algorithm_t algorithm_to_gnutls(dnssec_tsig_algorithm_t tsig) +{ + const algorithm_id_t *found = lookup_algorithm(match_id, (void *)tsig); + return (found ? found->gnutls_id : GNUTLS_MAC_UNKNOWN); +} + +/* -- public API ----------------------------------------------------------- */ + +_public_ +dnssec_tsig_algorithm_t dnssec_tsig_algorithm_from_dname(const uint8_t *dname) +{ + if (!dname) { + return DNSSEC_TSIG_UNKNOWN; + } + + const algorithm_id_t *found = lookup_algorithm(match_dname, dname); + return (found ? found->id : DNSSEC_TSIG_UNKNOWN); +} + +_public_ +const uint8_t *dnssec_tsig_algorithm_to_dname(dnssec_tsig_algorithm_t algorithm) +{ + const algorithm_id_t *found = lookup_algorithm(match_id, (void *)algorithm); + return (found ? (uint8_t *)found->dname : NULL); +} + +_public_ +dnssec_tsig_algorithm_t dnssec_tsig_algorithm_from_name(const char *name) +{ + if (!name) { + return DNSSEC_TSIG_UNKNOWN; + } + + const algorithm_id_t *found = lookup_algorithm(match_name, name); + return (found ? found->id : DNSSEC_TSIG_UNKNOWN); +} + +_public_ +const char *dnssec_tsig_algorithm_to_name(dnssec_tsig_algorithm_t algorithm) +{ + const algorithm_id_t *found = lookup_algorithm(match_id, (void *)algorithm); + return (found ? found->name : NULL); +} + +_public_ +int dnssec_tsig_optimal_key_size(dnssec_tsig_algorithm_t tsig) +{ + gnutls_mac_algorithm_t mac = algorithm_to_gnutls(tsig); + if (mac == GNUTLS_MAC_UNKNOWN) { + return 0; + } + + return gnutls_mac_get_key_size(mac) * CHAR_BIT; +} + +_public_ +int dnssec_tsig_new(dnssec_tsig_ctx_t **ctx_ptr, + dnssec_tsig_algorithm_t algorithm, + const dnssec_binary_t *key) +{ + if (!ctx_ptr || !key) { + return DNSSEC_EINVAL; + } + + dnssec_tsig_ctx_t *ctx = calloc(1, sizeof(*ctx)); + if (!ctx) { + return DNSSEC_ENOMEM; + } + + ctx->algorithm = algorithm_to_gnutls(algorithm); + if (ctx->algorithm == GNUTLS_MAC_UNKNOWN) { + free(ctx); + return DNSSEC_INVALID_KEY_ALGORITHM; + } + + int result = gnutls_hmac_init(&ctx->hash, ctx->algorithm, key->data, key->size); + if (result != 0) { + free(ctx); + return DNSSEC_SIGN_INIT_ERROR; + } + + *ctx_ptr = ctx; + + return DNSSEC_EOK; +} + +_public_ +void dnssec_tsig_free(dnssec_tsig_ctx_t *ctx) +{ + if (!ctx) { + return; + } + + gnutls_hmac_deinit(ctx->hash, NULL); + free(ctx); +} + +_public_ +int dnssec_tsig_add(dnssec_tsig_ctx_t *ctx, const dnssec_binary_t *data) +{ + if (!ctx || !data) { + return DNSSEC_EINVAL; + } + + int result = gnutls_hmac(ctx->hash, data->data, data->size); + if (result != 0) { + return DNSSEC_SIGN_ERROR; + } + + return DNSSEC_EOK; +} + +_public_ +size_t dnssec_tsig_size(dnssec_tsig_ctx_t *ctx) +{ + if (!ctx) { + return 0; + } + + return gnutls_hmac_get_len(ctx->algorithm); +} + +_public_ +size_t dnssec_tsig_algorithm_size(dnssec_tsig_algorithm_t algorithm) +{ + int gnutls_algorithm = algorithm_to_gnutls(algorithm); + return gnutls_hmac_get_len(gnutls_algorithm); +} + +_public_ +int dnssec_tsig_write(dnssec_tsig_ctx_t *ctx, uint8_t *mac) +{ + if (!ctx || !mac) { + return DNSSEC_EINVAL; + } + + gnutls_hmac_output(ctx->hash, mac); + + return DNSSEC_EOK; +} diff --git a/src/libdnssec/tsig.h b/src/libdnssec/tsig.h new file mode 100644 index 0000000..e861982 --- /dev/null +++ b/src/libdnssec/tsig.h @@ -0,0 +1,207 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +/*! + * \file + * + * \addtogroup tsig + * + * \brief Low-level TSIG signing API. + * + * Example: + * + * ~~~~~ {.c} + * + * int result; + * + * dnssec_binary_t *covered = // ... ; + * dnssec_binary_t *signature = // ... ; + * + * // convert algorithm from textual representation + * dnssec_tsig_algorithm_t algorithm; + * algorithm = dnssec_tsig_algorithm_from_name("hmac-sha256"); + * assert(algorithm == DNSSEC_TSIG_HMAC_SHA256); + * + * // get shared key + * dnssec_binary_t key = { + * .size = 4, + * .data = (uint8_t *) { 0x11, 0x22, 0x33, 0x44 } + * }; + * + * // create computation context + * dnssec_tsig_ctx_t *ctx; + * result = dnssec_tsig_new(&ctx, algorithm, &key); + * if (result != DNSSEC_EOK) { + * return result; + * } + * + * // add data to be covered by the signature + * dnssec_tsig_add(ctx, covered); + * + * // compute the expected signature (MAC) + * size_t size = dnssec_tsig_size(ctx); + * dnssec_binary_t expected_signature = { 0 }; + * result = dnssec_binary_alloc(&expected_signature, size); + * if (result != DNSSEC_EOK) { + * dnssec_tsig_free(ctx); + * return result; + * } + * dnssec_tsig_write(ctx, &expected_signature); + * + * // compare the signatures + * if (dnssec_binary_cmp(signature, &expected_signature) == 0) { + * // valid signature + * } else { + * // invalid signature + * } + * + * // cleanup + * dnssec_binary_free(&expected_signature); + * dnssec_tsig_free(ctx); + * + * ~~~~~ + * + * @{ + */ + +#pragma once + +#include <stdint.h> + +#include <libdnssec/binary.h> + +/*! + * TSIG algorithms. + * + * \note The numeric values are library specific. + */ +typedef enum dnssec_tsig_algorithm { + DNSSEC_TSIG_UNKNOWN = 0, + DNSSEC_TSIG_HMAC_MD5, + DNSSEC_TSIG_HMAC_SHA1, + DNSSEC_TSIG_HMAC_SHA224, + DNSSEC_TSIG_HMAC_SHA256, + DNSSEC_TSIG_HMAC_SHA384, + DNSSEC_TSIG_HMAC_SHA512 +} dnssec_tsig_algorithm_t; + +/*! + * Get TSIG algorithm number from domain name. + * + * \see https://www.iana.org/assignments/tsig-algorithm-names/tsig-algorithm-names.xhtml + * + * \param dname Domain name of the algorithm (e.g., 0x0b hmac-sha256). + * + * \return TSIG algorithm. + */ +dnssec_tsig_algorithm_t dnssec_tsig_algorithm_from_dname(const uint8_t *dname); + +/*! + * Get a domain name of the TSIG algorithm. + * + * \param algorithm TSIG algorithm. + * + * \return Domain name of the TSIG algorithm. + */ +const uint8_t *dnssec_tsig_algorithm_to_dname(dnssec_tsig_algorithm_t algorithm); + +/*! + * Get TSIG algorithm from a MAC name. + * + * \param name MAC name (e.g., hmac-sha256). + * + * \return TSIG algorithm. + */ +dnssec_tsig_algorithm_t dnssec_tsig_algorithm_from_name(const char *name); + +/*! + * Get MAC name from a TSIG algorithm. + * + * \param algorithm TSIG algorithm. + * + * \return MAC name of the TSIG algorithm. + */ +const char *dnssec_tsig_algorithm_to_name(dnssec_tsig_algorithm_t algorithm); + +/*! + * Get optimal size of a TSIG algorithm. + */ +int dnssec_tsig_optimal_key_size(dnssec_tsig_algorithm_t algorithm); + +struct dnssec_tsig_ctx; + +/*! + * TSIG signing context. + */ +typedef struct dnssec_tsig_ctx dnssec_tsig_ctx_t; + +/*! + * Create new TSIG signing context. + * + * \param[out] ctx Resulting TSIG context. + * \param[in] algorithm TSIG algorithm. + * \param[in] key Shared key to be used for signing. + * + * \return Error code, DNSSEC_EOK if successful. + */ +int dnssec_tsig_new(dnssec_tsig_ctx_t **ctx, dnssec_tsig_algorithm_t algorithm, + const dnssec_binary_t *key); + +/*! + * Free the TSIG signing context. + * + * \param ctx TSIG signing context to be freed. + */ +void dnssec_tsig_free(dnssec_tsig_ctx_t *ctx); + +/*! + * Add data to be signed by TSIG. + * + * \param ctx TSIG signing context. + * \param data Data to be signed. + * + * \return Error code, DNSSEC_EOK if successful. + */ +int dnssec_tsig_add(dnssec_tsig_ctx_t *ctx, const dnssec_binary_t *data); + +/*! + * Get size of the TSIG signature for given signing context. + * + * \param ctx TSIG signing context. + * + * \return The size of the TSIG signature. + */ +size_t dnssec_tsig_size(dnssec_tsig_ctx_t *ctx); + +/*! + * Get size of the TSIG signature for given algorithm. + * + * \param algorithm TSIG algorithm. + * + * \return The size of the TSIG signature. + */ +size_t dnssec_tsig_algorithm_size(dnssec_tsig_algorithm_t algorithm); + +/*! + * Write TSIG signature. + * + * \param[in] ctx TSIG signing context. + * \param[out] mac Resulting TSIG signature. + * + * \return Error code, DNSSEC_EOK if successful. + */ +int dnssec_tsig_write(dnssec_tsig_ctx_t *ctx, uint8_t *mac); + +/*! @} */ diff --git a/src/libdnssec/version.h b/src/libdnssec/version.h new file mode 100644 index 0000000..e2c362f --- /dev/null +++ b/src/libdnssec/version.h @@ -0,0 +1,25 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#define DNSSEC_VERSION_MAJOR 2 +#define DNSSEC_VERSION_MINOR 7 +#define DNSSEC_VERSION_PATCH 0x06 + +#define DNSSEC_VERSION_HEX ((DNSSEC_VERSION_MAJOR << 16) | \ + (DNSSEC_VERSION_MINOR << 8) | \ + (DNSSEC_VERSION_PATCH)) diff --git a/src/libdnssec/version.h.in b/src/libdnssec/version.h.in new file mode 100644 index 0000000..de8752f --- /dev/null +++ b/src/libdnssec/version.h.in @@ -0,0 +1,25 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#define DNSSEC_VERSION_MAJOR @KNOT_VERSION_MAJOR@ +#define DNSSEC_VERSION_MINOR @KNOT_VERSION_MINOR@ +#define DNSSEC_VERSION_PATCH 0x0@KNOT_VERSION_PATCH@ + +#define DNSSEC_VERSION_HEX ((DNSSEC_VERSION_MAJOR << 16) | \ + (DNSSEC_VERSION_MINOR << 8) | \ + (DNSSEC_VERSION_PATCH)) diff --git a/src/libknot.pc.in b/src/libknot.pc.in new file mode 100644 index 0000000..b723b24 --- /dev/null +++ b/src/libknot.pc.in @@ -0,0 +1,14 @@ +prefix=@prefix@ +exec_prefix=@prefix@ +libdir=@libdir@ +includedir=@includedir@ +soname=@libknot_SONAME@ + +Name: libknot +Description: Knot DNS library +URL: https://www.knot-dns.cz +Version: @PACKAGE_VERSION@ +Requires.private: libdnssec = @PACKAGE_VERSION@ +Libs: -L${libdir} -lknot +Libs.private: -lm @external_lmdb_LIBS@ +Cflags: -I${includedir} diff --git a/src/libknot/Makefile.inc b/src/libknot/Makefile.inc new file mode 100644 index 0000000..16dc077 --- /dev/null +++ b/src/libknot/Makefile.inc @@ -0,0 +1,78 @@ +lib_LTLIBRARIES += libknot.la +pkgconfig_DATA += libknot.pc + +libknot_la_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAG_VISIBILITY) $(lmdb_CFLAGS) +libknot_la_LDFLAGS = $(AM_LDFLAGS) $(libknot_VERSION_INFO) $(lmdb_LIBS) \ + $(LDFLAG_EXCLUDE_LIBS) +libknot_la_LIBADD = libcontrib.la libdnssec.la $(math_LIBS) + +include_libknotdir = $(includedir) +nobase_include_libknot_HEADERS = \ + libknot/attribute.h \ + libknot/codes.h \ + libknot/consts.h \ + libknot/control/control.h \ + libknot/cookies.h \ + libknot/descriptor.h \ + libknot/dname.h \ + libknot/endian.h \ + libknot/errcode.h \ + libknot/error.h \ + libknot/libknot.h \ + libknot/lookup.h \ + libknot/mm_ctx.h \ + libknot/db/db.h \ + libknot/db/db_lmdb.h \ + libknot/db/db_trie.h \ + libknot/packet/compr.h \ + libknot/packet/pkt.h \ + libknot/packet/rrset-wire.h \ + libknot/packet/wire.h \ + libknot/rdata.h \ + libknot/rdataset.h \ + libknot/rrset-dump.h \ + libknot/rrset.h \ + libknot/rrtype/dnskey.h \ + libknot/rrtype/ds.h \ + libknot/rrtype/naptr.h \ + libknot/rrtype/nsec.h \ + libknot/rrtype/nsec3.h \ + libknot/rrtype/nsec3param.h \ + libknot/rrtype/opt.h \ + libknot/rrtype/rdname.h \ + libknot/rrtype/rrsig.h \ + libknot/rrtype/soa.h \ + libknot/rrtype/tsig.h \ + libknot/tsig-op.h \ + libknot/tsig.h \ + libknot/wire.h \ + libknot/yparser/yparser.h \ + libknot/yparser/ypformat.h \ + libknot/yparser/ypschema.h \ + libknot/yparser/yptrafo.h \ + libknot/version.h + +libknot_la_SOURCES = \ + libknot/codes.c \ + libknot/control/control.c \ + libknot/cookies.c \ + libknot/descriptor.c \ + libknot/dname.c \ + libknot/error.c \ + libknot/db/db_lmdb.c \ + libknot/db/db_trie.c \ + libknot/packet/pkt.c \ + libknot/packet/rrset-wire.c \ + libknot/rdataset.c \ + libknot/rrset-dump.c \ + libknot/rrset.c \ + libknot/rrtype/naptr.c \ + libknot/rrtype/opt.c \ + libknot/rrtype/tsig.c \ + libknot/tsig-op.c \ + libknot/tsig.c \ + libknot/yparser/yparser.c \ + libknot/yparser/ypbody.c \ + libknot/yparser/ypformat.c \ + libknot/yparser/ypschema.c \ + libknot/yparser/yptrafo.c diff --git a/src/libknot/attribute.h b/src/libknot/attribute.h new file mode 100644 index 0000000..629bcc6 --- /dev/null +++ b/src/libknot/attribute.h @@ -0,0 +1,46 @@ +/* Copyright (C) 2014 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \file + * + * \brief Function attributes. + * + * \addtogroup libknot + * @{ + */ + +#pragma once + +/*! \brief Library visibility macros. */ +#define _public_ __attribute__((visibility("default"))) +#define _hidden_ __attribute__((visibility("hidden"))) + +/*! \brief GNU C function attributes. */ +#if __GNUC__ >= 3 +#define _pure_ __attribute__ ((pure)) +#define _const_ __attribute__ ((const)) +#define _noreturn_ __attribute__ ((noreturn)) +#define _malloc_ __attribute__ ((malloc)) +#define _mustcheck_ __attribute__ ((warn_unused_result)) +#else +#define _pure_ +#define _const_ +#define _noreturn_ +#define _malloc_ +#define _mustcheck_ +#endif + +/*! @} */ diff --git a/src/libknot/codes.c b/src/libknot/codes.c new file mode 100644 index 0000000..55f20fb --- /dev/null +++ b/src/libknot/codes.c @@ -0,0 +1,80 @@ +/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "libknot/attribute.h" +#include "libknot/codes.h" +#include "libknot/consts.h" + +_public_ +const knot_lookup_t knot_opcode_names[] = { + { KNOT_OPCODE_QUERY, "QUERY" }, + { KNOT_OPCODE_IQUERY, "IQUERY" }, + { KNOT_OPCODE_STATUS, "STATUS" }, + { KNOT_OPCODE_NOTIFY, "NOTIFY" }, + { KNOT_OPCODE_UPDATE, "UPDATE" }, + { 0, NULL } +}; + +_public_ +const knot_lookup_t knot_rcode_names[] = { + { KNOT_RCODE_NOERROR, "NOERROR" }, + { KNOT_RCODE_FORMERR, "FORMERR" }, + { KNOT_RCODE_SERVFAIL, "SERVFAIL" }, + { KNOT_RCODE_NXDOMAIN, "NXDOMAIN" }, + { KNOT_RCODE_NOTIMPL, "NOTIMPL" }, + { KNOT_RCODE_REFUSED, "REFUSED" }, + { KNOT_RCODE_YXDOMAIN, "YXDOMAIN" }, + { KNOT_RCODE_YXRRSET, "YXRRSET" }, + { KNOT_RCODE_NXRRSET, "NXRRSET" }, + { KNOT_RCODE_NOTAUTH, "NOTAUTH" }, + { KNOT_RCODE_NOTZONE, "NOTZONE" }, + { KNOT_RCODE_BADVERS, "BADVERS" }, + { KNOT_RCODE_BADKEY, "BADKEY" }, + { KNOT_RCODE_BADTIME, "BADTIME" }, + { KNOT_RCODE_BADMODE, "BADMODE" }, + { KNOT_RCODE_BADNAME, "BADNAME" }, + { KNOT_RCODE_BADALG, "BADALG" }, + { KNOT_RCODE_BADTRUNC, "BADTRUNC" }, + { KNOT_RCODE_BADCOOKIE, "BADCOOKIE" }, + { 0, NULL } +}; + +_public_ +const knot_lookup_t knot_tsig_rcode_names[] = { + { KNOT_RCODE_BADSIG, "BADSIG" }, + { 0, NULL } +}; + +_public_ +const knot_lookup_t knot_dnssec_alg_names[] = { + { KNOT_DNSSEC_ALG_RSAMD5, "RSAMD5" }, + { KNOT_DNSSEC_ALG_DH, "DH" }, + { KNOT_DNSSEC_ALG_DSA, "DSA" }, + { KNOT_DNSSEC_ALG_RSASHA1, "RSASHA1" }, + { KNOT_DNSSEC_ALG_DSA_NSEC3_SHA1, "DSA_NSEC3_SHA1" }, + { KNOT_DNSSEC_ALG_RSASHA1_NSEC3_SHA1, "RSASHA1_NSEC3_SHA1" }, + { KNOT_DNSSEC_ALG_RSASHA256, "RSASHA256" }, + { KNOT_DNSSEC_ALG_RSASHA512, "RSASHA512" }, + { KNOT_DNSSEC_ALG_ECC_GOST, "ECC_GOST" }, + { KNOT_DNSSEC_ALG_ECDSAP256SHA256, "ECDSAP256SHA256" }, + { KNOT_DNSSEC_ALG_ECDSAP384SHA384, "ECDSAP384SHA384" }, + { KNOT_DNSSEC_ALG_ED25519, "ED25519" }, + { KNOT_DNSSEC_ALG_ED448, "ED448" }, + { KNOT_DNSSEC_ALG_INDIRECT, "INDIRECT" }, + { KNOT_DNSSEC_ALG_PRIVATEDNS, "PRIVATEDNS" }, + { KNOT_DNSSEC_ALG_PRIVATEOID, "PRIVATEOID" }, + { 0, NULL } +}; diff --git a/src/libknot/codes.h b/src/libknot/codes.h new file mode 100644 index 0000000..c5cf323 --- /dev/null +++ b/src/libknot/codes.h @@ -0,0 +1,49 @@ +/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \file + * + * \brief Some DNS-related code names. + * + * \addtogroup libknot + * @{ + */ + +#pragma once + +#include "libknot/lookup.h" + +/*! + * \brief DNS operation code names. + */ +extern const knot_lookup_t knot_opcode_names[]; + +/*! + * \brief DNS reply code names. + */ +extern const knot_lookup_t knot_rcode_names[]; + +/*! + * \brief TSIG exceptions to reply code names. + */ +extern const knot_lookup_t knot_tsig_rcode_names[]; + +/*! + * \brief DNSSEC algorithm names. + */ +extern const knot_lookup_t knot_dnssec_alg_names[]; + +/*! @} */ diff --git a/src/libknot/consts.h b/src/libknot/consts.h new file mode 100644 index 0000000..e516b9f --- /dev/null +++ b/src/libknot/consts.h @@ -0,0 +1,157 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \file + * + * \brief Some DNS-related constants. + * + * \addtogroup libknot + * @{ + */ + +#pragma once + +/*! + * \brief Basic limits for domain names (RFC 1035). + */ +#define KNOT_DNAME_MAXLEN 255 /*!< 1-byte maximum. */ +#define KNOT_DNAME_MAXLABELS 127 /*!< 1-char labels. */ +#define KNOT_DNAME_MAXLABELLEN 63 /*!< 2^6 - 1 */ + +/*! + * \brief The longest textual dname representation. + * + * 3 x maximum_label + 1 x rest_label + 1 x zero_label + * Each dname label byte takes 4 characters (\\DDD). + * Each label takes 1 more byte for '.' character. + * + * KNOT_DNAME_TXT_MAXLEN = 3x(1 + 63x4) + 1x(1 + 61x4) + 1x(1 + 0) + */ +#define KNOT_DNAME_TXT_MAXLEN 1005 + +/*! + * \brief Address family numbers. + * + * http://www.iana.org/assignments/address-family-numbers/address-family-numbers.xml + */ +typedef enum { + KNOT_ADDR_FAMILY_IPV4 = 1, /*!< IP version 4. */ + KNOT_ADDR_FAMILY_IPV6 = 2 /*!< IP version 6. */ +} knot_addr_family_t; + +/*! + * \brief DNS operation codes (OPCODEs). + * + * http://www.iana.org/assignments/dns-parameters/dns-parameters.xml + */ +typedef enum { + KNOT_OPCODE_QUERY = 0, /*!< Standard query. */ + KNOT_OPCODE_IQUERY = 1, /*!< Inverse query. */ + KNOT_OPCODE_STATUS = 2, /*!< Server status request. */ + KNOT_OPCODE_NOTIFY = 4, /*!< Notify message. */ + KNOT_OPCODE_UPDATE = 5 /*!< Dynamic update. */ +} knot_opcode_t; + +/*! + * \brief DNS reply codes (RCODEs). + * + * http://www.iana.org/assignments/dns-parameters/dns-parameters.xml + */ +typedef enum { + KNOT_RCODE_NOERROR = 0, /*!< No error. */ + KNOT_RCODE_FORMERR = 1, /*!< Format error. */ + KNOT_RCODE_SERVFAIL = 2, /*!< Server failure. */ + KNOT_RCODE_NXDOMAIN = 3, /*!< Non-existent domain. */ + KNOT_RCODE_NOTIMPL = 4, /*!< Not implemented. */ + KNOT_RCODE_REFUSED = 5, /*!< Refused. */ + KNOT_RCODE_YXDOMAIN = 6, /*!< Name should not exist. */ + KNOT_RCODE_YXRRSET = 7, /*!< RR set should not exist. */ + KNOT_RCODE_NXRRSET = 8, /*!< RR set does not exist. */ + KNOT_RCODE_NOTAUTH = 9, /*!< Server not authoritative. / Query not authorized. */ + KNOT_RCODE_NOTZONE = 10, /*!< Name is not inside zone. */ + KNOT_RCODE_BADVERS = 16, /*!< Bad OPT Version. */ + KNOT_RCODE_BADSIG = 16, /*!< (TSIG) Signature failure. */ + KNOT_RCODE_BADKEY = 17, /*!< (TSIG) Key is not supported. */ + KNOT_RCODE_BADTIME = 18, /*!< (TSIG) Signature out of time window. */ + KNOT_RCODE_BADMODE = 19, /*!< (TKEY) Bad mode. */ + KNOT_RCODE_BADNAME = 20, /*!< (TKEY) Duplicate key name. */ + KNOT_RCODE_BADALG = 21, /*!< (TKEY) Algorithm not supported. */ + KNOT_RCODE_BADTRUNC = 22, /*!< (TSIG) Bad truncation. */ + KNOT_RCODE_BADCOOKIE = 23 /*!< Bad/missing server cookie. */ +} knot_rcode_t; + +/*! + * \brief DNS packet section identifiers. + */ +typedef enum { + KNOT_ANSWER = 0, + KNOT_AUTHORITY = 1, + KNOT_ADDITIONAL = 2 +} knot_section_t; + +/*! + * \brief DS digest lengths. + */ +enum knot_ds_algorithm_len +{ + KNOT_DS_DIGEST_LEN_SHA1 = 20, /*!< RFC 3658 */ + KNOT_DS_DIGEST_LEN_SHA256 = 32, /*!< RFC 4509 */ + KNOT_DS_DIGEST_LEN_GOST = 32, /*!< RFC 5933 */ + KNOT_DS_DIGEST_LEN_SHA384 = 48 /*!< RFC 6605 */ +}; + +/*! + * \brief Constants for DNSSEC algorithm types. + * + * Source: http://www.iana.org/assignments/ds-rr-types/ds-rr-types.xml + */ +typedef enum { + KNOT_DS_ALG_SHA1 = 1, + KNOT_DS_ALG_SHA256 = 2, + KNOT_DS_ALG_GOST = 3, + KNOT_DS_ALG_SHA384 = 4 +} knot_ds_algorithm_t; + +/*! + * \brief DNSSEC algorithm numbers. + * + * http://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xml + */ +typedef enum { + KNOT_DNSSEC_ALG_RSAMD5 = 1, + KNOT_DNSSEC_ALG_DH = 2, + KNOT_DNSSEC_ALG_DSA = 3, + + KNOT_DNSSEC_ALG_RSASHA1 = 5, + KNOT_DNSSEC_ALG_DSA_NSEC3_SHA1 = 6, + KNOT_DNSSEC_ALG_RSASHA1_NSEC3_SHA1 = 7, + KNOT_DNSSEC_ALG_RSASHA256 = 8, + + KNOT_DNSSEC_ALG_RSASHA512 = 10, + + KNOT_DNSSEC_ALG_ECC_GOST = 12, + KNOT_DNSSEC_ALG_ECDSAP256SHA256 = 13, + KNOT_DNSSEC_ALG_ECDSAP384SHA384 = 14, + + KNOT_DNSSEC_ALG_ED25519 = 15, + KNOT_DNSSEC_ALG_ED448 = 16, + + KNOT_DNSSEC_ALG_INDIRECT = 252, + KNOT_DNSSEC_ALG_PRIVATEDNS = 253, + KNOT_DNSSEC_ALG_PRIVATEOID = 254 +} knot_dnssec_algorithm_t; + +/*! @} */ diff --git a/src/libknot/control/control.c b/src/libknot/control/control.c new file mode 100644 index 0000000..af68cfe --- /dev/null +++ b/src/libknot/control/control.c @@ -0,0 +1,561 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> +#include <poll.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "libknot/control/control.h" +#include "libknot/attribute.h" +#include "libknot/error.h" +#include "contrib/mempattern.h" +#include "contrib/net.h" +#include "contrib/sockaddr.h" +#include "contrib/ucw/mempool.h" +#include "contrib/wire_ctx.h" + +/*! Size of the input and output buffers. */ +#ifndef CTL_BUFF_SIZE +#define CTL_BUFF_SIZE (256 * 1024) +#endif + +/*! Default socket operations timeout in milliseconds. */ +#define DEFAULT_TIMEOUT (5 * 1000) + +/*! The first data item code. */ +#define DATA_CODE_OFFSET 16 + +/*! Control context structure. */ +struct knot_ctl { + /*! Memory pool context. */ + knot_mm_t mm; + /*! Network operations timeout. */ + int timeout; + /*! Server listening socket. */ + int listen_sock; + /*! Remote server/client socket. */ + int sock; + + /*! The latter read data. */ + knot_ctl_data_t data; + + /*! Write wire context. */ + wire_ctx_t wire_out; + /*! Read wire context. */ + wire_ctx_t wire_in; + + /*! Write buffer. */ + uint8_t buff_out[CTL_BUFF_SIZE]; + /*! Read buffer. */ + uint8_t buff_in[CTL_BUFF_SIZE]; +}; + +static int type_to_code(knot_ctl_type_t type) +{ + switch (type) { + case KNOT_CTL_TYPE_END: return 0; + case KNOT_CTL_TYPE_DATA: return 1; + case KNOT_CTL_TYPE_EXTRA: return 2; + case KNOT_CTL_TYPE_BLOCK: return 3; + default: return -1; + } +} + +static int code_to_type(uint8_t code) +{ + switch (code) { + case 0: return KNOT_CTL_TYPE_END; + case 1: return KNOT_CTL_TYPE_DATA; + case 2: return KNOT_CTL_TYPE_EXTRA; + case 3: return KNOT_CTL_TYPE_BLOCK; + default: return -1; + } +} + +static bool is_data_type(knot_ctl_type_t type) +{ + switch (type) { + case KNOT_CTL_TYPE_DATA: + case KNOT_CTL_TYPE_EXTRA: + return true; + default: + return false; + } +} + +static int idx_to_code(knot_ctl_idx_t idx) +{ + if (idx >= KNOT_CTL_IDX__COUNT) { + return -1; + } + + return DATA_CODE_OFFSET + idx; +} + +static int code_to_idx(uint8_t code) +{ + if (code < DATA_CODE_OFFSET || + code >= DATA_CODE_OFFSET + KNOT_CTL_IDX__COUNT) { + return -1; + } + + return code - DATA_CODE_OFFSET; +} + +static void reset_buffers(knot_ctl_t *ctx) +{ + ctx->wire_out = wire_ctx_init(ctx->buff_out, CTL_BUFF_SIZE); + ctx->wire_in = wire_ctx_init(ctx->buff_in, 0); +} + +static void clean_data(knot_ctl_t *ctx) +{ + mp_flush(ctx->mm.ctx); + memset(ctx->data, 0, sizeof(ctx->data)); +} + +static void close_sock(int *sock) +{ + if (*sock < 0) { + return; + } + + close(*sock); + *sock = -1; +} + +_public_ +knot_ctl_t* knot_ctl_alloc(void) +{ + knot_ctl_t *ctx = malloc(sizeof(*ctx)); + if (ctx == NULL) { + return NULL; + } + memset(ctx, 0, sizeof(*ctx)); + + mm_ctx_mempool(&ctx->mm, MM_DEFAULT_BLKSIZE); + ctx->timeout = DEFAULT_TIMEOUT; + ctx->listen_sock = -1; + ctx->sock = -1; + + reset_buffers(ctx); + + return ctx; +} + +_public_ +void knot_ctl_free(knot_ctl_t *ctx) +{ + if (ctx == NULL) { + return; + } + + close_sock(&ctx->listen_sock); + close_sock(&ctx->sock); + + clean_data(ctx); + + mp_delete(ctx->mm.ctx); + + memset(ctx, 0, sizeof(*ctx)); + free(ctx); +} + +_public_ +void knot_ctl_set_timeout(knot_ctl_t *ctx, int timeout_ms) +{ + if (ctx == NULL) { + return; + } + + ctx->timeout = (timeout_ms > 0) ? timeout_ms : -1; +} + +_public_ +int knot_ctl_bind(knot_ctl_t *ctx, const char *path) +{ + if (ctx == NULL || path == NULL) { + return KNOT_EINVAL; + } + + // Prepare socket address. + struct sockaddr_storage addr; + int ret = sockaddr_set(&addr, AF_UNIX, path, 0); + if (ret != KNOT_EOK) { + return ret; + } + + // Bind the socket. + ctx->listen_sock = net_bound_socket(SOCK_STREAM, (struct sockaddr *)&addr, 0); + if (ctx->listen_sock < 0) { + return ctx->listen_sock; + } + + // Start listening. + if (listen(ctx->listen_sock, 1) != 0) { + close_sock(&ctx->listen_sock); + return knot_map_errno(); + } + + return KNOT_EOK; +} + +_public_ +void knot_ctl_unbind(knot_ctl_t *ctx) +{ + if (ctx == NULL || ctx->listen_sock < 0) { + return; + } + + // Remove the control socket file. + struct sockaddr_storage addr; + socklen_t addr_len = sizeof(addr); + if (getsockname(ctx->listen_sock, (struct sockaddr *)&addr, &addr_len) == 0) { + char addr_str[SOCKADDR_STRLEN] = { 0 }; + if (sockaddr_tostr(addr_str, sizeof(addr_str), (struct sockaddr *)&addr) > 0) { + (void)unlink(addr_str); + } + } + + // Close the listening socket. + close_sock(&ctx->listen_sock); +} + +_public_ +int knot_ctl_accept(knot_ctl_t *ctx) +{ + if (ctx == NULL) { + return KNOT_EINVAL; + } + + knot_ctl_close(ctx); + + // Control interface. + struct pollfd pfd = { .fd = ctx->listen_sock, .events = POLLIN }; + int ret = poll(&pfd, 1, -1); + if (ret <= 0) { + return knot_map_errno(); + } + + int client = net_accept(ctx->listen_sock, NULL); + if (client < 0) { + return client; + } + + ctx->sock = client; + + reset_buffers(ctx); + + return KNOT_EOK; +} + +_public_ +int knot_ctl_connect(knot_ctl_t *ctx, const char *path) +{ + if (ctx == NULL || path == NULL) { + return KNOT_EINVAL; + } + + // Prepare socket address. + struct sockaddr_storage addr; + int ret = sockaddr_set(&addr, AF_UNIX, path, 0); + if (ret != KNOT_EOK) { + return ret; + } + + // Connect to socket. + ctx->sock = net_connected_socket(SOCK_STREAM, (struct sockaddr *)&addr, NULL); + if (ctx->sock < 0) { + return ctx->sock; + } + + reset_buffers(ctx); + + return KNOT_EOK; +} + +_public_ +void knot_ctl_close(knot_ctl_t *ctx) +{ + if (ctx == NULL) { + return; + } + + close_sock(&ctx->sock); +} + +static int ensure_output(knot_ctl_t *ctx, uint16_t len) +{ + wire_ctx_t *w = &ctx->wire_out; + + // Check for enough available room in the output buffer. + size_t available = wire_ctx_available(w); + if (available >= len) { + return KNOT_EOK; + } + + // Flush the buffer. + int ret = net_stream_send(ctx->sock, w->wire, wire_ctx_offset(w), + ctx->timeout); + if (ret < 0) { + return ret; + } + + *w = wire_ctx_init(w->wire, CTL_BUFF_SIZE); + + return KNOT_EOK; +} + +static int send_item(knot_ctl_t *ctx, uint8_t code, const char *data, bool flush) +{ + wire_ctx_t *w = &ctx->wire_out; + + // Write the control block code. + int ret = ensure_output(ctx, sizeof(uint8_t)); + if (ret != KNOT_EOK) { + return ret; + } + wire_ctx_write_u8(w, code); + if (w->error != KNOT_EOK) { + return w->error; + } + + // Control block data is optional. + if (data != NULL) { + // Get the data length. + size_t data_len = strlen(data); + if (data_len > UINT16_MAX) { + return KNOT_ERANGE; + } + + // Write the data length. + ret = ensure_output(ctx, sizeof(uint16_t)); + if (ret != KNOT_EOK) { + return ret; + } + wire_ctx_write_u16(w, data_len); + if (w->error != KNOT_EOK) { + return w->error; + } + + // Write the data. + ret = ensure_output(ctx, data_len); + if (ret != KNOT_EOK) { + return ret; + } + wire_ctx_write(w, (uint8_t *)data, data_len); + if (w->error != KNOT_EOK) { + return w->error; + } + } + + // Send finalized buffer. + if (flush && wire_ctx_offset(w) > 0) { + ret = net_stream_send(ctx->sock, w->wire, wire_ctx_offset(w), + ctx->timeout); + if (ret < 0) { + return ret; + } + + *w = wire_ctx_init(w->wire, CTL_BUFF_SIZE); + } + + return KNOT_EOK; +} + +_public_ +int knot_ctl_send(knot_ctl_t *ctx, knot_ctl_type_t type, knot_ctl_data_t *data) +{ + if (ctx == NULL) { + return KNOT_EINVAL; + } + + // Get the type code. + int code = type_to_code(type); + if (code == -1) { + return KNOT_EINVAL; + } + + // Send unit type. + int ret = send_item(ctx, code, NULL, !is_data_type(type)); + if (ret != KNOT_EOK) { + return ret; + } + + // Send unit data. + if (is_data_type(type) && data != NULL) { + // Send all non-empty data items. + for (knot_ctl_idx_t i = 0; i < KNOT_CTL_IDX__COUNT; i++) { + const char *value = (*data)[i]; + if (value == NULL) { + continue; + } + + ret = send_item(ctx, idx_to_code(i), value, false); + if (ret != KNOT_EOK) { + return ret; + } + } + } + + return KNOT_EOK; +} + +static int ensure_input(knot_ctl_t *ctx, uint16_t len) +{ + wire_ctx_t *w = &ctx->wire_in; + + // Check for enough available room in the input buffer. + size_t available = wire_ctx_available(w); + if (available >= len) { + return KNOT_EOK; + } + + // Move unprocessed data to the beginning of the buffer. + memmove(w->wire, w->wire + wire_ctx_offset(w), available); + + // Receive enough data. + while (available < len) { + int ret = net_stream_recv(ctx->sock, w->wire + available, + CTL_BUFF_SIZE - available, + ctx->timeout); + if (ret < 0) { + return ret; + } + assert(ret > 0); + available += ret; + } + + ctx->wire_in = wire_ctx_init(w->wire, available); + + return KNOT_EOK; +} + +static int receive_item_code(knot_ctl_t *ctx, uint8_t *code) +{ + wire_ctx_t *w = &ctx->wire_in; + + // Read the type. + int ret = ensure_input(ctx, sizeof(uint8_t)); + if (ret != KNOT_EOK) { + return ret; + } + *code = wire_ctx_read_u8(w); + if (w->error != KNOT_EOK) { + return w->error; + } + + return KNOT_EOK; +} + +static int receive_item_value(knot_ctl_t *ctx, char **value) +{ + wire_ctx_t *w = &ctx->wire_in; + + // Read value length. + int ret = ensure_input(ctx, sizeof(uint16_t)); + if (ret != KNOT_EOK) { + return ret; + } + uint16_t data_len = wire_ctx_read_u16(w); + if (w->error != KNOT_EOK) { + return w->error; + } + + // Read the value. + ret = ensure_input(ctx, data_len); + if (ret != KNOT_EOK) { + return ret; + } + *value = mm_alloc(&ctx->mm, data_len + 1); + if (*value == NULL) { + return KNOT_ENOMEM; + } + wire_ctx_read(w, *value, data_len); + if (w->error != KNOT_EOK) { + return w->error; + } + (*value)[data_len] = '\0'; + + return KNOT_EOK; +} + +_public_ +int knot_ctl_receive(knot_ctl_t *ctx, knot_ctl_type_t *type, knot_ctl_data_t *data) +{ + if (ctx == NULL || type == NULL) { + return KNOT_EINVAL; + } + + wire_ctx_t *w = &ctx->wire_in; + + // Reset output variables. + *type = KNOT_CTL_TYPE_END; + clean_data(ctx); + + // Read data units until end of message. + bool have_type = false; + while (true) { + uint8_t code; + int ret = receive_item_code(ctx, &code); + if (ret != KNOT_EOK) { + return ret; + } + + // Process unit type. + int current_type = code_to_type(code); + if (current_type != -1) { + if (have_type) { + // Revert parsed type. + wire_ctx_skip(w, -sizeof(uint8_t)); + assert(w->error == KNOT_EOK); + break; + } + + // Set the unit type. + *type = current_type; + + if (is_data_type(current_type)) { + have_type = true; + continue; + } else { + break; + } + } + + // Check for data item code. + int idx = code_to_idx(code); + if (idx == -1) { + return KNOT_EINVAL; + } + + // Store the item data value. + ret = receive_item_value(ctx, (char **)&ctx->data[idx]); + if (ret != KNOT_EOK) { + return ret; + } + } + + // Set the output data. + if (data != NULL) { + memcpy(*data, ctx->data, sizeof(*data)); + } + + return KNOT_EOK; +} diff --git a/src/libknot/control/control.h b/src/libknot/control/control.h new file mode 100644 index 0000000..e4f73a1 --- /dev/null +++ b/src/libknot/control/control.h @@ -0,0 +1,158 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \file + * + * \brief A server control interface. + * + * \addtogroup ctl + * @{ + */ + +#pragma once + +/*! Control data item indexes. */ +typedef enum { + KNOT_CTL_IDX_CMD = 0, /*!< Control command name. */ + KNOT_CTL_IDX_FLAGS, /*!< Control command flags. */ + KNOT_CTL_IDX_ERROR, /*!< Error message. */ + KNOT_CTL_IDX_SECTION, /*!< Configuration section name. */ + KNOT_CTL_IDX_ITEM, /*!< Configuration item name. */ + KNOT_CTL_IDX_ID, /*!< Congiguration item identifier. */ + KNOT_CTL_IDX_ZONE, /*!< Zone name. */ + KNOT_CTL_IDX_OWNER, /*!< Zone record owner */ + KNOT_CTL_IDX_TTL, /*!< Zone record TTL. */ + KNOT_CTL_IDX_TYPE, /*!< Zone record type name. */ + KNOT_CTL_IDX_DATA, /*!< Configuration item/zone record data. */ + KNOT_CTL_IDX_FILTER, /*!< An option or a filter for output data processing. */ + KNOT_CTL_IDX__COUNT, /*!< The number of data items. */ +} knot_ctl_idx_t; + +/*! Control unit types. */ +typedef enum { + KNOT_CTL_TYPE_END, /*!< End of message, cache flushed. */ + KNOT_CTL_TYPE_DATA, /*!< Data unit, cached. */ + KNOT_CTL_TYPE_EXTRA, /*!< Extra value data unit, cached. */ + KNOT_CTL_TYPE_BLOCK, /*!< End of data block, cache flushed. */ +} knot_ctl_type_t; + +/*! Control input/output string data. */ +typedef const char* knot_ctl_data_t[KNOT_CTL_IDX__COUNT]; + +/*! A control context. */ +struct knot_ctl; +typedef struct knot_ctl knot_ctl_t; + +/*! + * Allocates a control context. + * + * \return Control context. + */ +knot_ctl_t* knot_ctl_alloc(void); + +/*! + * Deallocates a control context. + * + * \param[in] ctx Control context. + */ +void knot_ctl_free(knot_ctl_t *ctx); + +/*! + * Sets the timeout for socket operations. + * + * Default value is 5 seconds. + * + * \param[in] ctx Control context. + * \param[in] timeout_ms Timeout in milliseconds (0 for infinity). + */ +void knot_ctl_set_timeout(knot_ctl_t *ctx, int timeout_ms); + +/*! + * Binds a specified UNIX socket path. + * + * \note Server operation. + * + * \param[in] ctx Control context. + * \param[in] path Control UNIX socket path. + * + * \return Error code, KNOT_EOK if successful. + */ +int knot_ctl_bind(knot_ctl_t *ctx, const char *path); + +/*! + * Unbinds a control socket. + * + * \note Server operation. + * + * \param[in] ctx Control context. + */ +void knot_ctl_unbind(knot_ctl_t *ctx); + +/*! + * Connects to a specified UNIX socket path. + * + * \note Client operation. + * + * \param[in] ctx Control context. + * \param[in] path Control UNIX socket path. + * + * \return Error code, KNOT_EOK if successful. + */ +int knot_ctl_connect(knot_ctl_t *ctx, const char *path); + +/*! + * Waits for an incoming connection. + * + * \note Server operation. + * + * \param[in] ctx Control context. + * + * \return Error code, KNOT_EOK if successful. + */ +int knot_ctl_accept(knot_ctl_t *ctx); + +/*! + * Closes the remote connections. + * + * \note Applies to both server and client. + * + * \param[in] ctx Control context. + */ +void knot_ctl_close(knot_ctl_t *ctx); + +/*! + * Sends one control unit. + * + * \param[in] ctx Control context. + * \param[in] type Unit type to send. + * \param[in] data Data unit to send (optional, ignored if non-data type). + * + * \return Error code, KNOT_EOK if successful. + */ +int knot_ctl_send(knot_ctl_t *ctx, knot_ctl_type_t type, knot_ctl_data_t *data); + +/*! + * Receives one control unit. + * + * \param[in] ctx Control context. + * \param[out] type Received unit type. + * \param[out] data Received data unit (optional). + * + * \return Error code, KNOT_EOK if successful. + */ +int knot_ctl_receive(knot_ctl_t *ctx, knot_ctl_type_t *type, knot_ctl_data_t *data); + +/*! @} */ diff --git a/src/libknot/cookies.c b/src/libknot/cookies.c new file mode 100644 index 0000000..dd024d5 --- /dev/null +++ b/src/libknot/cookies.c @@ -0,0 +1,130 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> +#include <stdbool.h> + +#include "libknot/attribute.h" +#include "libknot/cookies.h" +#include "libknot/errcode.h" +#include "contrib/string.h" +#include "contrib/sockaddr.h" +#include "contrib/openbsd/siphash.h" + +_public_ +int knot_edns_cookie_client_generate(knot_edns_cookie_t *out, + const knot_edns_cookie_params_t *params) +{ + if (out == NULL || params == NULL || params->client_addr == NULL || + params->server_addr == NULL) { + return KNOT_EINVAL; + } + + SIPHASH_CTX ctx; + assert(sizeof(params->secret) == sizeof(SIPHASH_KEY)); + SipHash24_Init(&ctx, (const SIPHASH_KEY *)params->secret); + + size_t addr_len = 0; + void *addr = sockaddr_raw(params->client_addr, &addr_len); + SipHash24_Update(&ctx, addr, addr_len); + + addr_len = 0; + addr = sockaddr_raw(params->server_addr, &addr_len); + SipHash24_Update(&ctx, addr, addr_len); + + uint64_t hash = SipHash24_End(&ctx); + memcpy(out->data, &hash, sizeof(hash)); + out->len = sizeof(hash); + + return KNOT_EOK; +} + +_public_ +int knot_edns_cookie_client_check(const knot_edns_cookie_t *cc, + const knot_edns_cookie_params_t *params) +{ + if (cc == NULL || cc->len != KNOT_EDNS_COOKIE_CLNT_SIZE) { + return KNOT_EINVAL; + } + + knot_edns_cookie_t ref; + int ret = knot_edns_cookie_client_generate(&ref, params); + if (ret != KNOT_EOK) { + return ret; + } + assert(ref.len == KNOT_EDNS_COOKIE_CLNT_SIZE); + + ret = const_time_memcmp(cc->data, ref.data, KNOT_EDNS_COOKIE_CLNT_SIZE); + if (ret != 0) { + return KNOT_EINVAL; + } + + return KNOT_EOK; +} + +_public_ +int knot_edns_cookie_server_generate(knot_edns_cookie_t *out, + const knot_edns_cookie_t *cc, + const knot_edns_cookie_params_t *params) +{ + if (out == NULL || cc == NULL || cc->len != KNOT_EDNS_COOKIE_CLNT_SIZE || + params == NULL || params->client_addr == NULL) { + return KNOT_EINVAL; + } + + SIPHASH_CTX ctx; + assert(sizeof(params->secret) == sizeof(SIPHASH_KEY)); + SipHash24_Init(&ctx, (const SIPHASH_KEY *)params->secret); + + size_t addr_len = 0; + void *addr = sockaddr_raw(params->client_addr, &addr_len); + SipHash24_Update(&ctx, addr, addr_len); + + SipHash24_Update(&ctx, cc->data, cc->len); + + uint64_t hash = SipHash24_End(&ctx); + memcpy(out->data, &hash, sizeof(hash)); + out->len = sizeof(hash); + + return KNOT_EOK; +} + +_public_ +int knot_edns_cookie_server_check(const knot_edns_cookie_t *sc, + const knot_edns_cookie_t *cc, + const knot_edns_cookie_params_t *params) +{ + if (sc == NULL || cc == NULL || params == NULL) { + return KNOT_EINVAL; + } + + knot_edns_cookie_t ref; + int ret = knot_edns_cookie_server_generate(&ref, cc, params); + if (ret != KNOT_EOK) { + return ret; + } + + if (sc->len != ref.len) { + return KNOT_EINVAL; + } + + ret = const_time_memcmp(sc->data, ref.data, sc->len); + if (ret != 0) { + return KNOT_EINVAL; + } + + return KNOT_EOK; +} diff --git a/src/libknot/cookies.h b/src/libknot/cookies.h new file mode 100644 index 0000000..5bdfb68 --- /dev/null +++ b/src/libknot/cookies.h @@ -0,0 +1,97 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! +* \file +* +* \brief DNS cookies processing. +* +* \addtogroup libknot +* @{ +*/ + +#pragma once + +#include <stdint.h> +#include <sys/socket.h> + +#include "libknot/rrtype/opt.h" + +#define KNOT_EDNS_COOKIE_SECRET_SIZE 16 + +/*! + * \brief DNS Cookie parameters needed to compute the cookie value. + * + * \note Server address is not used for the server cookie check. + */ +typedef struct { + const struct sockaddr *client_addr; /*!< Client socket address. */ + const struct sockaddr *server_addr; /*!< Server socket address. */ + uint8_t secret[KNOT_EDNS_COOKIE_SECRET_SIZE]; /*!< Cookie secret data. */ +} knot_edns_cookie_params_t; + +/*! + * \brief Generate a client cookie using given parameters. + * + * \param out Generated client cookie. + * \param params Client cookie parameters. + * + * \retval KNOT_EOK + * \retval KNOT_EINVAL + */ +int knot_edns_cookie_client_generate(knot_edns_cookie_t *out, + const knot_edns_cookie_params_t *params); + +/*! + * \brief Check whether client cookie was generated using given parameters. + * + * \param cc Client cookie that should be checked. + * \param params Client cookie parameters. + * + * \retval KNOT_EOK + * \retval KNOT_EINVAL + */ +int knot_edns_cookie_client_check(const knot_edns_cookie_t *cc, + const knot_edns_cookie_params_t *params); + +/*! + * \brief Generate a server cookie using given parameters. + * + * \param out Generated server cookie. + * \param cc Client cookie parameter. + * \param params Server cookie parameters. + * + * \retval KNOT_EOK + * \retval KNOT_EINVAL + */ +int knot_edns_cookie_server_generate(knot_edns_cookie_t *out, + const knot_edns_cookie_t *cc, + const knot_edns_cookie_params_t *params); + +/*! + * \brief Check whether server cookie was generated using given parameters. + * + * \param sc Server cookie that should be checked. + * \param cc Client cookie parameter. + * \param params Server cookie parameters. + * + * \retval KNOT_EOK + * \retval KNOT_EINVAL + */ +int knot_edns_cookie_server_check(const knot_edns_cookie_t *sc, + const knot_edns_cookie_t *cc, + const knot_edns_cookie_params_t *params); + +/*! @} */ diff --git a/src/libknot/db/db.h b/src/libknot/db/db.h new file mode 100644 index 0000000..f3edc15 --- /dev/null +++ b/src/libknot/db/db.h @@ -0,0 +1,91 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +/*! + * \file + * + * \brief Structures for binary data handling. + * + * \addtogroup db + * @{ + */ + +#pragma once + +#include "libknot/mm_ctx.h" + +enum { + /* Database flags */ + + KNOT_DB_RDONLY = 1 << 0, /*!< Read only. */ + KNOT_DB_SORTED = 1 << 1, /*!< Sorted output. */ + + /* Operations */ + + KNOT_DB_NOOP = 1 << 2, /*!< No operation. */ + KNOT_DB_FIRST = 1 << 3, /*!< First entry. */ + KNOT_DB_LAST = 1 << 4, /*!< Last entry. */ + KNOT_DB_NEXT = 1 << 5, /*!< Next entry. */ + KNOT_DB_PREV = 1 << 6, /*!< Previous entry. */ + KNOT_DB_LEQ = 1 << 7, /*!< Lesser or equal. */ + KNOT_DB_GEQ = 1 << 8 /*!< Greater or equal. */ +}; + +typedef void knot_db_t; +typedef void knot_db_iter_t; + +typedef struct knot_db_val { + void *data; + size_t len; +} knot_db_val_t; + +typedef struct knot_db_txn { + knot_db_t *db; + void *txn; +} knot_db_txn_t; + +typedef struct knot_db_api { + const char *name; + + /* Context operations */ + + int (*init)(knot_db_t **db, knot_mm_t *mm, void *opts); + void (*deinit)(knot_db_t *db); + + /* Transactions */ + + int (*txn_begin)(knot_db_t *db, knot_db_txn_t *txn, unsigned flags); + int (*txn_commit)(knot_db_txn_t *txn); + void (*txn_abort)(knot_db_txn_t *txn); + + /* Data access */ + + int (*count)(knot_db_txn_t *txn); + int (*clear)(knot_db_txn_t *txn); + int (*find)(knot_db_txn_t *txn, knot_db_val_t *key, knot_db_val_t *val, unsigned flags); + int (*insert)(knot_db_txn_t *txn, knot_db_val_t *key, knot_db_val_t *val, unsigned flags); + int (*del)(knot_db_txn_t *txn, knot_db_val_t *key); + + /* Iteration */ + + knot_db_iter_t *(*iter_begin)(knot_db_txn_t *txn, unsigned flags); + knot_db_iter_t *(*iter_seek)(knot_db_iter_t *iter, knot_db_val_t *key, unsigned flags); + knot_db_iter_t *(*iter_next)(knot_db_iter_t *iter); + int (*iter_key)(knot_db_iter_t *iter, knot_db_val_t *key); + int (*iter_val)(knot_db_iter_t *iter, knot_db_val_t *val); + void (*iter_finish)(knot_db_iter_t *iter); +} knot_db_api_t; + +/*! @} */ diff --git a/src/libknot/db/db_lmdb.c b/src/libknot/db/db_lmdb.c new file mode 100644 index 0000000..f0e7be5 --- /dev/null +++ b/src/libknot/db/db_lmdb.c @@ -0,0 +1,568 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <assert.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <unistd.h> + +#include "libknot/attribute.h" +#include "libknot/errcode.h" +#include "libknot/db/db_lmdb.h" +#include "contrib/mempattern.h" + +#include <lmdb.h> + +/* Defines */ +#define LMDB_DIR_MODE 0770 +#define LMDB_FILE_MODE 0660 + +_public_ const unsigned KNOT_DB_LMDB_NOTLS = MDB_NOTLS; +_public_ const unsigned KNOT_DB_LMDB_RDONLY = MDB_RDONLY; +_public_ const unsigned KNOT_DB_LMDB_INTEGERKEY = MDB_INTEGERKEY; +_public_ const unsigned KNOT_DB_LMDB_NOSYNC = MDB_NOSYNC; +_public_ const unsigned KNOT_DB_LMDB_WRITEMAP = MDB_WRITEMAP; +_public_ const unsigned KNOT_DB_LMDB_MAPASYNC = MDB_MAPASYNC; +_public_ const unsigned KNOT_DB_LMDB_DUPSORT = MDB_DUPSORT; + +struct lmdb_env +{ + bool shared; + MDB_dbi dbi; + MDB_env *env; + knot_mm_t *pool; +}; + +/*! + * \brief Convert error code returned by LMDB to Knot DNS error code. + * + * LMDB defines own error codes but uses additional ones from libc: + * - LMDB errors do not conflict with Knot DNS ones. + * - Significant LMDB errors are mapped to Knot DNS ones. + * - Standard errors are converted to negative value to match Knot DNS mapping. + */ +static int lmdb_error_to_knot(int error) +{ + if (error == MDB_SUCCESS) { + return KNOT_EOK; + } + + if (error == MDB_NOTFOUND) { + return KNOT_ENOENT; + } + + if (error == MDB_TXN_FULL) { + return KNOT_ELIMIT; + } + + if (error == MDB_MAP_FULL || error == ENOSPC) { + return KNOT_ESPACE; + } + + return -abs(error); +} + +static int create_env_dir(const char *path) +{ + int r = mkdir(path, LMDB_DIR_MODE); + if (r == -1 && errno != EEXIST) { + return lmdb_error_to_knot(errno); + } + + return KNOT_EOK; +} + +/*! \brief Set the environment map size. + * \note This also sets the maximum database size, see mdb_env_set_mapsize + */ +static int set_mapsize(MDB_env *env, size_t map_size) +{ + long page_size = sysconf(_SC_PAGESIZE); + if (page_size <= 0) { + return KNOT_ERROR; + } + + /* Round to page size. */ + map_size = (map_size / page_size) * page_size; + int ret = mdb_env_set_mapsize(env, map_size); + if (ret != MDB_SUCCESS) { + return lmdb_error_to_knot(ret); + } + + return KNOT_EOK; +} + +/*! \brief Close the database. */ +static void dbase_close(struct lmdb_env *env) +{ + mdb_dbi_close(env->env, env->dbi); + if (!env->shared) { + mdb_env_close(env->env); + } +} + +/*! \brief Open database environment. */ +static int dbase_open_env(struct lmdb_env *env, struct knot_db_lmdb_opts *opts) +{ + MDB_env *mdb_env = NULL; + int ret = mdb_env_create(&mdb_env); + if (ret != MDB_SUCCESS) { + return lmdb_error_to_knot(ret); + } + + ret = create_env_dir(opts->path); + if (ret != KNOT_EOK) { + mdb_env_close(mdb_env); + return ret; + } + + ret = set_mapsize(mdb_env, opts->mapsize); + if (ret != KNOT_EOK) { + mdb_env_close(mdb_env); + return ret; + } + + ret = mdb_env_set_maxdbs(mdb_env, opts->maxdbs); + if (ret != MDB_SUCCESS) { + mdb_env_close(mdb_env); + return lmdb_error_to_knot(ret); + } + + ret = mdb_env_set_maxreaders(mdb_env, opts->maxreaders); + if (ret != MDB_SUCCESS) { + mdb_env_close(mdb_env); + return lmdb_error_to_knot(ret); + } + +#ifdef __OpenBSD__ + /* + * Enforce that MDB_WRITEMAP is set. + * + * MDB assumes a unified buffer cache. + * + * See http://www.openldap.org/pub/hyc/mdm-paper.pdf section 3.1, + * references 17, 18, and 19. + * + * From Howard Chu: "This requirement can be relaxed in the + * current version of the library. If you create the environment + * with the MDB_WRITEMAP option then all reads and writes are + * performed using mmap, so the file buffer cache is irrelevant. + * Of course then you lose the protection that the read-only + * map offers." + */ + opts->flags.env |= MDB_WRITEMAP; +#endif + + ret = mdb_env_open(mdb_env, opts->path, opts->flags.env, LMDB_FILE_MODE); + if (ret != MDB_SUCCESS) { + mdb_env_close(mdb_env); + return lmdb_error_to_knot(ret); + } + + /* Keep the environment pointer. */ + env->env = mdb_env; + + return KNOT_EOK; +} + +static int dbase_open(struct lmdb_env *env, struct knot_db_lmdb_opts *opts) +{ + unsigned flags = 0; + if (opts->flags.env & KNOT_DB_LMDB_RDONLY) { + flags = MDB_RDONLY; + } + + /* Open the database. */ + MDB_txn *txn = NULL; + int ret = mdb_txn_begin(env->env, NULL, flags, &txn); + if (ret != MDB_SUCCESS) { + mdb_env_close(env->env); + return lmdb_error_to_knot(ret); + } + + ret = mdb_dbi_open(txn, opts->dbname, opts->flags.db | MDB_CREATE, &env->dbi); + if (ret != MDB_SUCCESS) { + mdb_txn_abort(txn); + mdb_env_close(env->env); + return lmdb_error_to_knot(ret); + } + + ret = mdb_txn_commit(txn); + if (ret != MDB_SUCCESS) { + mdb_env_close(env->env); + return lmdb_error_to_knot(ret); + } + + return KNOT_EOK; +} + +static int init(knot_db_t **db_ptr, knot_mm_t *mm, void *arg) +{ + if (db_ptr == NULL || arg == NULL) { + return KNOT_EINVAL; + } + + struct lmdb_env *env = mm_alloc(mm, sizeof(struct lmdb_env)); + if (env == NULL) { + return KNOT_ENOMEM; + } + + memset(env, 0, sizeof(struct lmdb_env)); + env->pool = mm; + + /* Open new environment. */ + struct lmdb_env *old_env = *db_ptr; + if (old_env == NULL) { + int ret = dbase_open_env(env, (struct knot_db_lmdb_opts *)arg); + if (ret != KNOT_EOK) { + mm_free(mm, env); + return ret; + } + } else { + /* Shared environment, this instance just owns the DBI. */ + env->env = old_env->env; + env->shared = true; + } + + /* Open the database. */ + int ret = dbase_open(env, (struct knot_db_lmdb_opts *)arg); + if (ret != KNOT_EOK) { + mm_free(mm, env); + return ret; + } + + /* Store the new environment. */ + *db_ptr = env; + + return KNOT_EOK; +} + +static void deinit(knot_db_t *db) +{ + if (db) { + struct lmdb_env *env = db; + + dbase_close(env); + mm_free(env->pool, env); + } +} + +_public_ +int knot_db_lmdb_txn_begin(knot_db_t *db, knot_db_txn_t *txn, knot_db_txn_t *parent, + unsigned flags) +{ + txn->db = db; + txn->txn = NULL; + + unsigned txn_flags = 0; + if (flags & KNOT_DB_RDONLY) { + txn_flags |= MDB_RDONLY; + } + + MDB_txn *parent_txn = (parent != NULL) ? (MDB_txn *)parent->txn : NULL; + + struct lmdb_env *env = db; + int ret = mdb_txn_begin(env->env, parent_txn, txn_flags, (MDB_txn **)&txn->txn); + if (ret != MDB_SUCCESS) { + return lmdb_error_to_knot(ret); + } + + return KNOT_EOK; +} + +static int txn_begin(knot_db_t *db, knot_db_txn_t *txn, unsigned flags) +{ + return knot_db_lmdb_txn_begin(db, txn, NULL, flags); +} + +static int txn_commit(knot_db_txn_t *txn) +{ + int ret = mdb_txn_commit((MDB_txn *)txn->txn); + if (ret != MDB_SUCCESS) { + return lmdb_error_to_knot(ret); + } + + return KNOT_EOK; +} + +static void txn_abort(knot_db_txn_t *txn) +{ + mdb_txn_abort((MDB_txn *)txn->txn); +} + +static int count(knot_db_txn_t *txn) +{ + struct lmdb_env *env = txn->db; + + MDB_stat stat; + int ret = mdb_stat(txn->txn, env->dbi, &stat); + if (ret != MDB_SUCCESS) { + return lmdb_error_to_knot(ret); + } + + return stat.ms_entries; +} + +static int clear(knot_db_txn_t *txn) +{ + struct lmdb_env *env = txn->db; + + int ret = mdb_drop(txn->txn, env->dbi, 0); + if (ret != MDB_SUCCESS) { + return lmdb_error_to_knot(ret); + } + + return KNOT_EOK; +} + +static knot_db_iter_t *iter_set(knot_db_iter_t *iter, knot_db_val_t *key, unsigned flags) +{ + MDB_cursor *cursor = iter; + + MDB_cursor_op op = MDB_SET; + switch(flags) { + case KNOT_DB_NOOP: return cursor; + case KNOT_DB_FIRST: op = MDB_FIRST; break; + case KNOT_DB_LAST: op = MDB_LAST; break; + case KNOT_DB_NEXT: op = MDB_NEXT; break; + case KNOT_DB_PREV: op = MDB_PREV; break; + case KNOT_DB_LEQ: + case KNOT_DB_GEQ: op = MDB_SET_RANGE; break; + default: break; + } + + MDB_val db_key = { 0, NULL }; + if (key) { + db_key.mv_data = key->data; + db_key.mv_size = key->len; + } + MDB_val unused_key = { 0, NULL }, unused_val = { 0, NULL }; + + int ret = mdb_cursor_get(cursor, key ? &db_key : &unused_key, &unused_val, op); + + /* LEQ is not supported in LMDB, workaround using GEQ. */ + if (flags == KNOT_DB_LEQ && key) { + /* Searched key is after the last key. */ + if (ret != MDB_SUCCESS) { + return iter_set(iter, NULL, KNOT_DB_LAST); + } + /* If the searched key != matched, get previous. */ + if ((key->len != db_key.mv_size) || + (memcmp(key->data, db_key.mv_data, key->len) != 0)) { + return iter_set(iter, NULL, KNOT_DB_PREV); + } + } + + if (ret != MDB_SUCCESS) { + mdb_cursor_close(cursor); + return NULL; + } + + return cursor; +} + +static knot_db_iter_t *iter_begin(knot_db_txn_t *txn, unsigned flags) +{ + struct lmdb_env *env = txn->db; + MDB_cursor *cursor = NULL; + + int ret = mdb_cursor_open(txn->txn, env->dbi, &cursor); + if (ret != MDB_SUCCESS) { + return NULL; + } + + /* Clear sorted flag, as it's always sorted. */ + flags &= ~KNOT_DB_SORTED; + + return iter_set(cursor, NULL, (flags == 0) ? KNOT_DB_FIRST : flags); +} + +static knot_db_iter_t *iter_next(knot_db_iter_t *iter) +{ + return iter_set(iter, NULL, KNOT_DB_NEXT); +} + +_public_ +int knot_db_lmdb_iter_del(knot_db_iter_t *iter) +{ + MDB_cursor *cursor = iter; + + int ret = mdb_cursor_del(cursor, 0); + if (ret != MDB_SUCCESS) { + return lmdb_error_to_knot(ret); + } + + return KNOT_EOK; +} + +static int iter_key(knot_db_iter_t *iter, knot_db_val_t *key) +{ + MDB_cursor *cursor = iter; + + MDB_val mdb_key, mdb_val; + int ret = mdb_cursor_get(cursor, &mdb_key, &mdb_val, MDB_GET_CURRENT); + if (ret != MDB_SUCCESS) { + return lmdb_error_to_knot(ret); + } + + key->data = mdb_key.mv_data; + key->len = mdb_key.mv_size; + return KNOT_EOK; +} + +static int iter_val(knot_db_iter_t *iter, knot_db_val_t *val) +{ + MDB_cursor *cursor = iter; + + MDB_val mdb_key, mdb_val; + int ret = mdb_cursor_get(cursor, &mdb_key, &mdb_val, MDB_GET_CURRENT); + if (ret != MDB_SUCCESS) { + return lmdb_error_to_knot(ret); + } + + val->data = mdb_val.mv_data; + val->len = mdb_val.mv_size; + return KNOT_EOK; +} + +static void iter_finish(knot_db_iter_t *iter) +{ + if (iter == NULL) { + return; + } + + MDB_cursor *cursor = iter; + mdb_cursor_close(cursor); +} + +static int find(knot_db_txn_t *txn, knot_db_val_t *key, knot_db_val_t *val, unsigned flags) +{ + knot_db_iter_t *iter = iter_begin(txn, KNOT_DB_NOOP); + if (iter == NULL) { + return KNOT_ERROR; + } + + int ret = KNOT_EOK; + if (iter_set(iter, key, flags) == NULL) { + return KNOT_ENOENT; + } else { + ret = iter_val(iter, val); + } + + iter_finish(iter); + return ret; +} + +static int insert(knot_db_txn_t *txn, knot_db_val_t *key, knot_db_val_t *val, unsigned flags) +{ + struct lmdb_env *env = txn->db; + + MDB_val db_key = { key->len, key->data }; + MDB_val data = { val->len, val->data }; + + /* Reserve if only size is declared. */ + unsigned mdb_flags = 0; + if (val->len > 0 && val->data == NULL) { + mdb_flags |= MDB_RESERVE; + } + + int ret = mdb_put(txn->txn, env->dbi, &db_key, &data, mdb_flags); + if (ret != MDB_SUCCESS) { + return lmdb_error_to_knot(ret); + } + + /* Update the result. */ + val->data = data.mv_data; + val->len = data.mv_size; + + return KNOT_EOK; +} + +static int del(knot_db_txn_t *txn, knot_db_val_t *key) +{ + struct lmdb_env *env = txn->db; + MDB_val db_key = { key->len, key->data }; + MDB_val data = { 0, NULL }; + + int ret = mdb_del(txn->txn, env->dbi, &db_key, &data); + if (ret != MDB_SUCCESS) { + return lmdb_error_to_knot(ret); + } + + return KNOT_EOK; +} + +_public_ +int knot_db_lmdb_del_exact(knot_db_txn_t *txn, knot_db_val_t *key, knot_db_val_t *val) +{ + struct lmdb_env *env = txn->db; + MDB_val db_key = { key->len, key->data }; + MDB_val data = { val->len, val->data }; + + int ret = mdb_del(txn->txn, env->dbi, &db_key, &data); + if (ret != MDB_SUCCESS) { + return lmdb_error_to_knot(ret); + } + + return KNOT_EOK; +} + +_public_ +size_t knot_db_lmdb_get_mapsize(knot_db_t *db) +{ + struct lmdb_env *env = db; + MDB_envinfo info; + if (mdb_env_info(env->env, &info) != MDB_SUCCESS) { + return 0; + } + + return info.me_mapsize; +} + +// you should SUM all the usages of DBs sharing one mapsize +_public_ +size_t knot_db_lmdb_get_usage(knot_db_t *db) +{ + struct lmdb_env *env = db; + knot_db_txn_t txn; + knot_db_lmdb_txn_begin(db, &txn, NULL, KNOT_DB_RDONLY); + MDB_stat st; + if (mdb_stat(txn.txn, env->dbi, &st) != MDB_SUCCESS) { + txn_abort(&txn); + return 0; + } + txn_abort(&txn); + + size_t pgs_used = st.ms_branch_pages + st.ms_leaf_pages + st.ms_overflow_pages; + + return (pgs_used * st.ms_psize); +} + +_public_ +const knot_db_api_t *knot_db_lmdb_api(void) +{ + static const knot_db_api_t api = { + "lmdb", + init, deinit, + txn_begin, txn_commit, txn_abort, + count, clear, find, insert, del, + iter_begin, iter_set, iter_next, iter_key, iter_val, iter_finish + }; + + return &api; +} diff --git a/src/libknot/db/db_lmdb.h b/src/libknot/db/db_lmdb.h new file mode 100644 index 0000000..d0213cf --- /dev/null +++ b/src/libknot/db/db_lmdb.h @@ -0,0 +1,71 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +/*! + * \file + * + * \addtogroup db + * @{ + */ + +#pragma once + +#include "libknot/db/db.h" + +/* Defines. */ +#define KNOT_DB_LMDB_MAPSIZE (100 * 1024 * 1024) + +/* LMDB specific flags. */ +extern const unsigned KNOT_DB_LMDB_NOTLS; +extern const unsigned KNOT_DB_LMDB_RDONLY; +extern const unsigned KNOT_DB_LMDB_INTEGERKEY; +extern const unsigned KNOT_DB_LMDB_NOSYNC; +extern const unsigned KNOT_DB_LMDB_WRITEMAP; +extern const unsigned KNOT_DB_LMDB_MAPASYNC; +extern const unsigned KNOT_DB_LMDB_DUPSORT; + +/* Native options. */ +struct knot_db_lmdb_opts { + const char *path; /*!< Database environment path. */ + const char *dbname; /*!< Database name (or NULL). */ + size_t mapsize; /*!< Environment map size. */ + unsigned maxdbs; /*!< Maximum number of databases in the env. */ + unsigned maxreaders; /*!< Maximum number of concurrent readers */ + struct { + unsigned env; /*!< Environment flags. */ + unsigned db; /*!< Database flags. */ + } flags; +}; + +/* Default options. */ +#define KNOT_DB_LMDB_OPTS_INITIALIZER { \ + NULL, NULL, \ + KNOT_DB_LMDB_MAPSIZE, \ + 0, \ + 126, /* = contrib/lmdb/mdb.c DEFAULT_READERS */ \ + { 0, 0 } \ +} + +const knot_db_api_t *knot_db_lmdb_api(void); + +/* LMDB specific operations. */ +int knot_db_lmdb_del_exact(knot_db_txn_t *txn, knot_db_val_t *key, knot_db_val_t *val); +int knot_db_lmdb_txn_begin(knot_db_t *db, knot_db_txn_t *txn, knot_db_txn_t *parent, + unsigned flags); +int knot_db_lmdb_iter_del(knot_db_iter_t *iter); +size_t knot_db_lmdb_get_mapsize(knot_db_t *db); +size_t knot_db_lmdb_get_usage(knot_db_t *db); + +/*! @} */ diff --git a/src/libknot/db/db_trie.c b/src/libknot/db/db_trie.c new file mode 100644 index 0000000..d0fbb0c --- /dev/null +++ b/src/libknot/db/db_trie.c @@ -0,0 +1,178 @@ +/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <assert.h> + +#include "libknot/attribute.h" +#include "libknot/errcode.h" +#include "libknot/db/db_trie.h" +#include "contrib/qp-trie/trie.h" +#include "contrib/macros.h" +#include "contrib/mempattern.h" + +static int init(knot_db_t **db, knot_mm_t *mm, void *arg) +{ + if (db == NULL || arg == NULL) { + return KNOT_EINVAL; + } + + struct knot_db_trie_opts *opts = arg; + UNUSED(opts); + trie_t *trie = trie_create(mm); + if (!trie) { + return KNOT_ENOMEM; + } + + *db = trie; + + return KNOT_EOK; +} + +static void deinit(knot_db_t *db) +{ + trie_free((trie_t *)db); +} + +static int txn_begin(knot_db_t *db, knot_db_txn_t *txn, unsigned flags) +{ + txn->txn = (void *)(size_t)flags; + txn->db = db; + return KNOT_EOK; /* N/A */ +} + +static int txn_commit(knot_db_txn_t *txn) +{ + return KNOT_EOK; +} + +static void txn_abort(knot_db_txn_t *txn) +{ +} + +static int count(knot_db_txn_t *txn) +{ + return trie_weight((trie_t *)txn->db); +} + +static int clear(knot_db_txn_t *txn) +{ + trie_clear((trie_t *)txn->db); + + return KNOT_EOK; +} + +static int find(knot_db_txn_t *txn, knot_db_val_t *key, knot_db_val_t *val, unsigned flags) +{ + trie_val_t *ret = trie_get_try((trie_t *)txn->db, key->data, key->len); + if (ret == NULL) { + return KNOT_ENOENT; + } + + val->data = *ret; + val->len = sizeof(trie_val_t); /* Trie doesn't support storing length. */ + return KNOT_EOK; +} + +static int insert(knot_db_txn_t *txn, knot_db_val_t *key, knot_db_val_t *val, unsigned flags) +{ + /* No flags supported. */ + if (flags != 0) { + return KNOT_ENOTSUP; + } + + trie_val_t *ret = trie_get_ins((trie_t *)txn->db, key->data, key->len); + if (ret == NULL) { + return KNOT_ENOMEM; + } + + *ret = val->data; + return KNOT_EOK; +} + +static int del(knot_db_txn_t *txn, knot_db_val_t *key) +{ + return trie_del((trie_t *)txn->db, key->data, key->len, NULL); +} + +static knot_db_iter_t *iter_begin(knot_db_txn_t *txn, unsigned flags) +{ + flags &= ~KNOT_DB_SORTED; + + /* No operations other than begin are supported right now. */ + if (flags != 0) { + return NULL; + } + + return trie_it_begin((trie_t *)txn->db); +} + +static knot_db_iter_t *iter_seek(knot_db_iter_t *iter, knot_db_val_t *key, unsigned flags) +{ + assert(0); + return NULL; /* ENOTSUP */ +} + +static knot_db_iter_t *iter_next(knot_db_iter_t *iter) +{ + trie_it_next((trie_it_t *)iter); + if (trie_it_finished((trie_it_t *)iter)) { + trie_it_free((trie_it_t *)iter); + return NULL; + } + + return iter; +} + +static int iter_key(knot_db_iter_t *iter, knot_db_val_t *val) +{ + val->data = (void *)trie_it_key((trie_it_t *)iter, &val->len); + if (val->data == NULL) { + return KNOT_ENOENT; + } + + return KNOT_EOK; +} + +static int iter_val(knot_db_iter_t *iter, knot_db_val_t *val) +{ + trie_val_t *ret = trie_it_val((trie_it_t *)iter); + if (ret == NULL) { + return KNOT_ENOENT; + } + + val->data = *ret; + val->len = sizeof(trie_val_t); + return KNOT_EOK; +} + +static void iter_finish(knot_db_iter_t *iter) +{ + trie_it_free((trie_it_t *)iter); +} + +_public_ +const knot_db_api_t *knot_db_trie_api(void) +{ + static const knot_db_api_t api = { + "trie", + init, deinit, + txn_begin, txn_commit, txn_abort, + count, clear, find, insert, del, + iter_begin, iter_seek, iter_next, iter_key, iter_val, iter_finish + }; + + return &api; +} diff --git a/src/libknot/db/db_trie.h b/src/libknot/db/db_trie.h new file mode 100644 index 0000000..d2afdf8 --- /dev/null +++ b/src/libknot/db/db_trie.h @@ -0,0 +1,39 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +/*! + * \file + * + * \addtogroup db + * @{ + */ + +#pragma once + +#include "libknot/db/db.h" + +/* Native options. */ +struct knot_db_trie_opts { + unsigned unused; +}; + +/* Default options. */ +#define KNOT_DB_TRIE_OPTS_INITIALIZER { \ + 0 \ +} + +const knot_db_api_t *knot_db_trie_api(void); + +/*! @} */ diff --git a/src/libknot/descriptor.c b/src/libknot/descriptor.c new file mode 100644 index 0000000..fa3bbd6 --- /dev/null +++ b/src/libknot/descriptor.c @@ -0,0 +1,411 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> + +#include "libknot/attribute.h" +#include "libknot/descriptor.h" + +/*! + * \brief Table with DNS classes. + */ +static const char* dns_classes[] = { + [KNOT_CLASS_IN] = "IN", + [KNOT_CLASS_CH] = "CH", + [KNOT_CLASS_NONE] = "NONE", + [KNOT_CLASS_ANY] = "ANY" +}; + +/*! + * \brief RR type descriptors. + */ +static const knot_rdata_descriptor_t rdata_descriptors[] = { + [0] = { { KNOT_RDATA_WF_REMAINDER, + KNOT_RDATA_WF_END }, NULL }, + [KNOT_RRTYPE_A] = { { 4, KNOT_RDATA_WF_END }, "A" }, + [KNOT_RRTYPE_NS] = { { KNOT_RDATA_WF_COMPRESSIBLE_DNAME, + KNOT_RDATA_WF_END }, "NS" }, + [KNOT_RRTYPE_CNAME] = { { KNOT_RDATA_WF_COMPRESSIBLE_DNAME, + KNOT_RDATA_WF_END }, "CNAME" }, + [KNOT_RRTYPE_SOA] = { { KNOT_RDATA_WF_COMPRESSIBLE_DNAME, + KNOT_RDATA_WF_COMPRESSIBLE_DNAME, + 20, KNOT_RDATA_WF_END }, "SOA" }, + [KNOT_RRTYPE_NULL] = { { KNOT_RDATA_WF_REMAINDER, + KNOT_RDATA_WF_END }, "NULL" }, + [KNOT_RRTYPE_PTR] = { { KNOT_RDATA_WF_COMPRESSIBLE_DNAME, + KNOT_RDATA_WF_END }, "PTR" }, + [KNOT_RRTYPE_HINFO] = { { KNOT_RDATA_WF_REMAINDER, + KNOT_RDATA_WF_END }, "HINFO" }, + [KNOT_RRTYPE_MINFO] = { { KNOT_RDATA_WF_COMPRESSIBLE_DNAME, + KNOT_RDATA_WF_COMPRESSIBLE_DNAME, + KNOT_RDATA_WF_END }, "MINFO" }, + [KNOT_RRTYPE_MX] = { { 2, KNOT_RDATA_WF_COMPRESSIBLE_DNAME, + KNOT_RDATA_WF_END }, "MX" }, + [KNOT_RRTYPE_TXT] = { { KNOT_RDATA_WF_REMAINDER, + KNOT_RDATA_WF_END }, "TXT" }, + [KNOT_RRTYPE_RP] = { { KNOT_RDATA_WF_DECOMPRESSIBLE_DNAME, + KNOT_RDATA_WF_DECOMPRESSIBLE_DNAME, + KNOT_RDATA_WF_END }, "RP" }, + [KNOT_RRTYPE_AFSDB] = { { 2, KNOT_RDATA_WF_DECOMPRESSIBLE_DNAME, + KNOT_RDATA_WF_END }, "AFSDB" }, + [KNOT_RRTYPE_RT] = { { 2, KNOT_RDATA_WF_DECOMPRESSIBLE_DNAME, + KNOT_RDATA_WF_END }, "RT" }, + [KNOT_RRTYPE_SIG] = { { 18, KNOT_RDATA_WF_DECOMPRESSIBLE_DNAME, + KNOT_RDATA_WF_REMAINDER, + KNOT_RDATA_WF_END }, "SIG" }, + [KNOT_RRTYPE_KEY] = { { KNOT_RDATA_WF_REMAINDER, + KNOT_RDATA_WF_END }, "KEY" }, + [KNOT_RRTYPE_AAAA] = { { 16, KNOT_RDATA_WF_END }, "AAAA" }, + [KNOT_RRTYPE_LOC] = { { 16, KNOT_RDATA_WF_END }, "LOC" }, + [KNOT_RRTYPE_SRV] = { { 6, KNOT_RDATA_WF_DECOMPRESSIBLE_DNAME, + KNOT_RDATA_WF_END }, "SRV" }, + [KNOT_RRTYPE_NAPTR] = { { KNOT_RDATA_WF_NAPTR_HEADER, + KNOT_RDATA_WF_DECOMPRESSIBLE_DNAME, + KNOT_RDATA_WF_END }, "NAPTR" }, + [KNOT_RRTYPE_KX] = { { 2, KNOT_RDATA_WF_FIXED_DNAME, + KNOT_RDATA_WF_END }, "KX" }, + [KNOT_RRTYPE_CERT] = { { KNOT_RDATA_WF_REMAINDER, + KNOT_RDATA_WF_END }, "CERT" }, + [KNOT_RRTYPE_DNAME] = { { KNOT_RDATA_WF_FIXED_DNAME, + KNOT_RDATA_WF_END }, "DNAME" }, + [KNOT_RRTYPE_OPT] = { { KNOT_RDATA_WF_REMAINDER, + KNOT_RDATA_WF_END }, "OPT" }, + [KNOT_RRTYPE_APL] = { { KNOT_RDATA_WF_REMAINDER, + KNOT_RDATA_WF_END }, "APL" }, + [KNOT_RRTYPE_DS] = { { KNOT_RDATA_WF_REMAINDER, + KNOT_RDATA_WF_END }, "DS" }, + [KNOT_RRTYPE_SSHFP] = { { KNOT_RDATA_WF_REMAINDER, + KNOT_RDATA_WF_END }, "SSHFP" }, + [KNOT_RRTYPE_IPSECKEY] = { { KNOT_RDATA_WF_REMAINDER, + KNOT_RDATA_WF_END }, "IPSECKEY" }, + [KNOT_RRTYPE_RRSIG] = { { 18, KNOT_RDATA_WF_FIXED_DNAME, + KNOT_RDATA_WF_REMAINDER, + KNOT_RDATA_WF_END }, "RRSIG" }, + [KNOT_RRTYPE_NSEC] = { { KNOT_RDATA_WF_FIXED_DNAME, + KNOT_RDATA_WF_REMAINDER, + KNOT_RDATA_WF_END }, "NSEC" }, + [KNOT_RRTYPE_DNSKEY] = { { KNOT_RDATA_WF_REMAINDER, + KNOT_RDATA_WF_END }, "DNSKEY" }, + [KNOT_RRTYPE_DHCID] = { { KNOT_RDATA_WF_REMAINDER, + KNOT_RDATA_WF_END }, "DHCID" }, + [KNOT_RRTYPE_NSEC3] = { { KNOT_RDATA_WF_REMAINDER, + KNOT_RDATA_WF_END }, "NSEC3" }, + [KNOT_RRTYPE_NSEC3PARAM] = { { KNOT_RDATA_WF_REMAINDER, + KNOT_RDATA_WF_END }, "NSEC3PARAM" }, + [KNOT_RRTYPE_TLSA] = { { KNOT_RDATA_WF_REMAINDER, + KNOT_RDATA_WF_END }, "TLSA" }, + [KNOT_RRTYPE_CDS] = { { KNOT_RDATA_WF_REMAINDER, + KNOT_RDATA_WF_END }, "CDS" }, + [KNOT_RRTYPE_CDNSKEY] = { { KNOT_RDATA_WF_REMAINDER, + KNOT_RDATA_WF_END }, "CDNSKEY" }, + [KNOT_RRTYPE_SPF] = { { KNOT_RDATA_WF_REMAINDER, + KNOT_RDATA_WF_END }, "SPF" }, + [KNOT_RRTYPE_NID] = { { 10, KNOT_RDATA_WF_END }, "NID" }, + [KNOT_RRTYPE_L32] = { { 6, KNOT_RDATA_WF_END }, "L32" }, + [KNOT_RRTYPE_L64] = { { 10, KNOT_RDATA_WF_END }, "L64" }, + [KNOT_RRTYPE_LP] = { { 2, KNOT_RDATA_WF_FIXED_DNAME, + KNOT_RDATA_WF_END }, "LP" }, + [KNOT_RRTYPE_EUI48] = { { 6, KNOT_RDATA_WF_END }, "EUI48" }, + [KNOT_RRTYPE_EUI64] = { { 8, KNOT_RDATA_WF_END }, "EUI64" }, + [KNOT_RRTYPE_TKEY] = { { KNOT_RDATA_WF_FIXED_DNAME, + KNOT_RDATA_WF_REMAINDER, + KNOT_RDATA_WF_END }, "TKEY" }, + [KNOT_RRTYPE_TSIG] = { { KNOT_RDATA_WF_FIXED_DNAME, + KNOT_RDATA_WF_REMAINDER, + KNOT_RDATA_WF_END }, "TSIG" }, + [KNOT_RRTYPE_IXFR] = { { KNOT_RDATA_WF_REMAINDER, + KNOT_RDATA_WF_END }, "IXFR" }, + [KNOT_RRTYPE_AXFR] = { { KNOT_RDATA_WF_REMAINDER, + KNOT_RDATA_WF_END }, "AXFR" }, + [KNOT_RRTYPE_ANY] = { { KNOT_RDATA_WF_REMAINDER, + KNOT_RDATA_WF_END }, "ANY" }, + [KNOT_RRTYPE_URI] = { { KNOT_RDATA_WF_REMAINDER, + KNOT_RDATA_WF_END }, "URI" }, + [KNOT_RRTYPE_CAA] = { { KNOT_RDATA_WF_REMAINDER, + KNOT_RDATA_WF_END }, "CAA" }, +}; + +#define MAX_RRTYPE sizeof(rdata_descriptors) / sizeof(knot_rdata_descriptor_t) - 1 + +/*! + * \brief Some (OBSOLETE) RR type descriptors. + */ +static const knot_rdata_descriptor_t obsolete_rdata_descriptors[] = { + [0] = { { KNOT_RDATA_WF_REMAINDER, + KNOT_RDATA_WF_END }, NULL }, + [KNOT_RRTYPE_MD] = { { KNOT_RDATA_WF_DECOMPRESSIBLE_DNAME, + KNOT_RDATA_WF_END }, "MD" }, + [KNOT_RRTYPE_MF] = { { KNOT_RDATA_WF_DECOMPRESSIBLE_DNAME, + KNOT_RDATA_WF_END }, "MF" }, + [KNOT_RRTYPE_MB] = { { KNOT_RDATA_WF_DECOMPRESSIBLE_DNAME, + KNOT_RDATA_WF_END }, "MB" }, + [KNOT_RRTYPE_MG] = { { KNOT_RDATA_WF_DECOMPRESSIBLE_DNAME, + KNOT_RDATA_WF_END }, "MG" }, + [KNOT_RRTYPE_MR] = { { KNOT_RDATA_WF_DECOMPRESSIBLE_DNAME, + KNOT_RDATA_WF_END }, "MR" }, + [KNOT_RRTYPE_PX] = { { 2, KNOT_RDATA_WF_DECOMPRESSIBLE_DNAME, + KNOT_RDATA_WF_DECOMPRESSIBLE_DNAME, + KNOT_RDATA_WF_END }, "PX" }, + [KNOT_RRTYPE_NXT] = { { KNOT_RDATA_WF_DECOMPRESSIBLE_DNAME, + KNOT_RDATA_WF_REMAINDER, + KNOT_RDATA_WF_END }, "NXT" }, +}; + +_public_ +const knot_rdata_descriptor_t *knot_get_rdata_descriptor(const uint16_t type) +{ + if (type <= MAX_RRTYPE && rdata_descriptors[type].type_name != NULL) { + return &rdata_descriptors[type]; + } else { + return &rdata_descriptors[0]; + } +} + +_public_ +const knot_rdata_descriptor_t *knot_get_obsolete_rdata_descriptor(const uint16_t type) +{ + if (type <= KNOT_RRTYPE_NXT && + obsolete_rdata_descriptors[type].type_name != NULL) { + return &obsolete_rdata_descriptors[type]; + } else { + return &obsolete_rdata_descriptors[0]; + } +} + +_public_ +int knot_rrtype_to_string(const uint16_t rrtype, + char *out, + const size_t out_len) +{ + if (out == NULL) { + return -1; + } + + int ret; + + const knot_rdata_descriptor_t *descr = knot_get_rdata_descriptor(rrtype); + + if (descr->type_name != NULL) { + ret = snprintf(out, out_len, "%s", descr->type_name); + } else { + ret = snprintf(out, out_len, "TYPE%u", rrtype); + } + + if (ret <= 0 || (size_t)ret >= out_len) { + return -1; + } else { + return ret; + } +} + +_public_ +int knot_rrtype_from_string(const char *name, uint16_t *num) +{ + if (name == NULL || num == NULL) { + return -1; + } + + int i; + char *end; + unsigned long n; + + // Try to find name in descriptors table. + for (i = 0; i <= MAX_RRTYPE; i++) { + if (rdata_descriptors[i].type_name != NULL && + strcasecmp(rdata_descriptors[i].type_name, name) == 0) { + *num = i; + return 0; + } + } + + // Type name must begin with TYPE. + if (strncasecmp(name, "TYPE", 4) != 0) { + return -1; + } else { + name += 4; + } + + // The rest must be a number. + n = strtoul(name, &end, 10); + if (end == name || *end != '\0' || n > UINT16_MAX) { + return -1; + } + + *num = n; + return 0; +} + +_public_ +int knot_rrclass_to_string(const uint16_t rrclass, + char *out, + const size_t out_len) +{ + if (out == NULL) { + return -1; + } + + int ret; + + if (rrclass <= KNOT_CLASS_ANY && dns_classes[rrclass] != NULL) { + ret = snprintf(out, out_len, "%s", dns_classes[rrclass]); + } else { + ret = snprintf(out, out_len, "CLASS%u", rrclass); + } + + if (ret <= 0 || (size_t)ret >= out_len) { + return -1; + } else { + return ret; + } +} + +_public_ +int knot_rrclass_from_string(const char *name, uint16_t *num) +{ + if (name == NULL || num == NULL) { + return -1; + } + + int i; + char *end; + unsigned long n; + + // Try to find the name in classes table. + for (i = 0; i <= KNOT_CLASS_ANY; i++) { + if (dns_classes[i] != NULL && + strcasecmp(dns_classes[i], name) == 0) { + *num = i; + return 0; + } + } + + // Class name must begin with CLASS. + if (strncasecmp(name, "CLASS", 5) != 0) { + return -1; + } else { + name += 5; + } + + // The rest must be a number. + n = strtoul(name, &end, 10); + if (end == name || *end != '\0' || n > UINT16_MAX) { + return -1; + } + + *num = n; + return 0; +} + +_public_ +int knot_rrtype_is_metatype(const uint16_t type) +{ + return type == KNOT_RRTYPE_SIG || + type == KNOT_RRTYPE_OPT || + type == KNOT_RRTYPE_TKEY || + type == KNOT_RRTYPE_TSIG || + type == KNOT_RRTYPE_IXFR || + type == KNOT_RRTYPE_AXFR || + type == KNOT_RRTYPE_ANY; +} + +_public_ +int knot_rrtype_is_dnssec(const uint16_t type) +{ + return type == KNOT_RRTYPE_DNSKEY || + type == KNOT_RRTYPE_RRSIG || + type == KNOT_RRTYPE_NSEC || + type == KNOT_RRTYPE_NSEC3 || + type == KNOT_RRTYPE_NSEC3PARAM || + type == KNOT_RRTYPE_CDNSKEY || + type == KNOT_RRTYPE_CDS; +} + +_public_ +int knot_rrtype_additional_needed(const uint16_t type) +{ + return type == KNOT_RRTYPE_NS || + type == KNOT_RRTYPE_MX || + type == KNOT_RRTYPE_SRV; +} + +_public_ +bool knot_rrtype_should_be_lowercased(const uint16_t type) +{ + return type == KNOT_RRTYPE_NS || + type == KNOT_RRTYPE_MD || + type == KNOT_RRTYPE_MF || + type == KNOT_RRTYPE_CNAME || + type == KNOT_RRTYPE_SOA || + type == KNOT_RRTYPE_MB || + type == KNOT_RRTYPE_MG || + type == KNOT_RRTYPE_MR || + type == KNOT_RRTYPE_PTR || + type == KNOT_RRTYPE_MINFO || + type == KNOT_RRTYPE_MX || + type == KNOT_RRTYPE_RP || + type == KNOT_RRTYPE_AFSDB || + type == KNOT_RRTYPE_RT || + type == KNOT_RRTYPE_SIG || + type == KNOT_RRTYPE_PX || + type == KNOT_RRTYPE_NXT || + type == KNOT_RRTYPE_NAPTR || + type == KNOT_RRTYPE_KX || + type == KNOT_RRTYPE_SRV || + type == KNOT_RRTYPE_DNAME || + type == KNOT_RRTYPE_RRSIG; +} + +_public_ +int knot_opt_code_to_string(const uint16_t code, char *out, const size_t out_len) +{ + if (out == NULL) { + return -1; + } + + const char *name = NULL; + + switch (code) { + case 1: name = "LLQ"; break; + case 2: name = "UL"; break; + case 3: name = "NSID"; break; + case 5: name = "DAU"; break; + case 6: name = "DHU"; break; + case 7: name = "N3U"; break; + case 8: name = "EDNS-CLIENT-SUBNET"; break; + case 9: name = "EDNS-EXPIRE"; break; + case 10: name = "COOKIE"; break; + case 11: name = "EDNS-TCP-KEEPALIVE"; break; + case 12: name = "PADDING"; break; + case 13: name = "CHAIN"; break; + case 14: name = "EDNS-KEY-TAG"; break; + } + + int ret; + + if (name != NULL) { + ret = snprintf(out, out_len, "%s", name); + } else { + ret = snprintf(out, out_len, "CODE%u", code); + } + + if (ret <= 0 || (size_t)ret >= out_len) { + return -1; + } else { + return ret; + } +} diff --git a/src/libknot/descriptor.h b/src/libknot/descriptor.h new file mode 100644 index 0000000..b91a7a9 --- /dev/null +++ b/src/libknot/descriptor.h @@ -0,0 +1,298 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \file + * + * \addtogroup rr + * @{ + */ + +#pragma once + +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> + +#define KNOT_MAX_RDATA_BLOCKS 8 +#define KNOT_MAX_RDATA_DNAMES 2 // Update this when defining new RR types! + +/*! + * \brief Resource record class codes. + * + * http://www.iana.org/assignments/dns-parameters/dns-parameters.xml + */ +enum knot_rr_class { + KNOT_CLASS_IN = 1, + KNOT_CLASS_CH = 3, + KNOT_CLASS_NONE = 254, + KNOT_CLASS_ANY = 255 +}; + +/*! + * \brief Resource record type constants. + * + * References: + * http://www.iana.org/assignments/dns-parameters/dns-parameters.xml + * RFC 3597#4 + * + * METATYPE: Contains DNS data that can't be in a zone file. + * QTYPE: Specifies DNS query type; can't be in a zone file. + */ +enum knot_rr_type { + KNOT_RRTYPE_A = 1, /*!< An IPv4 host address. */ + KNOT_RRTYPE_NS = 2, /*!< An authoritative name server. */ + + KNOT_RRTYPE_CNAME = 5, /*!< The canonical name for an alias. */ + KNOT_RRTYPE_SOA = 6, /*!< The start of a zone of authority. */ + + KNOT_RRTYPE_NULL = 10, /*!< METATYPE. Used in RFC 8145. */ + + KNOT_RRTYPE_PTR = 12, /*!< A domain name pointer. */ + KNOT_RRTYPE_HINFO = 13, /*!< A host information. */ + KNOT_RRTYPE_MINFO = 14, /*!< A mailbox information. */ + KNOT_RRTYPE_MX = 15, /*!< Mail exchange. */ + KNOT_RRTYPE_TXT = 16, /*!< Text strings. */ + KNOT_RRTYPE_RP = 17, /*!< For responsible person. */ + KNOT_RRTYPE_AFSDB = 18, /*!< For AFS Data Base location. */ + + KNOT_RRTYPE_RT = 21, /*!< For route through. */ + + KNOT_RRTYPE_SIG = 24, /*!< METATYPE. Transaction signature. */ + KNOT_RRTYPE_KEY = 25, /*!< For security key. */ + + KNOT_RRTYPE_AAAA = 28, /*!< IPv6 address. */ + KNOT_RRTYPE_LOC = 29, /*!< Location information. */ + + KNOT_RRTYPE_SRV = 33, /*!< Server selection. */ + + KNOT_RRTYPE_NAPTR = 35, /*!< Naming authority pointer . */ + KNOT_RRTYPE_KX = 36, /*!< Key exchanger. */ + KNOT_RRTYPE_CERT = 37, /*!< Certificate record. */ + + KNOT_RRTYPE_DNAME = 39, /*!< Delegation name. */ + + KNOT_RRTYPE_OPT = 41, /*!< METATYPE. Option for EDNS. */ + KNOT_RRTYPE_APL = 42, /*!< Address prefix list. */ + KNOT_RRTYPE_DS = 43, /*!< Delegation signer. */ + KNOT_RRTYPE_SSHFP = 44, /*!< SSH public key fingerprint. */ + KNOT_RRTYPE_IPSECKEY = 45, /*!< IPSEC key. */ + KNOT_RRTYPE_RRSIG = 46, /*!< DNSSEC signature. */ + KNOT_RRTYPE_NSEC = 47, /*!< Next-secure record. */ + KNOT_RRTYPE_DNSKEY = 48, /*!< DNS key. */ + KNOT_RRTYPE_DHCID = 49, /*!< DHCP identifier. */ + KNOT_RRTYPE_NSEC3 = 50, /*!< NSEC version 3. */ + KNOT_RRTYPE_NSEC3PARAM = 51, /*!< NSEC3 parameters. */ + KNOT_RRTYPE_TLSA = 52, /*!< DANE record. */ + + KNOT_RRTYPE_CDS = 59, /*!< Child delegation signer. */ + KNOT_RRTYPE_CDNSKEY = 60, /*!< Child DNS key. */ + + KNOT_RRTYPE_SPF = 99, /*!< Sender policy framework. */ + + KNOT_RRTYPE_NID = 104, /*!< Node identifier. */ + KNOT_RRTYPE_L32 = 105, /*!< 32-bit network locator. */ + KNOT_RRTYPE_L64 = 106, /*!< 64-bit network locator. */ + KNOT_RRTYPE_LP = 107, /*!< Subnetwork name. */ + KNOT_RRTYPE_EUI48 = 108, /*!< 48-bit extended unique identifier. */ + KNOT_RRTYPE_EUI64 = 109, /*!< 64-bit extended unique identifier. */ + + KNOT_RRTYPE_TKEY = 249, /*!< METATYPE. Transaction key. */ + KNOT_RRTYPE_TSIG = 250, /*!< METATYPE. Transaction signature. */ + KNOT_RRTYPE_IXFR = 251, /*!< QTYPE. Incremental zone transfer. */ + KNOT_RRTYPE_AXFR = 252, /*!< QTYPE. Authoritative zone transfer. */ + + KNOT_RRTYPE_ANY = 255, /*!< QTYPE. Any record. */ + KNOT_RRTYPE_URI = 256, /*!< Uniform resource identifier. */ + KNOT_RRTYPE_CAA = 257, /*!< Certification authority restriction. */ +}; + +/*! + * \brief Some (OBSOLETE) resource record type constants. + * + * References: + * http://www.iana.org/assignments/dns-parameters/dns-parameters.xml + * RFC 3597#4 + * + * \note These records can contain compressed domain name in rdata so + * it is important to know the position of them during transfers. + */ +enum knot_obsolete_rr_type { + KNOT_RRTYPE_MD = 3, + KNOT_RRTYPE_MF = 4, + KNOT_RRTYPE_MB = 7, + KNOT_RRTYPE_MG = 8, + KNOT_RRTYPE_MR = 9, + KNOT_RRTYPE_PX = 26, + KNOT_RRTYPE_NXT = 30 +}; + +/*! + * \brief Constants characterising the wire format of RDATA items. + */ +enum knot_rdata_wireformat { + /*!< Dname must not be compressed. */ + KNOT_RDATA_WF_FIXED_DNAME = -10, + /*!< Dname can be both compressed and decompressed. */ + KNOT_RDATA_WF_COMPRESSIBLE_DNAME, + /*!< Dname can be decompressed. */ + KNOT_RDATA_WF_DECOMPRESSIBLE_DNAME, + /*!< Initial part of NAPTR record before dname. */ + KNOT_RDATA_WF_NAPTR_HEADER, + /*!< Final part of a record. */ + KNOT_RDATA_WF_REMAINDER, + /*!< The last descriptor in array. */ + KNOT_RDATA_WF_END = 0 +}; + +/*! + * \brief Structure describing rdata. + */ +typedef struct { + /*!< Item types describing rdata. */ + const int block_types[KNOT_MAX_RDATA_BLOCKS]; + /*!< RR type name. */ + const char *type_name; +} knot_rdata_descriptor_t; + +/*! + * \brief Gets rdata descriptor for given RR name. + * + * \param type Mnemonic of RR type whose descriptor should be returned. + * + * \retval RR descriptor for given name, NULL descriptor if + * unknown type. + */ +const knot_rdata_descriptor_t *knot_get_rdata_descriptor(const uint16_t type); + +/*! + * \brief Gets rdata descriptor for given RR name (obsolete version). + * + * \param type Mnemonic of RR type whose descriptor should be returned. + * + * \retval RR descriptor for given name, NULL descriptor if + * unknown type. + */ +const knot_rdata_descriptor_t *knot_get_obsolete_rdata_descriptor(const uint16_t type); + +/*! + * \brief Converts numeric type representation to mnemonic string. + * + * \param rrtype Type RR type code to be converted. + * \param out Output buffer. + * \param out_len Length of the output buffer. + * + * \retval Length of output string. + * \retval -1 if error. + */ +int knot_rrtype_to_string(const uint16_t rrtype, + char *out, + const size_t out_len); + +/*! + * \brief Converts mnemonic string representation of a type to numeric one. + * + * \param name Mnemonic string to be converted. + * \param num Output variable. + * + * \retval 0 if OK. + * \retval -1 if error. + */ +int knot_rrtype_from_string(const char *name, uint16_t *num); + +/*! + * \brief Converts numeric class representation to the string one. + * + * \param rrclass Class code to be converted. + * \param out Output buffer. + * \param out_len Length of the output buffer. + * + * \retval Length of output string. + * \retval -1 if error. + */ +int knot_rrclass_to_string(const uint16_t rrclass, + char *out, + const size_t out_len); + +/*! + * \brief Converts string representation of a class to numeric one. + * + * \param name Mnemonic string to be converted. + * \param num Output variable. + * + * \retval 0 if OK. + * \retval -1 if error. + */ +int knot_rrclass_from_string(const char *name, uint16_t *num); + +/*! + * \brief Checks if given item is one of metatypes or qtypes. + * + * \param type Item value. + * + * \retval > 0 if YES. + * \retval 0 if NO. + */ +int knot_rrtype_is_metatype(const uint16_t type); + +/*! + * \brief Checks if given item is one of the DNSSEC types. + * + * \param type Item value. + * + * \retval > 0 if YES. + * \retval 0 if NO. + */ +int knot_rrtype_is_dnssec(const uint16_t type); + +/*! + * \brief Checks whether the given type requires additional processing. + * + * Only MX, NS and SRV types require additional processing. + * + * \param type Type to check. + * + * \retval <> 0 if additional processing is needed for \a qtype. + * \retval 0 otherwise. + */ +int knot_rrtype_additional_needed(const uint16_t type); + +/*! + * \brief Checks whether the RDATA domain names should be lowercased in + * canonical format of RRSet of the given type. + * + * Types that should be lowercased are accorrding to RFC 4034, Section 6.2, + * except for NSEC (updated by RFC 6840, Section 5.1) and A6 (not supported). + * + * \param type RRSet type to check. + * + * \retval true If RDATA dnames for type should be lowercased in canonical format. + * \retval false Otherwise. + */ +bool knot_rrtype_should_be_lowercased(const uint16_t type); + +/*! + * \brief Translates option code to string. + * + * \param code Code of the option to translate. + * \param out Buffer for the output string. + * \param out_len The available size of the buffer. + * + * \retval Length of output string. + * \retval -1 if error. + */ +int knot_opt_code_to_string(const uint16_t code, char *out, const size_t out_len); + +/*! @} */ diff --git a/src/libknot/dname.c b/src/libknot/dname.c new file mode 100644 index 0000000..1a2790e --- /dev/null +++ b/src/libknot/dname.c @@ -0,0 +1,755 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "libknot/attribute.h" +#include "libknot/consts.h" +#include "libknot/dname.h" +#include "libknot/errcode.h" +#include "libknot/packet/wire.h" +#include "contrib/ctype.h" +#include "contrib/mempattern.h" +#include "contrib/tolower.h" + +static int label_is_equal(const uint8_t *lb1, const uint8_t *lb2) +{ + return (*lb1 == *lb2) && memcmp(lb1 + 1, lb2 + 1, *lb1) == 0; +} + +/*! + * \brief Align name end-to-end and return number of common suffix labels. + * + * \param d1 Domain name1. + * \param d1_labels Number of labels in d1. + * \param d2 Domain name2. + * \param d2_labels Number of labels in d2. + */ +static int dname_align(const uint8_t **d1, uint8_t d1_labels, + const uint8_t **d2, uint8_t d2_labels) +{ + assert(d1 && d2); + + for (unsigned j = d1_labels; j < d2_labels; ++j) { + *d2 = knot_wire_next_label(*d2, NULL); + } + + for (unsigned j = d2_labels; j < d1_labels; ++j) { + *d1 = knot_wire_next_label(*d1, NULL); + } + + return (d1_labels < d2_labels) ? d1_labels : d2_labels; +} + +_public_ +int knot_dname_wire_check(const uint8_t *name, const uint8_t *endp, + const uint8_t *pkt) +{ + if (name == NULL || name == endp) { + return KNOT_EINVAL; + } + + int wire_len = 0; + int name_len = 1; /* Keep \x00 terminal label in advance. */ + bool is_compressed = false; + + while (*name != '\0') { + /* Check bounds (must have at least 2 octets remaining). */ + if (name + 2 > endp) { + return KNOT_EMALF; + } + + if (knot_wire_is_pointer(name)) { + /* Check that the pointer points backwards + * otherwise it could result in infinite loop + */ + if (pkt == NULL) { + return KNOT_EINVAL; + } + uint16_t ptr = knot_wire_get_pointer(name); + if (ptr >= (name - pkt)) { + return KNOT_EMALF; + } + + name = pkt + ptr; /* Hop to compressed label */ + if (!is_compressed) { /* Measure compressed size only */ + wire_len += sizeof(uint16_t); + is_compressed = true; + } + } else { + /* Check label length. */ + if (*name > KNOT_DNAME_MAXLABELLEN) { + return KNOT_EMALF; + } + /* Check if there's enough space. */ + int lblen = *name + 1; + if (name_len + lblen > KNOT_DNAME_MAXLEN) { + return KNOT_EMALF; + } + /* Update wire size only for noncompressed part. */ + name_len += lblen; + if (!is_compressed) { + wire_len += lblen; + } + /* Hop to next label. */ + name += lblen; + } + + /* Check bounds (must have at least 1 octet). */ + if (name + 1 > endp) { + return KNOT_EMALF; + } + } + + if (!is_compressed) { /* Terminal label. */ + wire_len += 1; + } + + return wire_len; +} + +_public_ +size_t knot_dname_store(knot_dname_storage_t dst, const knot_dname_t *name) +{ + if (dst == NULL || name == NULL) { + return 0; + } + + size_t len = knot_dname_size(name); + assert(len <= KNOT_DNAME_MAXLEN); + memcpy(dst, name, len); + + return len; +} + +_public_ +knot_dname_t *knot_dname_copy(const knot_dname_t *name, knot_mm_t *mm) +{ + if (name == NULL) { + return NULL; + } + + size_t len = knot_dname_size(name); + knot_dname_t *dst = mm_alloc(mm, len); + if (dst == NULL) { + return NULL; + } + memcpy(dst, name, len); + + return dst; +} + +_public_ +int knot_dname_to_wire(uint8_t *dst, const knot_dname_t *src, size_t maxlen) +{ + if (dst == NULL || src == NULL) { + return KNOT_EINVAL; + } + + size_t len = knot_dname_size(src); + if (len > maxlen) { + return KNOT_ESPACE; + } + memcpy(dst, src, len); + + return len; +} + +_public_ +int knot_dname_unpack(uint8_t *dst, const knot_dname_t *src, + size_t maxlen, const uint8_t *pkt) +{ + if (dst == NULL || src == NULL) { + return KNOT_EINVAL; + } + + /* Seek first real label occurrence. */ + src = knot_wire_seek_label(src, pkt); + + /* Unpack rest of the labels. */ + int len = 0; + while (*src != '\0') { + uint8_t lblen = *src + 1; + if (len + lblen > maxlen) { + return KNOT_ESPACE; + } + memcpy(dst + len, src, lblen); + len += lblen; + src = knot_wire_next_label(src, pkt); + } + + /* Terminal label */ + if (len + 1 > maxlen) { + return KNOT_EINVAL; + } + + *(dst + len) = '\0'; + return len + 1; +} + +_public_ +char *knot_dname_to_str(char *dst, const knot_dname_t *name, size_t maxlen) +{ + if (name == NULL) { + return NULL; + } + + size_t dname_size = knot_dname_size(name); + + /* Check the size for len(dname) + 1 char termination. */ + size_t alloc_size = (dst == NULL) ? dname_size + 1 : maxlen; + if (alloc_size < dname_size + 1) { + return NULL; + } + + char *res = (dst == NULL) ? malloc(alloc_size) : dst; + if (res == NULL) { + return NULL; + } + + uint8_t label_len = 0; + size_t str_len = 0; + + for (unsigned i = 0; i < dname_size; i++) { + uint8_t c = name[i]; + + /* Read next label size. */ + if (label_len == 0) { + label_len = c; + + /* Write label separation. */ + if (str_len > 0 || dname_size == 1) { + if (alloc_size <= str_len + 1) { + goto dname_to_str_failed; + } + res[str_len++] = '.'; + } + + continue; + } + + if (is_alnum(c) || c == '-' || c == '_' || c == '*' || + c == '/') { + if (alloc_size <= str_len + 1) { + goto dname_to_str_failed; + } + res[str_len++] = c; + } else if (is_punct(c) && c != '#') { + /* Exclusion of '#' character is to avoid possible + * collision with rdata hex notation '\#'. So it is + * encoded in \ddd notation. + */ + + if (dst != NULL) { + if (maxlen <= str_len + 2) { + goto dname_to_str_failed; + } + } else { + /* Extend output buffer for \x format. */ + alloc_size += 1; + char *extended = realloc(res, alloc_size); + if (extended == NULL) { + goto dname_to_str_failed; + } + res = extended; + } + + /* Write encoded character. */ + res[str_len++] = '\\'; + res[str_len++] = c; + } else { + if (dst != NULL) { + if (maxlen <= str_len + 4) { + goto dname_to_str_failed; + } + } else { + /* Extend output buffer for \DDD format. */ + alloc_size += 3; + char *extended = realloc(res, alloc_size); + if (extended == NULL) { + goto dname_to_str_failed; + } + res = extended; + } + + /* Write encoded character. */ + int ret = snprintf(res + str_len, alloc_size - str_len, + "\\%03u", c); + if (ret <= 0 || ret >= alloc_size - str_len) { + goto dname_to_str_failed; + } + + str_len += ret; + } + + label_len--; + } + + /* String_termination. */ + assert(str_len < alloc_size); + res[str_len] = 0; + + return res; + +dname_to_str_failed: + + if (dst == NULL) { + free(res); + } + + return NULL; +} + +_public_ +knot_dname_t *knot_dname_from_str(uint8_t *dst, const char *name, size_t maxlen) +{ + if (name == NULL) { + return NULL; + } + + size_t name_len = strlen(name); + if (name_len == 0) { + return NULL; + } + + /* Wire size estimation. */ + size_t alloc_size = maxlen; + if (dst == NULL) { + /* Check for the root label. */ + if (name[0] == '.') { + /* Just the root dname can begin with a dot. */ + if (name_len > 1) { + return NULL; + } + name_len = 0; /* Skip the following parsing. */ + alloc_size = 1; + } else if (name[name_len - 1] != '.') { /* Check for non-FQDN. */ + alloc_size = 1 + name_len + 1; + } else { + alloc_size = 1 + name_len ; /* + 1 ~ first label length. */ + } + } + + /* The minimal (root) dname takes 1 byte. */ + if (alloc_size == 0) { + return NULL; + } + + /* Check the maximal wire size. */ + if (alloc_size > KNOT_DNAME_MAXLEN) { + alloc_size = KNOT_DNAME_MAXLEN; + } + + /* Prepare output buffer. */ + uint8_t *wire = (dst == NULL) ? malloc(alloc_size) : dst; + if (wire == NULL) { + return NULL; + } + + uint8_t *label = wire; + uint8_t *wire_pos = wire + 1; + uint8_t *wire_end = wire + alloc_size; + + /* Initialize the first label (root label). */ + *label = 0; + + const uint8_t *ch = (const uint8_t *)name; + const uint8_t *end = ch + name_len; + + while (ch < end) { + /* Check the output buffer for enough space. */ + if (wire_pos >= wire_end) { + goto dname_from_str_failed; + } + + switch (*ch) { + case '.': + /* Check for invalid zero-length label. */ + if (*label == 0 && name_len > 1) { + goto dname_from_str_failed; + } + label = wire_pos++; + *label = 0; + break; + case '\\': + ch++; + + /* At least one more character is required OR + * check for maximal label length. + */ + if (ch == end || ++(*label) > KNOT_DNAME_MAXLABELLEN) { + goto dname_from_str_failed; + } + + /* Check for \DDD notation. */ + if (is_digit(*ch)) { + /* Check for next two digits. */ + if (ch + 2 >= end || + !is_digit(*(ch + 1)) || + !is_digit(*(ch + 2))) { + goto dname_from_str_failed; + } + + uint32_t num = (*(ch + 0) - '0') * 100 + + (*(ch + 1) - '0') * 10 + + (*(ch + 2) - '0') * 1; + if (num > UINT8_MAX) { + goto dname_from_str_failed; + } + *(wire_pos++) = num; + ch +=2; + } else { + *(wire_pos++) = *ch; + } + break; + default: + /* Check for maximal label length. */ + if (++(*label) > KNOT_DNAME_MAXLABELLEN) { + goto dname_from_str_failed; + } + *(wire_pos++) = *ch; + } + ch++; + } + + /* Check for non-FQDN name. */ + if (*label > 0) { + if (wire_pos >= wire_end) { + goto dname_from_str_failed; + } + *(wire_pos++) = 0; + } + + /* Reduce output buffer if the size is overestimated. */ + if (wire_pos < wire_end && dst == NULL) { + uint8_t *reduced = realloc(wire, wire_pos - wire); + if (reduced == NULL) { + goto dname_from_str_failed; + } + wire = reduced; + } + + return wire; + +dname_from_str_failed: + + if (dst == NULL) { + free(wire); + } + + return NULL; +} + +_public_ +void knot_dname_to_lower(knot_dname_t *name) +{ + if (name == NULL) { + return; + } + + while (*name != '\0') { + uint8_t len = *name; + for (uint8_t i = 1; i <= len; ++i) { + name[i] = knot_tolower(name[i]); + } + name += 1 + len; + } +} + +_public_ +size_t knot_dname_size(const knot_dname_t *name) +{ + if (name == NULL) { + return 0; + } + + /* Count name size without terminal label. */ + size_t len = 0; + while (*name != '\0' && !knot_wire_is_pointer(name)) { + uint8_t lblen = *name + 1; + len += lblen; + name += lblen; + } + + if (knot_wire_is_pointer(name)) { + /* Add 2-octet compression pointer. */ + return len + 2; + } else { + /* Add 1-octet terminal label. */ + return len + 1; + } +} + +_public_ +size_t knot_dname_realsize(const knot_dname_t *name, const uint8_t *pkt) +{ + if (name == NULL) { + return 0; + } + + /* Seek first real label occurrence. */ + name = knot_wire_seek_label(name, pkt); + + size_t len = 0; + while (*name != '\0') { + len += *name + 1; + name = knot_wire_next_label(name, pkt); + } + + /* Add 1-octet terminal label. */ + return len + 1; +} + +_public_ +size_t knot_dname_matched_labels(const knot_dname_t *d1, const knot_dname_t *d2) +{ + /* Count labels. */ + size_t l1 = knot_dname_labels(d1, NULL); + size_t l2 = knot_dname_labels(d2, NULL); + if (l1 == 0 || l2 == 0) { + return 0; + } + + /* Align end-to-end to common suffix. */ + int common = dname_align(&d1, l1, &d2, l2); + + /* Count longest chain leading to root label. */ + size_t matched = 0; + while (common > 0) { + if (label_is_equal(d1, d2)) { + ++matched; + } else { + matched = 0; /* Broken chain. */ + } + + /* Next label. */ + d1 = knot_wire_next_label(d1, NULL); + d2 = knot_wire_next_label(d2, NULL); + --common; + } + + return matched; +} + +_public_ +knot_dname_t *knot_dname_replace_suffix(const knot_dname_t *name, unsigned labels, + const knot_dname_t *suffix, knot_mm_t *mm) +{ + if (name == NULL) { + return NULL; + } + + /* Calculate prefix and suffix lengths. */ + size_t dname_lbs = knot_dname_labels(name, NULL); + if (dname_lbs < labels) { + return NULL; + } + size_t prefix_lbs = dname_lbs - labels; + + size_t prefix_len = knot_dname_prefixlen(name, prefix_lbs, NULL); + size_t suffix_len = knot_dname_size(suffix); + if (prefix_len == 0 || suffix_len == 0) { + return NULL; + } + + /* Create target name. */ + size_t new_len = prefix_len + suffix_len; + knot_dname_t *out = mm_alloc(mm, new_len); + if (out == NULL) { + return NULL; + } + + /* Copy prefix. */ + uint8_t *dst = out; + while (prefix_lbs > 0) { + memcpy(dst, name, *name + 1); + dst += *name + 1; + name = knot_wire_next_label(name, NULL); + --prefix_lbs; + } + + /* Copy suffix. */ + while (*suffix != '\0') { + memcpy(dst, suffix, *suffix + 1); + dst += *suffix + 1; + suffix = knot_wire_next_label(suffix, NULL); + } + + *dst = '\0'; + return out; +} + +_public_ +void knot_dname_free(knot_dname_t *name, knot_mm_t *mm) +{ + if (name == NULL) { + return; + } + + mm_free(mm, name); +} + +_public_ +int knot_dname_cmp(const knot_dname_t *d1, const knot_dname_t *d2) +{ + if (d1 == NULL) { + return -1; + } else if (d2 == NULL) { + return 1; + } + + /* Convert to lookup format. */ + knot_dname_storage_t lf1_storage; + knot_dname_storage_t lf2_storage; + + uint8_t *lf1 = knot_dname_lf(d1, lf1_storage); + uint8_t *lf2 = knot_dname_lf(d2, lf2_storage); + assert(lf1 && lf2); + + /* Compare common part. */ + uint8_t common = lf1[0]; + if (common > lf2[0]) { + common = lf2[0]; + } + int ret = memcmp(lf1 + 1, lf2 + 1, common); + if (ret != 0) { + return ret; + } + + /* If they match, compare lengths. */ + if (lf1[0] < lf2[0]) { + return -1; + } else if (lf1[0] > lf2[0]) { + return 1; + } else { + return 0; + } +} + +_public_ +bool knot_dname_is_equal(const knot_dname_t *d1, const knot_dname_t *d2) +{ + if (d1 == NULL || d2 == NULL) { + return false; + } + + while (*d1 != '\0' || *d2 != '\0') { + if (label_is_equal(d1, d2)) { + d1 = knot_wire_next_label(d1, NULL); + d2 = knot_wire_next_label(d2, NULL); + } else { + return false; + } + } + + return true; +} + +_public_ +size_t knot_dname_prefixlen(const uint8_t *name, unsigned nlabels, const uint8_t *pkt) +{ + if (name == NULL) { + return 0; + } + + /* Zero labels means no prefix. */ + if (nlabels == 0) { + return 0; + } + + /* Seek first real label occurrence. */ + name = knot_wire_seek_label(name, pkt); + + size_t len = 0; + while (*name != '\0') { + len += *name + 1; + name = knot_wire_next_label(name, pkt); + if (--nlabels == 0) { /* Count N first labels only. */ + break; + } + } + + return len; +} + +_public_ +size_t knot_dname_labels(const uint8_t *name, const uint8_t *pkt) +{ + if (name == NULL) { + return 0; + } + + size_t count = 0; + while (*name != '\0') { + ++count; + name = knot_wire_next_label(name, pkt); + if (name == NULL) { + return 0; + } + } + + return count; +} + +_public_ +uint8_t *knot_dname_lf(const knot_dname_t *src, knot_dname_storage_t storage) +{ + if (src == NULL || storage == NULL) { + return NULL; + } + + /* Writing from the end. */ + storage[KNOT_DNAME_MAXLEN - 1] = '\0'; + size_t idx = KNOT_DNAME_MAXLEN - 1; + + while (*src != 0) { + size_t len = *src + 1; + + assert(idx >= len); + idx -= len; + memcpy(&storage[idx], src, len); + storage[idx] = '\0'; + + src += len; + } + + assert(KNOT_DNAME_MAXLEN >= 1 + idx); + storage[idx] = KNOT_DNAME_MAXLEN - 1 - idx; + + return &storage[idx]; +} + +_public_ +int knot_dname_in_bailiwick(const knot_dname_t *name, const knot_dname_t *bailiwick) +{ + if (name == NULL || bailiwick == NULL) { + return KNOT_EINVAL; + } + + int label_diff = knot_dname_labels(name, NULL) - knot_dname_labels(bailiwick, NULL); + if (label_diff < 0) { + return KNOT_EOUTOFZONE; + } + + for (int i = 0; i < label_diff; ++i) { + name = knot_wire_next_label(name, NULL); + } + + return knot_dname_is_equal(name, bailiwick) ? label_diff : KNOT_EOUTOFZONE; +} diff --git a/src/libknot/dname.h b/src/libknot/dname.h new file mode 100644 index 0000000..c701610 --- /dev/null +++ b/src/libknot/dname.h @@ -0,0 +1,328 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \file + * + * \brief Domain name structure and API for manipulating it. + * + * \addtogroup dname + * @{ + */ + +#pragma once + +#include <stdint.h> +#include <stdbool.h> + +#include "libknot/attribute.h" +#include "libknot/consts.h" +#include "libknot/mm_ctx.h" + +/*! \brief Type representing a domain name in wire format. */ +typedef uint8_t knot_dname_t; + +/*! \brief Local domain name storage. */ +typedef uint8_t knot_dname_storage_t[KNOT_DNAME_MAXLEN]; + +/*! + * \brief Check dname on the wire for constraints. + * + * If the name passes such checks, it is safe to be used in rest of the functions. + * + * \param name Name on the wire. + * \param endp Name boundary. + * \param pkt Wire. + * + * \retval (compressed) size of the domain name. + * \retval KNOT_EINVAL + * \retval KNOT_EMALF + */ +_pure_ _mustcheck_ +int knot_dname_wire_check(const uint8_t *name, const uint8_t *endp, + const uint8_t *pkt); + +/*! + * \brief Duplicates the given domain name to a local storage. + * + * \param dst Destination storage. + * \param name Domain name to be copied. + * + * \retval size of the domain name. + * \retval 0 if invalid argument. + */ +_mustcheck_ +size_t knot_dname_store(knot_dname_storage_t dst, const knot_dname_t *name); + +/*! + * \brief Duplicates the given domain name. + * + * \param name Domain name to be copied. + * \param mm Memory context. + * + * \return New domain name which is an exact copy of \a name. + */ +_mustcheck_ +knot_dname_t *knot_dname_copy(const knot_dname_t *name, knot_mm_t *mm); + +/*! + * \brief Copy name to wire as is, no compression pointer expansion will be done. + * + * \param dst Destination wire. + * \param src Source name. + * \param maxlen Maximum wire length. + * + * \return the number of bytes written or negative error code + */ +int knot_dname_to_wire(uint8_t *dst, const knot_dname_t *src, size_t maxlen); + +/*! + * \brief Write unpacked name (i.e. compression pointers expanded) + * + * \note The function is very similar to the knot_dname_to_wire(), except + * it expands compression pointers. E.g. you want to use knot_dname_unpack() + * if you copy a dname from incoming packet to some persistent storage. + * And you want to use knot_dname_to_wire() if you know the name is not + * compressed or you want to copy it 1:1. + * + * \param dst Destination wire. + * \param src Source name. + * \param maxlen Maximum destination wire size. + * \param pkt Name packet wire (for compression pointers). + * + * \return number of bytes written + */ +int knot_dname_unpack(uint8_t *dst, const knot_dname_t *src, + size_t maxlen, const uint8_t *pkt); + +/*! + * \brief Converts the given domain name to its string representation. + * + * \note Output buffer is allocated automatically if dst is NULL. + * + * \param dst Output buffer. + * \param name Domain name to be converted. + * \param maxlen Output buffer length. + * + * \return 0-terminated string if successful, NULL if error. + */ +char *knot_dname_to_str(char *dst, const knot_dname_t *name, size_t maxlen); + +/*! + * \brief This function is a shortcut for \ref knot_dname_to_str with + * no output buffer parameters. + */ +_mustcheck_ +static inline char *knot_dname_to_str_alloc(const knot_dname_t *name) +{ + return knot_dname_to_str(NULL, name, 0); +} + +/*! + * \brief Creates a dname structure from domain name given in presentation + * format. + * + * \note The resulting FQDN is stored in the wire format. + * \note Output buffer is allocated automatically if dst is NULL. + * + * \param dst Output buffer. + * \param name Domain name in presentation format (labels separated by dots, + * '\0' terminated). + * \param maxlen Output buffer length. + * + * \return New dname if successful, NULL if error. + */ +knot_dname_t *knot_dname_from_str(uint8_t *dst, const char *name, size_t maxlen); + +/*! + * \brief This function is a shortcut for \ref knot_dname_from_str with + * no output buffer parameters. + */ +_mustcheck_ +static inline knot_dname_t *knot_dname_from_str_alloc(const char *name) +{ + return knot_dname_from_str(NULL, name, 0); +} + +/*! + * \brief Convert name to lowercase. + * + * \param name Domain name to be converted. + */ +void knot_dname_to_lower(knot_dname_t *name); + +/*! + * \brief Returns size of the given domain name. + * + * \note If the domain name is compressed, the length of not compressed part + * is returned. + * + * \param name Domain name to get the size of. + * + * \retval size of the domain name. + * \retval 0 if invalid argument. + */ +_pure_ +size_t knot_dname_size(const knot_dname_t *name); + +/*! + * \brief Returns full size of the given domain name (expanded compression ptrs). + * + * \param name Domain name to get the size of. + * \param pkt Related packet (or NULL if unpacked) + * + * \retval size of the domain name. + * \retval 0 if invalid argument. + */ +_pure_ +size_t knot_dname_realsize(const knot_dname_t *name, const uint8_t *pkt); + +/*! + * \brief Checks if the domain name is a wildcard. + * + * \param name Domain name to check. + * + * \retval true if \a dname is a wildcard domain name. + * \retval false otherwise. + */ +static inline +bool knot_dname_is_wildcard(const knot_dname_t *name) +{ + return name != NULL && name[0] == 1 && name[1] == '*'; +} + +/*! + * \brief Returns the number of labels common for the two domain names (counted + * from the rightmost label. + * + * \param d1 First domain name. + * \param d2 Second domain name. + * + * \return Number of labels common for the two domain names. + */ +_pure_ +size_t knot_dname_matched_labels(const knot_dname_t *d1, const knot_dname_t *d2); + +/*! + * \brief Replaces the suffix of given size in one domain name with other domain + * name. + * + * \param name Domain name where to replace the suffix. + * \param labels Size of the suffix to be replaced. + * \param suffix New suffix to be used as a replacement. + * \param mm Memory context. + * + * \return New domain name created by replacing suffix of \a dname of size + * \a size with \a suffix. + */ +_mustcheck_ +knot_dname_t *knot_dname_replace_suffix(const knot_dname_t *name, unsigned labels, + const knot_dname_t *suffix, knot_mm_t *mm); + +/*! + * \brief Destroys the given domain name. + * + * \param name Domain name to be destroyed. + * \param mm Memory context. + */ +void knot_dname_free(knot_dname_t *name, knot_mm_t *mm); + +/*! + * \brief Compares two domain names by labels (case sensitive). + * + * \param d1 First domain name. + * \param d2 Second domain name. + * + * \retval < 0 if \a d1 goes before \a d2 in canonical order. + * \retval > 0 if \a d1 goes after \a d2 in canonical order. + * \retval 0 if the domain names are identical. + */ +_pure_ +int knot_dname_cmp(const knot_dname_t *d1, const knot_dname_t *d2); + +/*! + * \brief Compares two domain names (case sensitive). + * + * \param d1 First domain name. + * \param d2 Second domain name. + * + * \retval true if the domain names are identical + * \retval false if the domain names are NOT identical + */ +_pure_ +bool knot_dname_is_equal(const knot_dname_t *d1, const knot_dname_t *d2); + +/*! + * \brief Count length of the N first labels. + * + * \param name Domain name. + * \param nlabels First N labels. + * \param pkt Related packet (or NULL if not compressed). + * + * \return Length of the prefix. + */ +_pure_ +size_t knot_dname_prefixlen(const uint8_t *name, unsigned nlabels, const uint8_t *pkt); + +/*! + * \brief Return number of labels in the domain name. + * + * Terminal nullbyte is not counted. + * + * \param name Domain name. + * \param pkt Related packet (or NULL if not compressed). + * + * \return Number of labels. + */ +_pure_ +size_t knot_dname_labels(const uint8_t *name, const uint8_t *pkt); + +/*! + * \brief Convert domain name from wire to the lookup format. + * + * Formats names from rightmost label to the leftmost, separated by the lowest + * possible character (\\x00). Sorting such formatted names also gives + * correct canonical order (for NSEC/NSEC3). The first byte of the output + * contains length of the output. + * + * Examples: + * Name: lake.example.com. + * Wire: \\x04lake\\x07example\\x03com\\x00 + * Lookup: \\x11com\\x00example\\x00lake\\x00 + * + * Name: . + * Wire: \\x00 + * Lookup: \\x00 + * + * \param src Source domain name. + * \param storage Memory to store converted name into. Don't use directly! + * + * \retval Lookup format if successful (pointer into the storage). + * \retval NULL on invalid parameters. + */ +uint8_t *knot_dname_lf(const knot_dname_t *src, knot_dname_storage_t storage); + +/*! + * \brief Check whether a domain name is under another one and how deep. + * + * \param name The longer name to check. + * \param bailiwick The shorter name to check. + * + * \retval >=0 a subdomain nested this many labels. + * \retval <0 not a subdomain (KNOT_EOUTOFZONE) or another error (KNOT_EINVAL). + */ +int knot_dname_in_bailiwick(const knot_dname_t *name, const knot_dname_t *bailiwick); + +/*! @} */ diff --git a/src/libknot/endian.h b/src/libknot/endian.h new file mode 100644 index 0000000..30bdd77 --- /dev/null +++ b/src/libknot/endian.h @@ -0,0 +1,50 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \file + * + * \brief Endian dependent integer operations. + * + * \addtogroup wire + * @{ + */ + +#pragma once + +#if defined(__linux__) || defined(__gnu_hurd__) || \ + (defined(__FreeBSD_kernel__) && defined(__GLIBC__)) +# include <endian.h> +#elif defined(__FreeBSD__) || defined(__NetBSD__) +# include <sys/endian.h> +#elif defined(__OpenBSD__) +# include <endian.h> +#elif defined(__APPLE__) +# include <libkern/OSByteOrder.h> +# define be16toh(x) OSSwapBigToHostInt16(x) +# define be32toh(x) OSSwapBigToHostInt32(x) +# define be64toh(x) OSSwapBigToHostInt64(x) +# define htobe16(x) OSSwapHostToBigInt16(x) +# define htobe32(x) OSSwapHostToBigInt32(x) +# define htobe64(x) OSSwapHostToBigInt64(x) +# define le16toh(x) OSSwapLittleToHostInt16(x) +# define le32toh(x) OSSwapLittleToHostInt32(x) +# define le64toh(x) OSSwapLittleToHostInt64(x) +# define htole16(x) OSSwapHostToLittleInt16(x) +# define htole32(x) OSSwapHostToLittleInt32(x) +# define htole64(x) OSSwapHostToLittleInt64(x) +#endif + +/*! @} */ diff --git a/src/libknot/errcode.h b/src/libknot/errcode.h new file mode 100644 index 0000000..2fd1cea --- /dev/null +++ b/src/libknot/errcode.h @@ -0,0 +1,203 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +/*! +* \file +* +* \brief Knot error codes. +* +* \addtogroup libknot +* @{ +*/ + +#pragma once + +#include <errno.h> + +/*! \brief Error codes used in the library. */ +enum knot_error { + KNOT_EOK = 0, + + /* Directly mapped error codes. */ + KNOT_ENOMEM = -ENOMEM, + KNOT_EINVAL = -EINVAL, + KNOT_ENOTSUP = -ENOTSUP, + KNOT_EBUSY = -EBUSY, + KNOT_EAGAIN = -EAGAIN, + KNOT_EACCES = -EACCES, + KNOT_ECONNREFUSED = -ECONNREFUSED, + KNOT_EISCONN = -EISCONN, + KNOT_EADDRINUSE = -EADDRINUSE, + KNOT_ENOENT = -ENOENT, + KNOT_EEXIST = -EEXIST, + KNOT_ERANGE = -ERANGE, + KNOT_EADDRNOTAVAIL = -EADDRNOTAVAIL, + + KNOT_ERROR_MIN = -1000, + + /* General errors. */ + KNOT_ERROR = KNOT_ERROR_MIN, + KNOT_EPARSEFAIL, + KNOT_ESEMCHECK, + KNOT_EUPTODATE, + KNOT_EFEWDATA, + KNOT_ESPACE, + KNOT_EMALF, + KNOT_ENSEC3PAR, + KNOT_ENSEC3CHAIN, + KNOT_EOUTOFZONE, + KNOT_EZONEINVAL, + KNOT_ENOZONE, + KNOT_ENONODE, + KNOT_ENORECORD, + KNOT_ENOMASTER, + KNOT_EPREREQ, + KNOT_ETTL, + KNOT_ENOXFR, + KNOT_EDENIED, + KNOT_ECONN, + KNOT_ETIMEOUT, + KNOT_ENODIFF, + KNOT_ENOTSIG, + KNOT_ELIMIT, + KNOT_EZONESIZE, + KNOT_EOF, + KNOT_ESYSTEM, + KNOT_EFILE, + KNOT_ESOAINVAL, + KNOT_ETRAIL, + + /* Control states. */ + KNOT_CTL_ESTOP, + + /* Network errors. */ + KNOT_NET_EADDR, + KNOT_NET_ESOCKET, + KNOT_NET_ECONNECT, + KNOT_NET_ESEND, + KNOT_NET_ERECV, + KNOT_NET_ETIMEOUT, + + /* Encoding errors. */ + KNOT_BASE64_ESIZE, + KNOT_BASE64_ECHAR, + KNOT_BASE32HEX_ESIZE, + KNOT_BASE32HEX_ECHAR, + + /* TSIG errors. */ + KNOT_TSIG_EBADSIG, + KNOT_TSIG_EBADKEY, + KNOT_TSIG_EBADTIME, + KNOT_TSIG_EBADTRUNC, + + /* DNSSEC errors. */ + KNOT_DNSSEC_EMISSINGKEYTYPE, + KNOT_DNSSEC_ENOKEY, + + /* Yparser errors. */ + KNOT_YP_ECHAR_TAB, + KNOT_YP_EINVAL_ITEM, + KNOT_YP_EINVAL_ID, + KNOT_YP_EINVAL_DATA, + KNOT_YP_EINVAL_INDENT, + KNOT_YP_ENOTSUP_DATA, + KNOT_YP_ENOTSUP_ID, + KNOT_YP_ENODATA, + KNOT_YP_ENOID, + + /* Configuration errors. */ + KNOT_CONF_ENOTINIT, + KNOT_CONF_EVERSION, + KNOT_CONF_EREDEFINE, + + /* Transaction errors. */ + KNOT_TXN_EEXISTS, + KNOT_TXN_ENOTEXISTS, + + /* Processing error. */ + KNOT_LAYER_ERROR, + + /* DNSSEC errors. */ + KNOT_INVALID_PUBLIC_KEY, + KNOT_INVALID_PRIVATE_KEY, + KNOT_INVALID_KEY_ALGORITHM, + KNOT_INVALID_KEY_SIZE, + KNOT_INVALID_KEY_ID, + KNOT_INVALID_KEY_NAME, + KNOT_NO_PUBLIC_KEY, + KNOT_NO_PRIVATE_KEY, + + KNOT_ERROR_MAX = -501 +}; + +/*! + * \brief Map POSIX errno code to Knot error code. + * + * \param code Errno code to transform (set -1 to use the current errno). + * + * \return Mapped errno or KNOT_ERROR if unknown. + */ +inline static int knot_map_errno_code(int code) +{ + if (code < 0) { + code = errno; + } + + typedef struct { + int errno_code; + int libknot_code; + } err_table_t; + + #define ERR_ITEM(name) { name, KNOT_##name } + static const err_table_t errno_to_errcode[] = { + ERR_ITEM(ENOMEM), + ERR_ITEM(EINVAL), + ERR_ITEM(ENOTSUP), + ERR_ITEM(EBUSY), + ERR_ITEM(EAGAIN), + ERR_ITEM(EACCES), + ERR_ITEM(ECONNREFUSED), + ERR_ITEM(EISCONN), + ERR_ITEM(EADDRINUSE), + ERR_ITEM(ENOENT), + ERR_ITEM(EEXIST), + ERR_ITEM(ERANGE), + ERR_ITEM(EADDRNOTAVAIL), + + /* Terminator - default value. */ + { 0, KNOT_ERROR } + }; + #undef ERR_ITEM + + const err_table_t *err = errno_to_errcode; + + while (err->errno_code != 0 && err->errno_code != code) { + err++; + } + + return err->libknot_code; +} + +/*! + * \brief Get a POSIX errno mapped to Knot error code. + * + * \return Mapped errno or KNOT_ERROR if unknown. + */ +inline static int knot_map_errno(void) +{ + return knot_map_errno_code(-1); +} + +/*! @} */ diff --git a/src/libknot/error.c b/src/libknot/error.c new file mode 100644 index 0000000..1524b79 --- /dev/null +++ b/src/libknot/error.c @@ -0,0 +1,206 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <limits.h> +#include <lmdb.h> + +#include "libknot/attribute.h" +#include "libknot/error.h" +#include "libdnssec/error.h" + +struct error { + int code; + const char *message; +}; + +static const struct error errors[] = { + { KNOT_EOK, "OK" }, + + /* Directly mapped error codes. */ + { KNOT_ENOMEM, "not enough memory" }, + { KNOT_EINVAL, "invalid parameter" }, + { KNOT_ENOTSUP, "operation not supported" }, + { KNOT_EBUSY, "requested resource is busy" }, + { KNOT_EAGAIN, "OS lacked necessary resources" }, + { KNOT_EACCES, "operation not permitted" }, + { KNOT_ECONNREFUSED, "connection refused" }, + { KNOT_EISCONN, "already connected" }, + { KNOT_EADDRINUSE, "address already in use" }, + { KNOT_ENOENT, "not exists" }, + { KNOT_EEXIST, "already exists" }, + { KNOT_ERANGE, "value is out of range" }, + { KNOT_EADDRNOTAVAIL, "address is not available" }, + + /* General errors. */ + { KNOT_ERROR, "failed" }, + { KNOT_EPARSEFAIL, "parser failed" }, + { KNOT_ESEMCHECK, "semantic check" }, + { KNOT_EUPTODATE, "zone is up-to-date" }, + { KNOT_EFEWDATA, "not enough data to parse" }, + { KNOT_ESPACE, "not enough space provided" }, + { KNOT_EMALF, "malformed data" }, + { KNOT_ENSEC3PAR, "missing or wrong NSEC3PARAM record" }, + { KNOT_ENSEC3CHAIN, "missing or wrong NSEC3 chain in the zone" }, + { KNOT_EOUTOFZONE, "name does not belong to the zone" }, + { KNOT_EZONEINVAL, "invalid zone file" }, + { KNOT_ENOZONE, "no such zone found" }, + { KNOT_ENONODE, "no such node in zone found" }, + { KNOT_ENORECORD, "no such record in zone found" }, + { KNOT_ENOMASTER, "no usable master" }, + { KNOT_EPREREQ, "UPDATE prerequisity not met" }, + { KNOT_ETTL, "TTL mismatch" }, + { KNOT_ENOXFR, "transfer was not sent" }, + { KNOT_EDENIED, "not allowed" }, + { KNOT_ECONN, "connection reset" }, + { KNOT_ETIMEOUT, "connection timeout" }, + { KNOT_ENODIFF, "cannot create zone diff" }, + { KNOT_ENOTSIG, "expected a TSIG or SIG(0)" }, + { KNOT_ELIMIT, "exceeded response rate limit" }, + { KNOT_EZONESIZE, "zone size exceeded" }, + { KNOT_EOF, "end of file" }, + { KNOT_ESYSTEM, "system error" }, + { KNOT_EFILE, "file error" }, + { KNOT_ESOAINVAL, "SOA mismatch" }, + { KNOT_ETRAIL, "trailing data" }, + + /* Control states. */ + { KNOT_CTL_ESTOP, "stopping server" }, + + /* Network errors. */ + { KNOT_NET_EADDR, "bad address or host name" }, + { KNOT_NET_ESOCKET, "can't create socket" }, + { KNOT_NET_ECONNECT, "can't connect" }, + { KNOT_NET_ESEND, "can't send data" }, + { KNOT_NET_ERECV, "can't receive data" }, + { KNOT_NET_ETIMEOUT, "network timeout" }, + + /* Encoding errors. */ + { KNOT_BASE64_ESIZE, "invalid base64 string length" }, + { KNOT_BASE64_ECHAR, "invalid base64 character" }, + { KNOT_BASE32HEX_ESIZE, "invalid base32hex string length" }, + { KNOT_BASE32HEX_ECHAR, "invalid base32hex character" }, + + /* TSIG errors. */ + { KNOT_TSIG_EBADSIG, "failed to verify TSIG" }, + { KNOT_TSIG_EBADKEY, "TSIG key not recognized or invalid" }, + { KNOT_TSIG_EBADTIME, "TSIG out of time window" }, + { KNOT_TSIG_EBADTRUNC, "TSIG bad truncation" }, + + /* DNSSEC errors. */ + { KNOT_DNSSEC_ENOKEY, "no keys for signing" }, + { KNOT_DNSSEC_EMISSINGKEYTYPE, "missing active KSK or ZSK" }, + + /* Yparser errors. */ + { KNOT_YP_ECHAR_TAB, "tabulator character is not allowed" }, + { KNOT_YP_EINVAL_ITEM, "invalid item" }, + { KNOT_YP_EINVAL_ID, "invalid identifier" }, + { KNOT_YP_EINVAL_DATA, "invalid value" }, + { KNOT_YP_EINVAL_INDENT, "invalid indentation" }, + { KNOT_YP_ENOTSUP_DATA, "value not supported" }, + { KNOT_YP_ENOTSUP_ID, "identifier not supported" }, + { KNOT_YP_ENODATA, "missing value" }, + { KNOT_YP_ENOID, "missing identifier" }, + + /* Configuration errors. */ + { KNOT_CONF_ENOTINIT, "config DB not initialized" }, + { KNOT_CONF_EVERSION, "invalid config DB version" }, + { KNOT_CONF_EREDEFINE, "duplicate identifier" }, + + /* Transaction errors. */ + { KNOT_TXN_EEXISTS, "too many transactions" }, + { KNOT_TXN_ENOTEXISTS, "no active transaction" }, + + /* Processing errors. */ + { KNOT_LAYER_ERROR, "processing layer error" }, + + /* DNSSEC errors. */ + { KNOT_INVALID_PUBLIC_KEY, "invalid public key" }, + { KNOT_INVALID_PRIVATE_KEY, "invalid private key" }, + { KNOT_INVALID_KEY_ALGORITHM, "invalid key algorithm" }, + { KNOT_INVALID_KEY_SIZE, "invalid key size" }, + { KNOT_INVALID_KEY_ID, "invalid key ID" }, + { KNOT_INVALID_KEY_NAME, "invalid key name" }, + { KNOT_NO_PUBLIC_KEY, "no public key" }, + { KNOT_NO_PRIVATE_KEY, "no private key" }, + + { KNOT_ERROR, NULL } /* Terminator */ +}; + +/*! + * \brief Lookup error message by error code. + */ +static const char *lookup_message(int code) +{ + for (const struct error *e = errors; e->message; e++) { + if (e->code == code) { + return e->message; + } + } + + return NULL; +} + +_public_ +int knot_error_from_libdnssec(int libdnssec_errcode) +{ + switch (libdnssec_errcode) { + case DNSSEC_ERROR: + return KNOT_ERROR; + case DNSSEC_MALFORMED_DATA: + return KNOT_EMALF; + case DNSSEC_NOT_FOUND: + return KNOT_ENOENT; + case DNSSEC_NO_PUBLIC_KEY: + case DNSSEC_NO_PRIVATE_KEY: + return KNOT_DNSSEC_ENOKEY; + // EOK, EINVAL, ENOMEM and ENOENT are identical, no need to translate + case DNSSEC_INVALID_PUBLIC_KEY ... DNSSEC_INVALID_KEY_NAME: + return libdnssec_errcode + - DNSSEC_INVALID_PUBLIC_KEY + KNOT_INVALID_PUBLIC_KEY; + default: + return libdnssec_errcode; + } +} + +_public_ +const char *knot_strerror(int code) +{ + const char *msg; + + switch (code) { + case INT_MIN: // Cannot convert to a positive value. + code = KNOT_ERROR; + // FALLTHROUGH + case KNOT_ERROR_MIN ... KNOT_EOK: + msg = lookup_message(code); break; + case DNSSEC_ERROR_MIN ... DNSSEC_ERROR_MAX: + msg = dnssec_strerror(code); break; + case MDB_KEYEXIST ... MDB_LAST_ERRCODE: + msg = mdb_strerror(code); break; + default: + msg = NULL; + } + + if (msg != NULL) { + return msg; + } else { + // strerror_r would be better but it requires thread local storage. + return strerror(abs(code)); + } +} diff --git a/src/libknot/error.h b/src/libknot/error.h new file mode 100644 index 0000000..26fdddc --- /dev/null +++ b/src/libknot/error.h @@ -0,0 +1,49 @@ +/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +/*! +* \file +* +* \brief Error codes and function for getting error message. +* +* \addtogroup libknot +* @{ +*/ + +#pragma once + +#include "libknot/errcode.h" + +/*! + * \brief Returns error message for the given error code. + * + * \param code Error code. + * + * \return String containing the error message. + */ +const char *knot_strerror(int code); + +/*! + * \brief Translates error code from libdnssec into libknot. + * + * This is just temporary until everything from libdnssec moved to libknot. + * + * \param libdnssec_errcode Error code from libdnssec + * + * \return Error code. + */ +int knot_error_from_libdnssec(int libdnssec_errcode); + +/*! @} */ diff --git a/src/libknot/libknot.h b/src/libknot/libknot.h new file mode 100644 index 0000000..472fb8d --- /dev/null +++ b/src/libknot/libknot.h @@ -0,0 +1,65 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \file + * + * \brief Convenience header for including whole library. + * + * \addtogroup libknot + * @{ + */ + +#pragma once + +#include "libknot/version.h" +#include "libknot/cookies.h" +#include "libknot/codes.h" +#include "libknot/consts.h" +#include "libknot/descriptor.h" +#include "libknot/dname.h" +#include "libknot/endian.h" +#include "libknot/errcode.h" +#include "libknot/error.h" +#include "libknot/lookup.h" +#include "libknot/mm_ctx.h" +#include "libknot/rdata.h" +#include "libknot/rdataset.h" +#include "libknot/rrset-dump.h" +#include "libknot/rrset.h" +#include "libknot/tsig-op.h" +#include "libknot/tsig.h" +#include "libknot/control/control.h" +#include "libknot/db/db.h" +#include "libknot/db/db_lmdb.h" +#include "libknot/db/db_trie.h" +#include "libknot/packet/compr.h" +#include "libknot/packet/pkt.h" +#include "libknot/packet/rrset-wire.h" +#include "libknot/packet/wire.h" +#include "libknot/rrtype/dnskey.h" +#include "libknot/rrtype/ds.h" +#include "libknot/rrtype/naptr.h" +#include "libknot/rrtype/nsec.h" +#include "libknot/rrtype/nsec3.h" +#include "libknot/rrtype/nsec3param.h" +#include "libknot/rrtype/opt.h" +#include "libknot/rrtype/rdname.h" +#include "libknot/rrtype/rrsig.h" +#include "libknot/rrtype/soa.h" +#include "libknot/rrtype/tsig.h" +#include "libknot/wire.h" + +/*! @} */ diff --git a/src/libknot/lookup.h b/src/libknot/lookup.h new file mode 100644 index 0000000..709713e --- /dev/null +++ b/src/libknot/lookup.h @@ -0,0 +1,88 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \file + * + * \brief A general purpose lookup table. + * + * \addtogroup libknot + * @{ + */ + +#pragma once + +#include <string.h> +#include <strings.h> + +/*! + * \brief A general purpose lookup table. + */ +typedef struct knot_lookup { + int id; + const char *name; +} knot_lookup_t; + +/*! + * \brief Looks up the given name in the lookup table. + * + * \param table Lookup table. + * \param name Name to look up. + * + * \return Item in the lookup table with the given name or NULL if no such is + * present. + */ +inline static const knot_lookup_t *knot_lookup_by_name(const knot_lookup_t *table, const char *name) +{ + if (table == NULL || name == NULL) { + return NULL; + } + + while (table->name != NULL) { + if (strcasecmp(name, table->name) == 0) { + return table; + } + table++; + } + + return NULL; +} + +/*! + * \brief Looks up the given id in the lookup table. + * + * \param table Lookup table. + * \param id ID to look up. + * + * \return Item in the lookup table with the given id or NULL if no such is + * present. + */ +inline static const knot_lookup_t *knot_lookup_by_id(const knot_lookup_t *table, int id) +{ + if (table == NULL) { + return NULL; + } + + while (table->name != NULL) { + if (table->id == id) { + return table; + } + table++; + } + + return NULL; +} + +/*! @} */ diff --git a/src/libknot/mm_ctx.h b/src/libknot/mm_ctx.h new file mode 100644 index 0000000..1d0de70 --- /dev/null +++ b/src/libknot/mm_ctx.h @@ -0,0 +1,40 @@ +/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \file + * + * \brief Memory allocation context. + * + * \addtogroup libknot + * @{ + */ + +#pragma once + +#include <stddef.h> + +/* Memory allocation function prototypes. */ +typedef void* (*knot_mm_alloc_t)(void *ctx, size_t len); +typedef void (*knot_mm_free_t)(void *p); + +/*! \brief Memory allocation context. */ +typedef struct knot_mm { + void *ctx; /* \note Must be first */ + knot_mm_alloc_t alloc; + knot_mm_free_t free; +} knot_mm_t; + +/*! @} */ diff --git a/src/libknot/packet/compr.h b/src/libknot/packet/compr.h new file mode 100644 index 0000000..22c4a8e --- /dev/null +++ b/src/libknot/packet/compr.h @@ -0,0 +1,100 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \file + * + * \brief Name compression API. + * + * \addtogroup pkt + * @{ + */ + +#pragma once + +#include <stdint.h> + +#include "libknot/packet/wire.h" + +/*! \brief Compression hint type. */ +enum knot_compr_hint { + KNOT_COMPR_HINT_NONE = 0, /* No hint. */ + KNOT_COMPR_HINT_NOCOMP = 1, /* Don't compress. */ + KNOT_COMPR_HINT_QNAME = KNOT_WIRE_HEADER_SIZE /* Name is QNAME. */ +}; + +/*! \brief Compression hint array offsets. */ +enum knot_compr_offset { + KNOT_COMPR_HINT_OWNER = 0, /* First element in the array is RR owner. */ + KNOT_COMPR_HINT_RDATA = 1, /* First name in RDATA is at offset 1. */ + KNOT_COMPR_HINT_COUNT = 16 /* Maximum number of stored hints per-RR. */ +}; + +/* + * \note A little bit about how compression hints work. + * + * We're storing a RRSet say 'abcd. CNAME [0]net. [1]com.' (owner=abcd. 2 RRs). + * The owner 'abcd.' is same for both RRs, we put it at the offset 0 in rrinfo.compress_ptr + * The names 'net.' and 'com.' are in the RDATA, therefore go to offsets 1 and 2. + * Now this is useful when solving additionals for example, because we can scan + * rrinfo for this RRSet and we know that 'net.' name is at the hint 1 and that leads + * to packet position N. With that, we just put the pointer in without any calculation. + * This is also useful for positive answers, where we know the RRSet owner is always QNAME. + * All in all, we just remember the positions of written domain names. + */ + +/*! \brief Additional information about RRSet position and compression hints. */ +typedef struct { + uint16_t pos; /* RRSet position in the packet. */ + uint16_t flags; /* RRSet flags. */ + uint16_t compress_ptr[KNOT_COMPR_HINT_COUNT]; /* Array of compr. ptr hints. */ +} knot_rrinfo_t; + +/*! + * \brief Name compression context. + */ +typedef struct knot_compr { + uint8_t *wire; /* Packet wireformat. */ + knot_rrinfo_t *rrinfo; /* Hints for current RRSet. */ + struct { + uint16_t pos; /* Position of current suffix. */ + uint8_t labels; /* Label count of the suffix. */ + } suffix; +} knot_compr_t; + +/*! + * \brief Retrieve compression hint from given offset. + */ +static inline uint16_t knot_compr_hint(const knot_rrinfo_t *info, uint16_t hint_id) +{ + if (hint_id < KNOT_COMPR_HINT_COUNT) { + return info->compress_ptr[hint_id]; + } else { + return KNOT_COMPR_HINT_NONE; + } +} + +/*! + * \brief Store compression hint for given offset. + */ +static inline void knot_compr_hint_set(knot_rrinfo_t *info, uint16_t hint_id, + uint16_t val, uint16_t len) +{ + if ((hint_id < KNOT_COMPR_HINT_COUNT) && (val + len < KNOT_WIRE_PTR_MAX)) { + info->compress_ptr[hint_id] = val; + } +} + +/*! @} */ diff --git a/src/libknot/packet/pkt.c b/src/libknot/packet/pkt.c new file mode 100644 index 0000000..602d70d --- /dev/null +++ b/src/libknot/packet/pkt.c @@ -0,0 +1,826 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> +#include <stdlib.h> +#include <stdbool.h> + +#include "libknot/attribute.h" +#include "libknot/packet/pkt.h" +#include "libknot/codes.h" +#include "libknot/descriptor.h" +#include "libknot/errcode.h" +#include "libknot/rrtype/tsig.h" +#include "libknot/tsig-op.h" +#include "libknot/packet/wire.h" +#include "libknot/packet/rrset-wire.h" +#include "libknot/wire.h" +#include "contrib/mempattern.h" +#include "contrib/wire_ctx.h" + +/*! \brief Packet RR array growth step. */ +#define NEXT_RR_ALIGN 16 +#define NEXT_RR_COUNT(count) (((count) / NEXT_RR_ALIGN + 1) * NEXT_RR_ALIGN) + +/*! \brief Scan packet for RRSet existence. */ +static bool pkt_contains(const knot_pkt_t *packet, const knot_rrset_t *rrset) +{ + assert(packet); + assert(rrset); + + for (int i = 0; i < packet->rrset_count; ++i) { + const uint16_t type = packet->rr[i].type; + const knot_rdata_t *data = packet->rr[i].rrs.rdata; + if (type == rrset->type && data == rrset->rrs.rdata) { + return true; + } + } + + return false; +} + +/*! \brief Free all RRSets and reset RRSet count. */ +static void pkt_free_data(knot_pkt_t *pkt) +{ + assert(pkt); + + /* Free RRSets if applicable. */ + for (uint16_t i = 0; i < pkt->rrset_count; ++i) { + if (pkt->rr_info[i].flags & KNOT_PF_FREE) { + knot_rrset_clear(&pkt->rr[i], &pkt->mm); + } + } + pkt->rrset_count = 0; + + /* Free EDNS option positions. */ + mm_free(&pkt->mm, pkt->edns_opts); + pkt->edns_opts = 0; +} + +/*! \brief Allocate new wireformat of given length. */ +static int pkt_wire_alloc(knot_pkt_t *pkt, uint16_t len) +{ + assert(pkt); + + if (len < KNOT_WIRE_HEADER_SIZE) { + return KNOT_ERANGE; + } + + pkt->wire = mm_alloc(&pkt->mm, len); + if (pkt->wire == NULL) { + return KNOT_ENOMEM; + } + + pkt->flags |= KNOT_PF_FREE; + pkt->max_size = len; + + knot_pkt_clear(pkt); + + return KNOT_EOK; +} + +/*! \brief Set packet wireformat to an existing memory. */ +static void pkt_wire_set(knot_pkt_t *pkt, void *wire, uint16_t len) +{ + assert(pkt); + + pkt->wire = wire; + pkt->size = pkt->max_size = len; + pkt->parsed = 0; +} + +/*! \brief Calculate remaining size in the packet. */ +static uint16_t pkt_remaining(knot_pkt_t *pkt) +{ + assert(pkt); + + return pkt->max_size - pkt->size - pkt->reserved; +} + +/*! \brief Return RR count for given section (from wire xxCOUNT in header). */ +static uint16_t pkt_rr_wirecount(knot_pkt_t *pkt, knot_section_t section_id) +{ + assert(pkt); + switch (section_id) { + case KNOT_ANSWER: return knot_wire_get_ancount(pkt->wire); + case KNOT_AUTHORITY: return knot_wire_get_nscount(pkt->wire); + case KNOT_ADDITIONAL: return knot_wire_get_arcount(pkt->wire); + default: assert(0); return 0; + } +} + +/*! \brief Update RR count for given section (wire xxCOUNT in header). */ +static void pkt_rr_wirecount_add(knot_pkt_t *pkt, knot_section_t section_id, + int16_t val) +{ + assert(pkt); + switch (section_id) { + case KNOT_ANSWER: knot_wire_add_ancount(pkt->wire, val); break; + case KNOT_AUTHORITY: knot_wire_add_nscount(pkt->wire, val); break; + case KNOT_ADDITIONAL: knot_wire_add_arcount(pkt->wire, val); break; + } +} + +/*! \brief Reserve enough space in the RR arrays. */ +static int pkt_rr_array_alloc(knot_pkt_t *pkt, uint16_t count) +{ + /* Enough space. */ + if (pkt->rrset_allocd >= count) { + return KNOT_EOK; + } + + /* Allocate rr_info and rr fields to next size. */ + size_t next_size = NEXT_RR_COUNT(count); + knot_rrinfo_t *rr_info = mm_alloc(&pkt->mm, sizeof(knot_rrinfo_t) * next_size); + if (rr_info == NULL) { + return KNOT_ENOMEM; + } + + knot_rrset_t *rr = mm_alloc(&pkt->mm, sizeof(knot_rrset_t) * next_size); + if (rr == NULL) { + mm_free(&pkt->mm, rr_info); + return KNOT_ENOMEM; + } + + /* Copy the old data. */ + memcpy(rr_info, pkt->rr_info, pkt->rrset_allocd * sizeof(knot_rrinfo_t)); + memcpy(rr, pkt->rr, pkt->rrset_allocd * sizeof(knot_rrset_t)); + + /* Reassign and free old data. */ + mm_free(&pkt->mm, pkt->rr); + mm_free(&pkt->mm, pkt->rr_info); + pkt->rr = rr; + pkt->rr_info = rr_info; + pkt->rrset_allocd = next_size; + + return KNOT_EOK; +} + +static void compr_clear(knot_compr_t *compr) +{ + compr->rrinfo = NULL; + compr->suffix.pos = 0; + compr->suffix.labels = 0; +} + +static void compr_init(knot_compr_t *compr, uint8_t *wire) +{ + compr_clear(compr); + compr->wire = wire; +} + +/*! \brief Clear the packet and switch wireformat pointers (possibly allocate new). */ +static int pkt_init(knot_pkt_t *pkt, void *wire, uint16_t len, knot_mm_t *mm) +{ + assert(pkt); + + memset(pkt, 0, sizeof(knot_pkt_t)); + + /* No data to free, set memory context. */ + memcpy(&pkt->mm, mm, sizeof(knot_mm_t)); + + /* Initialize wire. */ + int ret = KNOT_EOK; + if (wire == NULL) { + ret = pkt_wire_alloc(pkt, len); + } else { + pkt_wire_set(pkt, wire, len); + } + + /* Initialize compression context. */ + compr_init(&pkt->compr, pkt->wire); + + return ret; +} + +/*! \brief Reset packet parse state. */ +static void sections_reset(knot_pkt_t *pkt) +{ + pkt->current = KNOT_ANSWER; + memset(pkt->sections, 0, sizeof(pkt->sections)); + (void)knot_pkt_begin(pkt, KNOT_ANSWER); +} + +/*! \brief Allocate new packet using memory context. */ +static knot_pkt_t *pkt_new_mm(void *wire, uint16_t len, knot_mm_t *mm) +{ + assert(mm); + + knot_pkt_t *pkt = mm_alloc(mm, sizeof(knot_pkt_t)); + if (pkt == NULL) { + return NULL; + } + + if (pkt_init(pkt, wire, len, mm) != KNOT_EOK) { + mm_free(mm, pkt); + return NULL; + } + + return pkt; +} + +_public_ +knot_pkt_t *knot_pkt_new(void *wire, uint16_t len, knot_mm_t *mm) +{ + /* Default memory allocator if NULL. */ + knot_mm_t _mm; + if (mm == NULL) { + mm_ctx_init(&_mm); + mm = &_mm; + } + + return pkt_new_mm(wire, len, mm); +} + +static int append_tsig(knot_pkt_t *dst, const knot_pkt_t *src) +{ + /* Check if a wire TSIG is available. */ + if (src->tsig_wire.pos != NULL) { + if (dst->max_size < src->size + src->tsig_wire.len) { + return KNOT_ESPACE; + } + memcpy(dst->wire + dst->size, src->tsig_wire.pos, + src->tsig_wire.len); + dst->size += src->tsig_wire.len; + + /* Increment arcount. */ + knot_wire_set_arcount(dst->wire, + knot_wire_get_arcount(dst->wire) + 1); + } else { + return knot_tsig_append(dst->wire, &dst->size, dst->max_size, + src->tsig_rr); + } + + return KNOT_EOK; +} + +_public_ +int knot_pkt_copy(knot_pkt_t *dst, const knot_pkt_t *src) +{ + if (dst == NULL || src == NULL) { + return KNOT_EINVAL; + } + + if (dst->max_size < src->size) { + return KNOT_ESPACE; + } + memcpy(dst->wire, src->wire, src->size); + dst->size = src->size; + + /* Append TSIG record. */ + if (src->tsig_rr) { + int ret = append_tsig(dst, src); + if (ret != KNOT_EOK) { + return ret; + } + } + + /* Invalidate arrays. */ + dst->rr = NULL; + dst->rr_info = NULL; + dst->rrset_count = 0; + dst->rrset_allocd = 0; + + /* @note This could be done more effectively if needed. */ + return knot_pkt_parse(dst, 0); +} + +static void payload_clear(knot_pkt_t *pkt) +{ + assert(pkt); + + /* Keep question. */ + pkt->parsed = 0; + pkt->reserved = 0; + + /* Free RRSets if applicable. */ + pkt_free_data(pkt); + + /* Reset sections. */ + sections_reset(pkt); + + /* Reset special types. */ + pkt->opt_rr = NULL; + pkt->tsig_rr = NULL; + + /* Reset TSIG wire reference. */ + pkt->tsig_wire.pos = NULL; + pkt->tsig_wire.len = 0; +} + +_public_ +int knot_pkt_init_response(knot_pkt_t *pkt, const knot_pkt_t *query) +{ + if (pkt == NULL || query == NULL) { + return KNOT_EINVAL; + } + + /* Header + question size. */ + size_t base_size = KNOT_WIRE_HEADER_SIZE + knot_pkt_question_size(query); + if (base_size > pkt->max_size) { + return KNOT_ESPACE; + } + + pkt->size = base_size; + memcpy(pkt->wire, query->wire, base_size); + + pkt->qname_size = query->qname_size; + if (query->qname_size == 0) { + /* Reset question count if malformed. */ + knot_wire_set_qdcount(pkt->wire, 0); + } + + /* Update flags and section counters. */ + knot_wire_set_ancount(pkt->wire, 0); + knot_wire_set_nscount(pkt->wire, 0); + knot_wire_set_arcount(pkt->wire, 0); + + knot_wire_set_qr(pkt->wire); + knot_wire_clear_tc(pkt->wire); + knot_wire_clear_ad(pkt->wire); + knot_wire_clear_ra(pkt->wire); + knot_wire_clear_aa(pkt->wire); + knot_wire_clear_z(pkt->wire); + + /* Clear payload. */ + payload_clear(pkt); + + return KNOT_EOK; +} + +_public_ +void knot_pkt_clear(knot_pkt_t *pkt) +{ + if (pkt == NULL) { + return; + } + + /* Reset to header size. */ + pkt->size = KNOT_WIRE_HEADER_SIZE; + memset(pkt->wire, 0, pkt->size); + + /* Clear payload. */ + payload_clear(pkt); + + /* Clear compression context. */ + compr_clear(&pkt->compr); +} + +_public_ +void knot_pkt_free(knot_pkt_t *pkt) +{ + if (pkt == NULL) { + return; + } + + /* Free temporary RRSets. */ + pkt_free_data(pkt); + + /* Free RR/RR info arrays. */ + mm_free(&pkt->mm, pkt->rr); + mm_free(&pkt->mm, pkt->rr_info); + + /* Free the space for wireformat. */ + if (pkt->flags & KNOT_PF_FREE) { + mm_free(&pkt->mm, pkt->wire); + } + + mm_free(&pkt->mm, pkt); +} + +_public_ +int knot_pkt_reserve(knot_pkt_t *pkt, uint16_t size) +{ + if (pkt == NULL) { + return KNOT_EINVAL; + } + + /* Reserve extra space (if possible). */ + if (pkt_remaining(pkt) >= size) { + pkt->reserved += size; + return KNOT_EOK; + } else { + return KNOT_ERANGE; + } +} + +_public_ +int knot_pkt_reclaim(knot_pkt_t *pkt, uint16_t size) +{ + if (pkt == NULL) { + return KNOT_EINVAL; + } + + if (pkt->reserved >= size) { + pkt->reserved -= size; + return KNOT_EOK; + } else { + return KNOT_ERANGE; + } +} + +_public_ +int knot_pkt_begin(knot_pkt_t *pkt, knot_section_t section_id) +{ + if (pkt == NULL || section_id < pkt->current) { + return KNOT_EINVAL; + } + + /* Remember watermark but not on repeated calls. */ + pkt->sections[section_id].pkt = pkt; + if (section_id > pkt->current) { + pkt->sections[section_id].pos = pkt->rrset_count; + } + + pkt->current = section_id; + + return KNOT_EOK; +} + +_public_ +int knot_pkt_put_question(knot_pkt_t *pkt, const knot_dname_t *qname, uint16_t qclass, uint16_t qtype) +{ + if (pkt == NULL || qname == NULL) { + return KNOT_EINVAL; + } + + assert(pkt->size == KNOT_WIRE_HEADER_SIZE); + assert(pkt->rrset_count == 0); + + /* Copy name wireformat. */ + wire_ctx_t wire = wire_ctx_init(pkt->wire, pkt->max_size); + wire_ctx_set_offset(&wire, KNOT_WIRE_HEADER_SIZE); + + int qname_len = knot_dname_to_wire(wire.position, + qname, wire_ctx_available(&wire)); + if (qname_len < 0) { + return qname_len; + } + wire_ctx_skip(&wire, qname_len); + + /* Copy QTYPE & QCLASS */ + wire_ctx_write_u16(&wire, qtype); + wire_ctx_write_u16(&wire, qclass); + + /* Check errors. */ + if (wire.error != KNOT_EOK) { + return wire.error; + } + + /* Update question count and sizes. */ + knot_wire_set_qdcount(pkt->wire, 1); + pkt->size = wire_ctx_offset(&wire); + pkt->qname_size = qname_len; + + /* Start writing ANSWER. */ + return knot_pkt_begin(pkt, KNOT_ANSWER); +} + +_public_ +int knot_pkt_put_rotate(knot_pkt_t *pkt, uint16_t compr_hint, const knot_rrset_t *rr, + uint16_t rotate, uint16_t flags) +{ + if (pkt == NULL || rr == NULL) { + return KNOT_EINVAL; + } + + /* Reserve memory for RR descriptors. */ + int ret = pkt_rr_array_alloc(pkt, pkt->rrset_count + 1); + if (ret != KNOT_EOK) { + return ret; + } + + /* Check for double insertion. */ + if ((flags & KNOT_PF_CHECKDUP) && pkt_contains(pkt, rr)) { + return KNOT_EOK; + } + + knot_rrinfo_t *rrinfo = &pkt->rr_info[pkt->rrset_count]; + memset(rrinfo, 0, sizeof(knot_rrinfo_t)); + rrinfo->pos = pkt->size; + rrinfo->flags = flags; + rrinfo->compress_ptr[0] = compr_hint; + memcpy(pkt->rr + pkt->rrset_count, rr, sizeof(knot_rrset_t)); + + /* Disable compression if no QNAME is available. */ + knot_compr_t *compr = NULL; + if (knot_pkt_qname(pkt) != NULL) { + /* Initialize compression context if it did not happen yet. */ + pkt->compr.rrinfo = rrinfo; + if (pkt->compr.suffix.pos == 0) { + pkt->compr.suffix.pos = KNOT_WIRE_HEADER_SIZE; + pkt->compr.suffix.labels = + knot_dname_labels(pkt->compr.wire + pkt->compr.suffix.pos, + pkt->compr.wire); + } + + compr = &pkt->compr; + } + + uint8_t *pos = pkt->wire + pkt->size; + size_t maxlen = pkt_remaining(pkt); + + /* Write RRSet to wireformat. */ + ret = knot_rrset_to_wire_extra(rr, pos, maxlen, rotate, compr, flags); + if (ret < 0) { + /* Truncate packet if required. */ + if (ret == KNOT_ESPACE && !(flags & KNOT_PF_NOTRUNC)) { + knot_wire_set_tc(pkt->wire); + } + return ret; + } + + size_t len = ret; + uint16_t rr_added = rr->rrs.count; + + /* Keep reference to special types. */ + if (rr->type == KNOT_RRTYPE_OPT) { + pkt->opt_rr = &pkt->rr[pkt->rrset_count]; + } + + if (rr_added > 0) { + pkt->rrset_count += 1; + pkt->sections[pkt->current].count += 1; + pkt->size += len; + pkt_rr_wirecount_add(pkt, pkt->current, rr_added); + } + + return KNOT_EOK; +} + +_public_ +int knot_pkt_parse_question(knot_pkt_t *pkt) +{ + if (pkt == NULL) { + return KNOT_EINVAL; + } + + /* Check at least header size. */ + if (pkt->size < KNOT_WIRE_HEADER_SIZE) { + return KNOT_EMALF; + } + + /* We have at least some DNS header. */ + pkt->parsed = KNOT_WIRE_HEADER_SIZE; + + /* Check QD count. */ + uint16_t qd = knot_wire_get_qdcount(pkt->wire); + if (qd > 1) { + return KNOT_EMALF; + } + + /* No question. */ + if (qd == 0) { + pkt->qname_size = 0; + return KNOT_EOK; + } + + /* Process question. */ + int len = knot_dname_wire_check(pkt->wire + pkt->parsed, + pkt->wire + pkt->size, + NULL /* No compression in QNAME. */); + if (len <= 0) { + return KNOT_EMALF; + } + + /* Check QCLASS/QTYPE size. */ + uint16_t question_size = len + 2 * sizeof(uint16_t); /* QCLASS + QTYPE */ + if (pkt->parsed + question_size > pkt->size) { + return KNOT_EMALF; + } + + pkt->parsed += question_size; + pkt->qname_size = len; + + return KNOT_EOK; +} + +/*! \brief Check constraints (position, uniqueness, validity) for special types + * (TSIG, OPT). + */ +static int check_rr_constraints(knot_pkt_t *pkt, knot_rrset_t *rr, size_t rr_size, + unsigned flags) +{ + switch (rr->type) { + case KNOT_RRTYPE_TSIG: + if (pkt->current != KNOT_ADDITIONAL || pkt->tsig_rr != NULL || + !knot_tsig_rdata_is_ok(rr)) { + return KNOT_EMALF; + } + + /* Strip TSIG RR from wireformat and decrease ARCOUNT. */ + if (!(flags & KNOT_PF_KEEPWIRE)) { + pkt->parsed -= rr_size; + pkt->size -= rr_size; + pkt->tsig_wire.pos = pkt->wire + pkt->parsed; + pkt->tsig_wire.len = rr_size; + knot_wire_set_arcount(pkt->wire, knot_wire_get_arcount(pkt->wire) - 1); + } + + pkt->tsig_rr = rr; + break; + case KNOT_RRTYPE_OPT: + if (pkt->current != KNOT_ADDITIONAL || pkt->opt_rr != NULL || + knot_edns_get_options(rr, &pkt->edns_opts, &pkt->mm) != KNOT_EOK) { + return KNOT_EMALF; + } + + pkt->opt_rr = rr; + break; + default: + break; + } + + return KNOT_EOK; +} + +static int parse_rr(knot_pkt_t *pkt, unsigned flags) +{ + assert(pkt); + + if (pkt->parsed >= pkt->size) { + return KNOT_EFEWDATA; + } + + /* Reserve memory for RR descriptors. */ + int ret = pkt_rr_array_alloc(pkt, pkt->rrset_count + 1); + if (ret != KNOT_EOK) { + return ret; + } + + /* Initialize RR info. */ + memset(&pkt->rr_info[pkt->rrset_count], 0, sizeof(knot_rrinfo_t)); + pkt->rr_info[pkt->rrset_count].pos = pkt->parsed; + pkt->rr_info[pkt->rrset_count].flags = KNOT_PF_FREE; + + /* Parse wire format. */ + size_t rr_size = pkt->parsed; + knot_rrset_t *rr = &pkt->rr[pkt->rrset_count]; + ret = knot_rrset_rr_from_wire(pkt->wire, &pkt->parsed, pkt->size, + rr, &pkt->mm, !(flags & KNOT_PF_NOCANON)); + if (ret != KNOT_EOK) { + return ret; + } + + /* Calculate parsed RR size from before/after parsing. */ + rr_size = (pkt->parsed - rr_size); + + /* Update packet RRSet count. */ + ++pkt->rrset_count; + ++pkt->sections[pkt->current].count; + + /* Check special RRs (OPT and TSIG). */ + return check_rr_constraints(pkt, rr, rr_size, flags); +} + +static int parse_section(knot_pkt_t *pkt, unsigned flags) +{ + assert(pkt); + + uint16_t rr_parsed = 0; + uint16_t rr_count = pkt_rr_wirecount(pkt, pkt->current); + + /* Parse all RRs belonging to the section. */ + for (rr_parsed = 0; rr_parsed < rr_count; ++rr_parsed) { + int ret = parse_rr(pkt, flags); + if (ret != KNOT_EOK) { + return ret; + } + } + + return KNOT_EOK; +} + +static int parse_payload(knot_pkt_t *pkt, unsigned flags) +{ + assert(pkt); + assert(pkt->wire); + assert(pkt->size > 0); + + /* Reserve memory in advance to avoid resizing. */ + size_t rr_count = knot_wire_get_ancount(pkt->wire) + + knot_wire_get_nscount(pkt->wire) + + knot_wire_get_arcount(pkt->wire); + + if (rr_count > pkt->size / KNOT_WIRE_RR_MIN_SIZE) { + return KNOT_EMALF; + } + + int ret = pkt_rr_array_alloc(pkt, rr_count); + if (ret != KNOT_EOK) { + return ret; + } + + for (knot_section_t i = KNOT_ANSWER; i <= KNOT_ADDITIONAL; ++i) { + ret = knot_pkt_begin(pkt, i); + if (ret != KNOT_EOK) { + return ret; + } + ret = parse_section(pkt, flags); + if (ret != KNOT_EOK) { + return ret; + } + } + + /* TSIG must be last record of AR if present. */ + const knot_pktsection_t *ar = knot_pkt_section(pkt, KNOT_ADDITIONAL); + if (pkt->tsig_rr != NULL) { + const knot_rrset_t *last_rr = knot_pkt_rr(ar, ar->count - 1); + if (ar->count > 0 && pkt->tsig_rr->rrs.rdata != last_rr->rrs.rdata) { + return KNOT_EMALF; + } + } + + /* Check for trailing garbage. */ + if (pkt->parsed < pkt->size) { + return KNOT_ETRAIL; + } + + return KNOT_EOK; +} + +_public_ +int knot_pkt_parse(knot_pkt_t *pkt, unsigned flags) +{ + if (pkt == NULL) { + return KNOT_EINVAL; + } + + /* Reset parse state. */ + sections_reset(pkt); + + int ret = knot_pkt_parse_question(pkt); + if (ret == KNOT_EOK) { + ret = parse_payload(pkt, flags); + } + + return ret; +} + +_public_ +uint16_t knot_pkt_ext_rcode(const knot_pkt_t *pkt) +{ + if (pkt == NULL) { + return 0; + } + + /* Get header RCODE. */ + uint16_t rcode = knot_wire_get_rcode(pkt->wire); + + /* Update to extended RCODE if EDNS is available. */ + if (pkt->opt_rr != NULL) { + uint8_t opt_rcode = knot_edns_get_ext_rcode(pkt->opt_rr); + rcode = knot_edns_whole_rcode(opt_rcode, rcode); + } + + /* Return if not NOTAUTH. */ + if (rcode != KNOT_RCODE_NOTAUTH) { + return rcode; + } + + /* Get TSIG RCODE. */ + uint16_t tsig_rcode = KNOT_RCODE_NOERROR; + if (pkt->tsig_rr != NULL) { + tsig_rcode = knot_tsig_rdata_error(pkt->tsig_rr); + } + + /* Return proper RCODE. */ + if (tsig_rcode != KNOT_RCODE_NOERROR) { + return tsig_rcode; + } else { + return rcode; + } +} + +_public_ +const char *knot_pkt_ext_rcode_name(const knot_pkt_t *pkt) +{ + if (pkt == NULL) { + return ""; + } + + uint16_t rcode = knot_pkt_ext_rcode(pkt); + + const knot_lookup_t *item = NULL; + if (pkt->tsig_rr != NULL) { + item = knot_lookup_by_id(knot_tsig_rcode_names, rcode); + } + if (item == NULL) { + item = knot_lookup_by_id(knot_rcode_names, rcode); + } + + return (item != NULL) ? item->name : ""; +} diff --git a/src/libknot/packet/pkt.h b/src/libknot/packet/pkt.h new file mode 100644 index 0000000..a105d9f --- /dev/null +++ b/src/libknot/packet/pkt.h @@ -0,0 +1,402 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \file + * + * \brief Structure for holding DNS packet data and metadata. + * + * \addtogroup pkt + * @{ + */ + +#pragma once + +#include <assert.h> +#include <stdint.h> + +#include "libknot/consts.h" +#include "libknot/dname.h" +#include "libknot/mm_ctx.h" +#include "libknot/rrset.h" +#include "libknot/rrtype/opt.h" +#include "libknot/packet/wire.h" +#include "libknot/packet/compr.h" +#include "libknot/wire.h" + +/* Number of packet sections (ANSWER, AUTHORITY, ADDITIONAL). */ +#define KNOT_PKT_SECTIONS 3 + +/*! + * \brief Packet flags. + */ +enum { + KNOT_PF_NULL = 0 << 0, /*!< No flags. */ + KNOT_PF_FREE = 1 << 1, /*!< Free with packet. */ + KNOT_PF_NOTRUNC = 1 << 2, /*!< Don't truncate. */ + KNOT_PF_CHECKDUP = 1 << 3, /*!< Check for duplicates. */ + KNOT_PF_KEEPWIRE = 1 << 4, /*!< Keep wireformat untouched when parsing. */ + KNOT_PF_NOCANON = 1 << 5, /*!< Don't canonicalize rrsets during parsing. */ + KNOT_PF_ORIGTTL = 1 << 6, /*!< Write RRSIGs with their original TTL. */ +}; + +typedef struct knot_pkt knot_pkt_t; + +/*! + * \brief Packet section. + * Points to RRSet and RRSet info arrays in the packet. + * This structure is required for random access to packet sections. + */ +typedef struct { + knot_pkt_t *pkt; /*!< Owner. */ + uint16_t pos; /*!< Position in the rr/rrinfo fields in packet. */ + uint16_t count; /*!< Number of RRSets in this section. */ +} knot_pktsection_t; + +/*! + * \brief Structure representing a DNS packet. + */ +struct knot_pkt { + uint8_t *wire; /*!< Wire format of the packet. */ + size_t size; /*!< Current wire size of the packet. */ + size_t max_size; /*!< Maximum allowed size of the packet. */ + size_t parsed; /*!< Parsed size. */ + uint16_t reserved; /*!< Reserved space. */ + uint16_t qname_size; /*!< QNAME size. */ + uint16_t rrset_count; /*!< Packet RRSet count. */ + uint16_t flags; /*!< Packet flags. */ + + knot_rrset_t *opt_rr; /*!< OPT RR included in the packet. */ + knot_rrset_t *tsig_rr; /*!< TSIG RR stored in the packet. */ + + /*! EDNS option positions in the wire (if parsed from wire). */ + knot_edns_options_t *edns_opts; + + /*! TSIG RR position in the wire (if parsed from wire). */ + struct { + uint8_t *pos; + size_t len; + } tsig_wire; + + /* Packet sections. */ + knot_section_t current; + knot_pktsection_t sections[KNOT_PKT_SECTIONS]; + + /* Packet RRSet (meta)data. */ + size_t rrset_allocd; + knot_rrinfo_t *rr_info; + knot_rrset_t *rr; + + knot_mm_t mm; /*!< Memory allocation context. */ + + knot_compr_t compr; /*!< Compression context. */ +}; + +/*! + * \brief Create new packet over existing memory, or allocate new from memory context. + * + * \note Packet is allocated from given memory context. + * + * \param wire If NULL, memory of 'len' size shall be allocated. + * Otherwise pointer is used for the wire format of the packet. + * \param len Wire format length. + * \param mm Memory context (NULL for default). + * \return New packet or NULL. + */ +knot_pkt_t *knot_pkt_new(void *wire, uint16_t len, knot_mm_t *mm); + +/*! + * \brief Copy packet. + * + * \note Current implementation is not very efficient, as it re-parses the wire. + * + * \param dst Target packet. + * \param src Source packet. + * + * \return new packet or NULL + */ +int knot_pkt_copy(knot_pkt_t *dst, const knot_pkt_t *src); + +/*! + * \brief Initialized response from query packet. + * + * \note Question is not checked, it is expected to be checked already. + * + * \param pkt Given packet. + * \param query Query. + * \return KNOT_EOK, KNOT_EINVAL, KNOT_ESPACE + */ +int knot_pkt_init_response(knot_pkt_t *pkt, const knot_pkt_t *query); + +/*! \brief Reinitialize packet for another use. */ +void knot_pkt_clear(knot_pkt_t *pkt); + +/*! \brief Begone you foul creature of the underworld. */ +void knot_pkt_free(knot_pkt_t *pkt); + +/*! + * \brief Reserve an arbitrary amount of space in the packet. + * + * \return KNOT_EOK + * \return KNOT_ERANGE if size can't be reserved + */ +int knot_pkt_reserve(knot_pkt_t *pkt, uint16_t size); + +/*! + * \brief Reclaim reserved size. + * + * \return KNOT_EOK + * \return KNOT_ERANGE if size can't be reclaimed + */ +int knot_pkt_reclaim(knot_pkt_t *pkt, uint16_t size); + +/* + * Packet QUESTION accessors. + */ +static inline uint16_t knot_pkt_question_size(const knot_pkt_t *pkt) +{ + if (pkt == NULL || pkt->qname_size == 0) { + return 0; + } + + return pkt->qname_size + 2 * sizeof(uint16_t); +} + +static inline knot_dname_t *knot_pkt_qname(const knot_pkt_t *pkt) +{ + if (pkt == NULL || pkt->qname_size == 0) { + return NULL; + } + + return pkt->wire + KNOT_WIRE_HEADER_SIZE; +} + +static inline uint16_t knot_pkt_qtype(const knot_pkt_t *pkt) +{ + if (pkt == NULL || pkt->qname_size == 0) { + return 0; + } + + unsigned off = KNOT_WIRE_HEADER_SIZE + pkt->qname_size; + return knot_wire_read_u16(pkt->wire + off); +} + +static inline uint16_t knot_pkt_qclass(const knot_pkt_t *pkt) +{ + if (pkt == NULL || pkt->qname_size == 0) { + return 0; + } + + unsigned off = KNOT_WIRE_HEADER_SIZE + pkt->qname_size + sizeof(uint16_t); + return knot_wire_read_u16(pkt->wire + off); +} + +/* + * Packet writing API. + */ + +/*! + * \brief Begin reading/writing packet section. + * + * \note You must proceed in the natural order (ANSWER, AUTHORITY, ADDITIONAL). + * + * \param pkt + * \param section_id + * \return KNOT_EOK or KNOT_EINVAL + */ +int knot_pkt_begin(knot_pkt_t *pkt, knot_section_t section_id); + +/*! + * \brief Put QUESTION in the packet. + * + * \note Since we support QD=1 only, QUESTION is a special type of packet section. + * \note Must not be used after putting RRsets into the packet. + * + * \param pkt + * \param qname + * \param qclass + * \param qtype + * \return KNOT_EOK or various errors + */ +int knot_pkt_put_question(knot_pkt_t *pkt, const knot_dname_t *qname, + uint16_t qclass, uint16_t qtype); + +/*! + * \brief Put RRSet into packet. + * + * \note See compr.h for description on how compression hints work. + * \note Available flags: PF_FREE, KNOT_PF_CHECKDUP, KNOT_PF_NOTRUNC + * + * \param pkt + * \param compr_hint Compression hint, see enum knot_compr_hint or absolute + * position. + * \param rr Given RRSet. + * \param rotate Rotate the RRSet order by this count. + * \param flags RRSet flags (set PF_FREE if you want RRSet to be freed + * with the packet). + * + * \return KNOT_EOK, KNOT_ESPACE, various errors + */ +int knot_pkt_put_rotate(knot_pkt_t *pkt, uint16_t compr_hint, const knot_rrset_t *rr, + uint16_t rotate, uint16_t flags); + +/*! \brief Same as knot_pkt_put_rotate but without rrset rotation. */ +static inline int knot_pkt_put(knot_pkt_t *pkt, uint16_t compr_hint, + const knot_rrset_t *rr, uint16_t flags) +{ + return knot_pkt_put_rotate(pkt, compr_hint, rr, 0, flags); +} + +/*! \brief Get description of the given packet section. */ +static inline const knot_pktsection_t *knot_pkt_section(const knot_pkt_t *pkt, + knot_section_t section_id) +{ + assert(pkt); + return &pkt->sections[section_id]; +} + +/*! \brief Get RRSet from the packet section. */ +static inline const knot_rrset_t *knot_pkt_rr(const knot_pktsection_t *section, + uint16_t i) +{ + assert(section); + return section->pkt->rr + section->pos + i; +} + +/*! \brief Get RRSet offset in the packet wire. */ +static inline uint16_t knot_pkt_rr_offset(const knot_pktsection_t *section, + uint16_t i) +{ + assert(section); + return section->pkt->rr_info[section->pos + i].pos; +} + +/* + * Packet parsing API. + */ + +/*! + * \brief Parse both packet question and payload. + * + * Parses both QUESTION and all packet sections, + * includes semantic checks over specific RRs (TSIG, OPT). + * + * \note If KNOT_PF_KEEPWIRE is set, TSIG RR is not stripped from the wire + * and is processed as any other RR. + * + * \param pkt Given packet. + * \param flags Parsing flags (allowed KNOT_PF_KEEPWIRE) + * + * \retval KNOT_EOK if success. + * \retval KNOT_ETRAIL if success but with some trailing data. + * \retval KNOT_EMALF and other errors. + */ +int knot_pkt_parse(knot_pkt_t *pkt, unsigned flags); + +/*! + * \brief Parse packet header and a QUESTION section. + */ +int knot_pkt_parse_question(knot_pkt_t *pkt); + +/*! + * \brief Get packet extended RCODE. + * + * Extended RCODE is created by considering TSIG RCODE, EDNS RCODE and + * DNS Header RCODE. (See RFC 6895, Section 2.3). + * + * \param pkt Packet to get the response code from. + * + * \return Whole extended RCODE (0 if pkt == NULL). + */ +uint16_t knot_pkt_ext_rcode(const knot_pkt_t *pkt); + +/*! + * \brief Get packet extended RCODE name. + * + * The packet parameter is important as the name depends on TSIG. + * + * \param pkt Packet to get the response code from. + * + * \return RCODE name (or empty string if not known). + */ +const char *knot_pkt_ext_rcode_name(const knot_pkt_t *pkt); + +/*! + * \brief Checks if there is an OPT RR in the packet. + */ +static inline bool knot_pkt_has_edns(const knot_pkt_t *pkt) +{ + assert(pkt); + return pkt->opt_rr != NULL; +} + +/*! + * \brief Checks if TSIG is present. + */ +static inline bool knot_pkt_has_tsig(const knot_pkt_t *pkt) +{ + assert(pkt); + return pkt->tsig_rr != NULL; +} + +/*! + * \brief Checks if DO bit is set in the packet's OPT RR. + */ +static inline bool knot_pkt_has_dnssec(const knot_pkt_t *pkt) +{ + assert(pkt); + return knot_pkt_has_edns(pkt) && knot_edns_do(pkt->opt_rr); +} + +/*! + * \brief Get specific EDNS option from a parsed packet. + */ +static inline uint8_t *knot_pkt_edns_option(const knot_pkt_t *pkt, uint16_t code) +{ + assert(pkt); + if (pkt->edns_opts != NULL && code <= KNOT_EDNS_MAX_OPTION_CODE) { + return pkt->edns_opts->ptr[code]; + } else { + return NULL; + } +} + +/*! + * \brief Computes a reasonable Padding data length for a given packet and opt RR. + * + * \param pkt DNS Packet prepared and otherwise ready to go, no OPT yet added. + * \param opt_rr OPT RR, not yet including padding. + * + * \return Required padding length or -1 if padding not required. + */ +static inline int knot_pkt_default_padding_size(const knot_pkt_t *pkt, + const knot_rrset_t *opt_rr) +{ + if (knot_wire_get_qr(pkt->wire)) { + return knot_edns_alignment_size(pkt->size, knot_rrset_size(opt_rr), + KNOT_EDNS_ALIGNMENT_RESPONSE_DEFAULT); + } else { + return knot_edns_alignment_size(pkt->size, knot_rrset_size(opt_rr), + KNOT_EDNS_ALIGNMENT_QUERY_DEFALT); + } +} + +static inline size_t knot_pkt_size(const knot_pkt_t *pkt) +{ + assert(pkt); + return pkt->size + (knot_pkt_has_tsig(pkt) ? pkt->tsig_wire.len : 0); +} + +/*! @} */ diff --git a/src/libknot/packet/rrset-wire.c b/src/libknot/packet/rrset-wire.c new file mode 100644 index 0000000..dadadd5 --- /dev/null +++ b/src/libknot/packet/rrset-wire.c @@ -0,0 +1,727 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> + +#include "libknot/attribute.h" +#include "libknot/consts.h" +#include "libknot/descriptor.h" +#include "libknot/packet/pkt.h" +#include "libknot/packet/rrset-wire.h" +#include "libknot/rrtype/naptr.h" +#include "libknot/rrtype/rrsig.h" +#include "contrib/macros.h" +#include "contrib/mempattern.h" +#include "contrib/tolower.h" +#include "contrib/wire_ctx.h" + +/*! + * \brief Get maximal size of a domain name in a wire with given capacity. + */ +static uint16_t dname_max(size_t wire_avail) +{ + return MIN(wire_avail, KNOT_DNAME_MAXLEN); +} + +/*! + * \brief Compares two domain name labels. + * + * \param label1 First label. + * \param label2 Second label (may be in upper-case). + * + * \retval true if the labels are identical + * \retval false if the labels are NOT identical + */ +static bool label_is_equal(const uint8_t *label1, const uint8_t *label2) +{ + assert(label1 && label2); + + if (*label1 != *label2) { + return false; + } + + uint8_t len = *label1; + for (uint8_t i = 1; i <= len; i++) { + if (label1[i] != knot_tolower(label2[i])) { + return false; + } + } + + return true; +} + +/*! + * Case insensitive comparison of two dnames in wire format. + * The second name may be compressed in a supplied wire. + */ +static bool dname_equal_wire(const knot_dname_t *d1, const knot_dname_t *d2, + const uint8_t *wire) +{ + assert(d1); + assert(d2); + + d2 = knot_wire_seek_label(d2, wire); + + while (*d1 != '\0' || *d2 != '\0') { + if (!label_is_equal(d1, d2)) { + return false; + } + d1 = knot_wire_next_label(d1, NULL); + d2 = knot_wire_next_label(d2, wire); + } + + return true; +} + +static uint16_t compr_get_ptr(knot_compr_t *compr, uint16_t hint) +{ + if (compr == NULL) { + return 0; + } + + return knot_compr_hint(compr->rrinfo, hint); +} + +static void compr_set_ptr(knot_compr_t *compr, uint16_t hint, + const uint8_t *written_at, uint16_t written_size) +{ + if (compr == NULL) { + return; + } + + assert(written_at >= compr->wire); + + uint16_t offset = written_at - compr->wire; + + knot_compr_hint_set(compr->rrinfo, hint, offset, written_size); +} + +static int write_rdata_fixed(const uint8_t **src, size_t *src_avail, + uint8_t **dst, size_t *dst_avail, size_t size) +{ + assert(src && *src); + assert(src_avail); + assert(dst && *dst); + assert(dst_avail); + + // Check input/output buffer boundaries. + if (size > *src_avail) { + return KNOT_EMALF; + } + + if (size > *dst_avail) { + return KNOT_ESPACE; + } + + // Data binary copy. + memcpy(*dst, *src, size); + + // Update buffers. + *src += size; + *src_avail -= size; + + *dst += size; + *dst_avail -= size; + + return KNOT_EOK; +} + +static int write_rdata_naptr_header(const uint8_t **src, size_t *src_avail, + uint8_t **dst, size_t *dst_avail) +{ + assert(src && *src); + assert(src_avail); + assert(dst && *dst); + assert(dst_avail); + + int ret = knot_naptr_header_size(*src, *src + *src_avail); + if (ret < 0) { + return ret; + } + + // Copy the data. + return write_rdata_fixed(src, src_avail, dst, dst_avail, ret); +} + +/*! \brief Helper for \ref compr_put_dname, writes label(s) with size checks. */ +#define WRITE_LABEL(dst, written, label, max, len) \ + if ((written) + (len) > (max)) { \ + return KNOT_ESPACE; \ + } else { \ + memcpy((dst) + (written), (label), (len)); \ + written += (len); \ + } + +/*! + * \brief Write compressed domain name to the destination wire. + * + * \param dname Name to be written. + * \param dst Destination wire. + * \param max Maximum number of bytes available. + * \param compr Compression context (NULL for no compression) + * \return Number of written bytes or an error. + */ +static int compr_put_dname(const knot_dname_t *dname, uint8_t *dst, uint16_t max, + knot_compr_t *compr) +{ + assert(dname && dst); + + // Write uncompressible names directly (zero label dname). + if (compr == NULL || *dname == '\0') { + return knot_dname_to_wire(dst, dname, max); + } + + // Get number of labels (should not be a zero label dname). + size_t name_labels = knot_dname_labels(dname, NULL); + assert(name_labels > 0); + + // Suffix must not be longer than whole name. + const knot_dname_t *suffix = compr->wire + compr->suffix.pos; + int suffix_labels = compr->suffix.labels; + while (suffix_labels > name_labels) { + suffix = knot_wire_next_label(suffix, compr->wire); + --suffix_labels; + } + + // Suffix is shorter than name, write labels until aligned. + uint8_t orig_labels = name_labels; + uint16_t written = 0; + while (name_labels > suffix_labels) { + WRITE_LABEL(dst, written, dname, max, (*dname + 1)); + dname = knot_wire_next_label(dname, NULL); + --name_labels; + } + + // Label count is now equal. + assert(name_labels == suffix_labels); + const knot_dname_t *match_begin = dname; + const knot_dname_t *compr_ptr = suffix; + while (dname[0] != '\0') { + // Next labels. + const knot_dname_t *next_dname = knot_wire_next_label(dname, NULL); + const knot_dname_t *next_suffix = knot_wire_next_label(suffix, compr->wire); + + // Two labels match, extend suffix length. + if (!label_is_equal(dname, suffix)) { + // If they don't match, write unmatched labels. + uint16_t mismatch_len = (dname - match_begin) + (*dname + 1); + WRITE_LABEL(dst, written, match_begin, max, mismatch_len); + // Start new potential match. + match_begin = next_dname; + compr_ptr = next_suffix; + } + + // Jump to next labels. + dname = next_dname; + suffix = next_suffix; + } + + // If match begins at the end of the name, write '\0' label. + if (match_begin == dname) { + WRITE_LABEL(dst, written, dname, max, 1); + } else { + // Match covers >0 labels, write out compression pointer. + if (written + sizeof(uint16_t) > max) { + return KNOT_ESPACE; + } + knot_wire_put_pointer(dst + written, compr_ptr - compr->wire); + written += sizeof(uint16_t); + } + + assert(dst >= compr->wire); + size_t wire_pos = dst - compr->wire; + assert(wire_pos < KNOT_WIRE_MAX_PKTSIZE); + + // Heuristics - expect similar names are grouped together. + if (written > sizeof(uint16_t) && wire_pos + written < KNOT_WIRE_PTR_MAX) { + compr->suffix.pos = wire_pos; + compr->suffix.labels = orig_labels; + } + + return written; +} + +#define WRITE_OWNER_CHECK(size, dst_avail) \ + if ((size) > *(dst_avail)) { \ + return KNOT_ESPACE; \ + } + +#define WRITE_OWNER_INCR(dst, dst_avail, size) \ + *(dst) += (size); \ + *(dst_avail) -= (size); + +static int write_owner(const knot_rrset_t *rrset, uint8_t **dst, size_t *dst_avail, + knot_compr_t *compr) +{ + assert(rrset); + assert(dst && *dst); + assert(dst_avail); + + // Check for zero label owner (don't compress). + uint16_t owner_pointer = 0; + if (*rrset->owner != '\0') { + owner_pointer = compr_get_ptr(compr, KNOT_COMPR_HINT_OWNER); + } + + // Write result. + if (owner_pointer > 0) { + WRITE_OWNER_CHECK(sizeof(uint16_t), dst_avail); + knot_wire_put_pointer(*dst, owner_pointer); + WRITE_OWNER_INCR(dst, dst_avail, sizeof(uint16_t)); + // Check for coincidence with previous RR set. + } else if (compr != NULL && compr->suffix.pos != 0 && *rrset->owner != '\0' && + dname_equal_wire(rrset->owner, compr->wire + compr->suffix.pos, + compr->wire)) { + WRITE_OWNER_CHECK(sizeof(uint16_t), dst_avail); + knot_wire_put_pointer(*dst, compr->suffix.pos); + compr_set_ptr(compr, KNOT_COMPR_HINT_OWNER, + compr->wire + compr->suffix.pos, + knot_dname_size(rrset->owner)); + WRITE_OWNER_INCR(dst, dst_avail, sizeof(uint16_t)); + } else { + if (compr != NULL) { + compr->suffix.pos = KNOT_WIRE_HEADER_SIZE; + compr->suffix.labels = + knot_dname_labels(compr->wire + compr->suffix.pos, + compr->wire); + } + // WRITE_OWNER_CHECK not needed, compr_put_dname has a check. + int written = compr_put_dname(rrset->owner, *dst, + dname_max(*dst_avail), compr); + if (written < 0) { + return written; + } + + compr_set_ptr(compr, KNOT_COMPR_HINT_OWNER, *dst, written); + WRITE_OWNER_INCR(dst, dst_avail, written); + } + + return KNOT_EOK; +} + +static int write_fixed_header(const knot_rrset_t *rrset, uint16_t rrset_index, + uint8_t **dst, size_t *dst_avail, uint16_t flags) +{ + assert(rrset); + assert(rrset_index < rrset->rrs.count); + assert(dst && *dst); + assert(dst_avail); + + // Write header. + wire_ctx_t write = wire_ctx_init(*dst, *dst_avail); + + wire_ctx_write_u16(&write, rrset->type); + wire_ctx_write_u16(&write, rrset->rclass); + + if ((flags & KNOT_PF_ORIGTTL) && rrset->type == KNOT_RRTYPE_RRSIG) { + const knot_rdata_t *rdata = knot_rdataset_at(&rrset->rrs, rrset_index); + wire_ctx_write_u32(&write, knot_rrsig_original_ttl(rdata)); + } else { + wire_ctx_write_u32(&write, rrset->ttl); + } + + // Check write. + if (write.error != KNOT_EOK) { + return write.error; + } + + // Update buffer. + *dst = write.position; + *dst_avail = wire_ctx_available(&write); + + return KNOT_EOK; +} + +static int compress_rdata_dname(const uint8_t **src, size_t *src_avail, + uint8_t **dst, size_t *dst_avail, + knot_compr_t *put_compr, knot_compr_t *compr, + uint16_t hint) +{ + assert(src && *src); + assert(src_avail); + assert(dst && *dst); + assert(dst_avail); + + // Source domain name. + const knot_dname_t *dname = *src; + size_t dname_size = knot_dname_size(dname); + + // Output domain name. + int written = compr_put_dname(dname, *dst, dname_max(*dst_avail), put_compr); + if (written < 0) { + return written; + } + + // Update compression hints. + if (compr_get_ptr(compr, hint) == 0) { + compr_set_ptr(compr, hint, *dst, written); + } + + // Update buffers. + *dst += written; + *dst_avail -= written; + + *src += dname_size; + *src_avail -= dname_size; + + return KNOT_EOK; +} + +static int rdata_traverse_write(const uint8_t **src, size_t *src_avail, + uint8_t **dst, size_t *dst_avail, + const knot_rdata_descriptor_t *desc, + knot_compr_t *compr, uint16_t hint) +{ + for (const int *type = desc->block_types; *type != KNOT_RDATA_WF_END; type++) { + int ret; + knot_compr_t *put_compr = NULL; + switch (*type) { + case KNOT_RDATA_WF_COMPRESSIBLE_DNAME: + put_compr = compr; + // FALLTHROUGH + case KNOT_RDATA_WF_DECOMPRESSIBLE_DNAME: + case KNOT_RDATA_WF_FIXED_DNAME: + ret = compress_rdata_dname(src, src_avail, dst, dst_avail, + put_compr, compr, hint); + break; + case KNOT_RDATA_WF_NAPTR_HEADER: + ret = write_rdata_naptr_header(src, src_avail, dst, dst_avail); + break; + case KNOT_RDATA_WF_REMAINDER: + ret = write_rdata_fixed(src, src_avail, dst, dst_avail, *src_avail); + break; + default: + // Fixed size block. + assert(*type > 0); + ret = write_rdata_fixed(src, src_avail, dst, dst_avail, *type); + break; + } + if (ret != KNOT_EOK) { + return ret; + } + } + + return KNOT_EOK; +} + +static int write_rdata(const knot_rrset_t *rrset, uint16_t rrset_index, + uint8_t **dst, size_t *dst_avail, knot_compr_t *compr) +{ + assert(rrset); + assert(rrset_index < rrset->rrs.count); + assert(dst && *dst); + assert(dst_avail); + + const knot_rdata_t *rdata = knot_rdataset_at(&rrset->rrs, rrset_index); + + // Reserve space for RDLENGTH. + if (sizeof(uint16_t) > *dst_avail) { + return KNOT_ESPACE; + } + + uint8_t *wire_rdlength = *dst; + *dst += sizeof(uint16_t); + *dst_avail -= sizeof(uint16_t); + uint8_t *wire_rdata_begin = *dst; + + // Write RDATA. + const uint8_t *src = rdata->data; + size_t src_avail = rdata->len; + if (src_avail > 0) { + // Only write non-empty data. + const knot_rdata_descriptor_t *desc = + knot_get_rdata_descriptor(rrset->type); + int ret = rdata_traverse_write(&src, &src_avail, dst, dst_avail, + desc, compr, KNOT_COMPR_HINT_RDATA + rrset_index); + if (ret != KNOT_EOK) { + return ret; + } + } + + // Check for trailing data in the message. + if (src_avail > 0) { + return KNOT_EMALF; + } + + // Write final RDLENGTH. + size_t rdlength = *dst - wire_rdata_begin; + knot_wire_write_u16(wire_rdlength, rdlength); + + return KNOT_EOK; +} + +static int write_rr(const knot_rrset_t *rrset, uint16_t rrset_index, uint8_t **dst, + size_t *dst_avail, knot_compr_t *compr, uint16_t flags) +{ + int ret = write_owner(rrset, dst, dst_avail, compr); + if (ret != KNOT_EOK) { + return ret; + } + + ret = write_fixed_header(rrset, rrset_index, dst, dst_avail, flags); + if (ret != KNOT_EOK) { + return ret; + } + + return write_rdata(rrset, rrset_index, dst, dst_avail, compr); +} + +_public_ +int knot_rrset_to_wire_extra(const knot_rrset_t *rrset, uint8_t *wire, + uint16_t max_size, uint16_t rotate, + knot_compr_t *compr, uint16_t flags) +{ + if (rrset == NULL || wire == NULL) { + return KNOT_EINVAL; + } + if (rrset->rrs.count == 0) { + return 0; + } + if (rotate != 0) { + rotate %= rrset->rrs.count; + } + + uint8_t *write = wire; + size_t capacity = max_size; + + uint16_t count = rrset->rrs.count; + for (uint16_t i = rotate; i < count + rotate; i++) { + uint16_t pos = (i < count) ? i : (i - count); + int ret = write_rr(rrset, pos, &write, &capacity, compr, flags); + if (ret != KNOT_EOK) { + return ret; + } + } + + return write - wire; +} + +_public_ +int knot_rrset_to_wire_rotate(const knot_rrset_t *rrset, uint8_t *wire, + uint16_t max_size, uint16_t rotate, + knot_compr_t *compr) +{ + return knot_rrset_to_wire_extra(rrset, wire, max_size, rotate, compr, 0); +} + +static int parse_header(const uint8_t *wire, size_t *pos, size_t pkt_size, + knot_mm_t *mm, knot_rrset_t *rrset, uint16_t *rdlen) +{ + assert(wire); + assert(pos); + assert(rrset); + assert(rdlen); + + wire_ctx_t src = wire_ctx_init_const(wire, pkt_size); + wire_ctx_set_offset(&src, *pos); + + int compr_size = knot_dname_wire_check(src.position, wire + pkt_size, wire); + if (compr_size <= 0) { + return KNOT_EMALF; + } + + uint8_t buff[KNOT_DNAME_MAXLEN]; + int decompr_size = knot_dname_unpack(buff, src.position, sizeof(buff), wire); + if (decompr_size <= 0) { + return KNOT_EMALF; + } + + knot_dname_t *owner = mm_alloc(mm, decompr_size); + if (owner == NULL) { + return KNOT_ENOMEM; + } + memcpy(owner, buff, decompr_size); + wire_ctx_skip(&src, compr_size); + + uint16_t type = wire_ctx_read_u16(&src); + uint16_t rclass = wire_ctx_read_u16(&src); + uint32_t ttl = wire_ctx_read_u32(&src); + *rdlen = wire_ctx_read_u16(&src); + + if (src.error != KNOT_EOK) { + knot_dname_free(owner, mm); + return KNOT_EMALF; + } + + if (wire_ctx_available(&src) < *rdlen) { + knot_dname_free(owner, mm); + return KNOT_EMALF; + } + + *pos = wire_ctx_offset(&src); + + knot_rrset_init(rrset, owner, type, rclass, ttl); + + return KNOT_EOK; +} + +static int decompress_rdata_dname(const uint8_t **src, size_t *src_avail, + uint8_t **dst, size_t *dst_avail, + const uint8_t *pkt_wire) +{ + assert(src && *src); + assert(src_avail); + assert(dst && *dst); + assert(dst_avail); + + int compr_size = knot_dname_wire_check(*src, *src + *src_avail, pkt_wire); + if (compr_size <= 0) { + return compr_size; + } + + int decompr_size = knot_dname_unpack(*dst, *src, *dst_avail, pkt_wire); + if (decompr_size <= 0) { + return decompr_size; + } + + // Update buffers. + *dst += decompr_size; + *dst_avail -= decompr_size; + + *src += compr_size; + *src_avail -= compr_size; + + return KNOT_EOK; +} + +static int rdata_traverse_parse(const uint8_t **src, size_t *src_avail, + uint8_t **dst, size_t *dst_avail, + const knot_rdata_descriptor_t *desc, + const uint8_t *pkt_wire) +{ + for (const int *type = desc->block_types; *type != KNOT_RDATA_WF_END; type++) { + int ret; + switch (*type) { + case KNOT_RDATA_WF_COMPRESSIBLE_DNAME: + case KNOT_RDATA_WF_DECOMPRESSIBLE_DNAME: + case KNOT_RDATA_WF_FIXED_DNAME: + ret = decompress_rdata_dname(src, src_avail, dst, dst_avail, + pkt_wire); + break; + case KNOT_RDATA_WF_NAPTR_HEADER: + ret = write_rdata_naptr_header(src, src_avail, dst, dst_avail); + break; + case KNOT_RDATA_WF_REMAINDER: + ret = write_rdata_fixed(src, src_avail, dst, dst_avail, *src_avail); + break; + default: + /* Fixed size block */ + assert(*type > 0); + ret = write_rdata_fixed(src, src_avail, dst, dst_avail, *type); + break; + } + if (ret != KNOT_EOK) { + return ret; + } + } + + return KNOT_EOK; +} + +static bool allow_zero_rdata(const knot_rrset_t *rr, + const knot_rdata_descriptor_t *desc) +{ + return rr->rclass != KNOT_CLASS_IN || // NONE and ANY for DDNS + rr->type == KNOT_RRTYPE_APL || // APL RR type + desc->type_name == NULL; // Unknown RR type +} + +static int parse_rdata(const uint8_t *pkt_wire, size_t *pos, size_t pkt_size, + knot_mm_t *mm, uint16_t rdlength, knot_rrset_t *rrset) +{ + assert(pkt_wire); + assert(pos); + assert(rrset); + + const knot_rdata_descriptor_t *desc = knot_get_rdata_descriptor(rrset->type); + if (desc->type_name == NULL) { + desc = knot_get_obsolete_rdata_descriptor(rrset->type); + } + + if (rdlength == 0) { + if (allow_zero_rdata(rrset, desc)) { + return knot_rrset_add_rdata(rrset, NULL, 0, mm); + } else { + return KNOT_EMALF; + } + } else if (pkt_size - *pos < rdlength) { + return KNOT_EMALF; + } + + // Buffer for parsed rdata (decompression extends rdata length). + const size_t max_rdata_len = UINT16_MAX; + uint8_t buf[knot_rdata_size(max_rdata_len)]; + knot_rdata_t *rdata = (knot_rdata_t *)buf; + + const uint8_t *src = pkt_wire + *pos; + size_t src_avail = rdlength; + uint8_t *dst = rdata->data; + size_t dst_avail = max_rdata_len; + + // Parse RDATA. + int ret = rdata_traverse_parse(&src, &src_avail, &dst, &dst_avail, desc, pkt_wire); + if (ret != KNOT_EOK) { + return KNOT_EMALF; + } + + // Check for trailing data. + size_t real_len = max_rdata_len - dst_avail; + if (real_len < rdlength) { + return KNOT_EMALF; + } + rdata->len = real_len; + + ret = knot_rdataset_add(&rrset->rrs, rdata, mm); + if (ret != KNOT_EOK) { + return ret; + } + + // Update position pointer. + *pos += rdlength; + + return KNOT_EOK; +} + +_public_ +int knot_rrset_rr_from_wire(const uint8_t *wire, size_t *pos, size_t max_size, + knot_rrset_t *rrset, knot_mm_t *mm, bool canonical) +{ + if (wire == NULL || pos == NULL || *pos > max_size || rrset == NULL) { + return KNOT_EINVAL; + } + + uint16_t rdlen = 0; + int ret = parse_header(wire, pos, max_size, mm, rrset, &rdlen); + if (ret != KNOT_EOK) { + return ret; + } + + ret = parse_rdata(wire, pos, max_size, mm, rdlen, rrset); + if (ret != KNOT_EOK) { + knot_rrset_clear(rrset, mm); + return ret; + } + + // Convert RR to the canonical format. + if (canonical) { + ret = knot_rrset_rr_to_canonical(rrset); + if (ret != KNOT_EOK) { + knot_rrset_clear(rrset, mm); + } + } + + return KNOT_EOK; +} diff --git a/src/libknot/packet/rrset-wire.h b/src/libknot/packet/rrset-wire.h new file mode 100644 index 0000000..e4370fd --- /dev/null +++ b/src/libknot/packet/rrset-wire.h @@ -0,0 +1,73 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \file + * + * \brief RRSet from/to wire conversion functions. + * + * \addtogroup wire + * @{ + */ + +#pragma once + +#include "libknot/rrset.h" +#include "libknot/packet/compr.h" + +/*! + * \brief Write RR Set content to a wire. + * + * \param rrset RRSet to be converted. + * \param wire Output wire buffer. + * \param max_size Capacity of wire buffer. + * \param rotate Rotate the RR order by this count. + * \param compr Compression context. + * \param flags Flags; currently only KNOT_PF_TTL_ORIG is accepted. + * + * \return Output size, negative number on error (KNOT_E*). + */ +int knot_rrset_to_wire_extra(const knot_rrset_t *rrset, uint8_t *wire, + uint16_t max_size, uint16_t rotate, + knot_compr_t *compr, uint16_t flags); + +/* TODO: remove in next major version. */ +int knot_rrset_to_wire_rotate(const knot_rrset_t *rrset, uint8_t *wire, + uint16_t max_size, uint16_t rotate, + knot_compr_t *compr); + +/*! \brief Same as knot_rrset_to_wire_extra but without rrset rotation and flags. */ +static inline int knot_rrset_to_wire(const knot_rrset_t *rrset, uint8_t *wire, + uint16_t max_size, knot_compr_t *compr) +{ + return knot_rrset_to_wire_extra(rrset, wire, max_size, 0, compr, 0); +} + +/*! +* \brief Creates one RR from wire, stores it into \a rrset. +* +* \param wire Source wire (the whole packet). +* \param pos Position in \a wire where to start parsing. +* \param max_size Total size of data in \a wire (size of the packet). +* \param rrset Destination RRSet. +* \param mm Memory context. +* \param canonical Convert rrset to canonical format indication. +* +* \return KNOT_E* +*/ +int knot_rrset_rr_from_wire(const uint8_t *wire, size_t *pos, size_t max_size, + knot_rrset_t *rrset, knot_mm_t *mm, bool canonical); + +/*! @} */ diff --git a/src/libknot/packet/wire.h b/src/libknot/packet/wire.h new file mode 100644 index 0000000..26bc80e --- /dev/null +++ b/src/libknot/packet/wire.h @@ -0,0 +1,980 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \file + * + * \brief Functions for manipulating and parsing raw data in DNS packets. + * + * \addtogroup wire + * @{ + */ + +#pragma once + +#include <assert.h> +#include <stdint.h> + +#include "libknot/attribute.h" +#include "libknot/wire.h" + +/*! \brief Offset of DNS header fields in wireformat. */ +enum knot_wire_offsets { + KNOT_WIRE_OFFSET_ID = 0, + KNOT_WIRE_OFFSET_FLAGS1 = 2, + KNOT_WIRE_OFFSET_FLAGS2 = 3, + KNOT_WIRE_OFFSET_QDCOUNT = 4, + KNOT_WIRE_OFFSET_ANCOUNT = 6, + KNOT_WIRE_OFFSET_NSCOUNT = 8, + KNOT_WIRE_OFFSET_ARCOUNT = 10 +}; + +/*! \brief Minimum size for some parts of the DNS packet. */ +enum knot_wire_sizes { + KNOT_WIRE_HEADER_SIZE = 12, + KNOT_WIRE_QUESTION_MIN_SIZE = 5, + KNOT_WIRE_RR_MIN_SIZE = 11, + KNOT_WIRE_MIN_PKTSIZE = 512, + KNOT_WIRE_MAX_PKTSIZE = 65535, + KNOT_WIRE_MAX_PAYLOAD = KNOT_WIRE_MAX_PKTSIZE + - KNOT_WIRE_HEADER_SIZE + - KNOT_WIRE_QUESTION_MIN_SIZE +}; + +/* + * Packet header manipulation functions. + */ + +/*! + * \brief Returns the ID from wire format of the packet. + * + * \param packet Wire format of the packet. + * + * \return DNS packet ID. + */ +static inline uint16_t knot_wire_get_id(const uint8_t *packet) +{ + return knot_wire_read_u16(packet + KNOT_WIRE_OFFSET_ID); +} + +/*! + * \brief Sets the ID to the wire format of the packet. + * + * \param packet Wire format of the packet. + * \param id DNS packet ID. + */ +static inline void knot_wire_set_id(uint8_t *packet, uint16_t id) +{ + knot_wire_write_u16(packet + KNOT_WIRE_OFFSET_ID, id); +} + +/*! + * \brief Returns the first byte of flags from wire format of the packet. + * + * \param packet Wire format of the packet. + * + * \return First byte of DNS flags. + */ +static inline uint8_t knot_wire_get_flags1(const uint8_t *packet) +{ + return *(packet + KNOT_WIRE_OFFSET_FLAGS1); +} + +/*! + * \brief Sets the first byte of flags to the wire format of the packet. + * + * \param packet Wire format of the packet. + * \param flags1 First byte of the DNS flags. + */ +static inline uint8_t knot_wire_set_flags1(uint8_t *packet, uint8_t flags1) +{ + return *(packet + KNOT_WIRE_OFFSET_FLAGS1) = flags1; +} + +/*! + * \brief Returns the second byte of flags from wire format of the packet. + * + * \param packet Wire format of the packet. + * + * \return Second byte of DNS flags. + */ +static inline uint8_t knot_wire_get_flags2(const uint8_t *packet) +{ + return *(packet + KNOT_WIRE_OFFSET_FLAGS2); +} + +/*! + * \brief Sets the second byte of flags to the wire format of the packet. + * + * \param packet Wire format of the packet. + * \param flags2 Second byte of the DNS flags. + */ +static inline uint8_t knot_wire_set_flags2(uint8_t *packet, uint8_t flags2) +{ + return *(packet + KNOT_WIRE_OFFSET_FLAGS2) = flags2; +} + +/*! + * \brief Returns the QDCOUNT (count of Question entries) from wire format of + * the packet. + * + * \param packet Wire format of the packet. + * + * \return QDCOUNT (count of Question entries in the packet). + */ +static inline uint16_t knot_wire_get_qdcount(const uint8_t *packet) +{ + return knot_wire_read_u16(packet + KNOT_WIRE_OFFSET_QDCOUNT); +} + +/*! + * \brief Sets the QDCOUNT (count of Question entries) to wire format of the + * packet. + * + * \param packet Wire format of the packet. + * \param qdcount QDCOUNT (count of Question entries in the packet). + */ +static inline void knot_wire_set_qdcount(uint8_t *packet, uint16_t qdcount) +{ + knot_wire_write_u16(packet + KNOT_WIRE_OFFSET_QDCOUNT, qdcount); +} + +/*! + * \brief Adds to QDCOUNT. + */ +static inline void knot_wire_add_qdcount(uint8_t *packet, int16_t n) +{ + knot_wire_write_u16(packet + KNOT_WIRE_OFFSET_QDCOUNT, + knot_wire_get_qdcount(packet) + n); +} + +/*! + * \brief Returns the ANCOUNT (count of Answer entries) from wire format of + * the packet. + * + * \param packet Wire format of the packet. + * + * \return ANCOUNT (count of Answer entries in the packet). + */ +static inline uint16_t knot_wire_get_ancount(const uint8_t *packet) +{ + return knot_wire_read_u16(packet + KNOT_WIRE_OFFSET_ANCOUNT); +} + +/*! + * \brief Sets the ANCOUNT (count of Answer entries) to wire format of the + * packet. + * + * \param packet Wire format of the packet. + * \param ancount ANCOUNT (count of Answer entries in the packet). + */ +static inline void knot_wire_set_ancount(uint8_t *packet, uint16_t ancount) +{ + knot_wire_write_u16(packet + KNOT_WIRE_OFFSET_ANCOUNT, ancount); +} + +/*! + * \brief Adds to ANCOUNT. + */ +static inline void knot_wire_add_ancount(uint8_t *packet, int16_t n) +{ + knot_wire_write_u16(packet + KNOT_WIRE_OFFSET_ANCOUNT, + knot_wire_get_ancount(packet) + n); +} + +/*! + * \brief Returns the NSCOUNT (count of Authority entries) from wire format of + * the packet. + * + * \param packet Wire format of the packet. + * + * \return NSCOUNT (count of Authority entries in the packet). + */ +static inline uint16_t knot_wire_get_nscount(const uint8_t *packet) +{ + return knot_wire_read_u16(packet + KNOT_WIRE_OFFSET_NSCOUNT); +} + +/*! + * \brief Sets the NSCOUNT (count of Authority entries) to wire format of the + * packet. + * + * \param packet Wire format of the packet. + * \param nscount NSCOUNT (count of Authority entries in the packet). + */ +static inline void knot_wire_set_nscount(uint8_t *packet, uint16_t nscount) +{ + knot_wire_write_u16(packet + KNOT_WIRE_OFFSET_NSCOUNT, nscount); +} + +/*! + * \brief Adds to NSCOUNT. + */ +static inline void knot_wire_add_nscount(uint8_t *packet, int16_t n) +{ + knot_wire_write_u16(packet + KNOT_WIRE_OFFSET_NSCOUNT, + knot_wire_get_nscount(packet) + n); +} + +/*! + * \brief Returns the ARCOUNT (count of Additional entries) from wire format of + * the packet. + * + * \param packet Wire format of the packet. + * + * \return ARCOUNT (count of Additional entries in the packet). + */ +static inline uint16_t knot_wire_get_arcount(const uint8_t *packet) +{ + return knot_wire_read_u16(packet + KNOT_WIRE_OFFSET_ARCOUNT); +} + +/*! + * \brief Sets the ARCOUNT (count of Additional entries) to wire format of the + * packet. + * + * \param packet Wire format of the packet. + * \param arcount ARCOUNT (count of Additional entries in the packet). + */ +static inline void knot_wire_set_arcount(uint8_t *packet, uint16_t arcount) +{ + knot_wire_write_u16(packet + KNOT_WIRE_OFFSET_ARCOUNT, arcount); +} + +/*! + * \brief Adds to ARCOUNT. + */ +static inline void knot_wire_add_arcount(uint8_t *packet, int16_t n) +{ + knot_wire_write_u16(packet + KNOT_WIRE_OFFSET_ARCOUNT, + knot_wire_get_arcount(packet) + n); +} + +/* + * Packet header flags manipulation functions. + */ +/*! \brief Constants for DNS header flags in the first flags byte. */ +enum knot_wire_flags1_consts { + KNOT_WIRE_RD_MASK = (uint8_t)0x01U, /*!< RD bit mask. */ + KNOT_WIRE_RD_SHIFT = 0, /*!< RD bit shift. */ + KNOT_WIRE_TC_MASK = (uint8_t)0x02U, /*!< TC bit mask. */ + KNOT_WIRE_TC_SHIFT = 1, /*!< TC bit shift. */ + KNOT_WIRE_AA_MASK = (uint8_t)0x04U, /*!< AA bit mask. */ + KNOT_WIRE_AA_SHIFT = 2, /*!< AA bit shift. */ + KNOT_WIRE_OPCODE_MASK = (uint8_t)0x78U, /*!< OPCODE mask. */ + KNOT_WIRE_OPCODE_SHIFT = 3, /*!< OPCODE shift. */ + KNOT_WIRE_QR_MASK = (uint8_t)0x80U, /*!< QR bit mask. */ + KNOT_WIRE_QR_SHIFT = 7 /*!< QR bit shift. */ +}; + +/*! \brief Constants for DNS header flags in the second flags byte. */ +enum knot_wire_flags2_consts { + KNOT_WIRE_RCODE_MASK = (uint8_t)0x0fU, /*!< RCODE mask. */ + KNOT_WIRE_RCODE_SHIFT = 0, /*!< RCODE shift. */ + KNOT_WIRE_CD_MASK = (uint8_t)0x10U, /*!< CD bit mask. */ + KNOT_WIRE_CD_SHIFT = 4, /*!< CD bit shift. */ + KNOT_WIRE_AD_MASK = (uint8_t)0x20U, /*!< AD bit mask. */ + KNOT_WIRE_AD_SHIFT = 5, /*!< AD bit shift. */ + KNOT_WIRE_Z_MASK = (uint8_t)0x40U, /*!< Zero bit mask. */ + KNOT_WIRE_Z_SHIFT = 6, /*!< Zero bit shift. */ + KNOT_WIRE_RA_MASK = (uint8_t)0x80U, /*!< RA bit mask. */ + KNOT_WIRE_RA_SHIFT = 7 /*!< RA bit shift. */ +}; + +/* + * Functions for getting / setting / clearing flags and codes directly in packet + */ + +/*! + * \brief Returns the RD bit from wire format of the packet. + * + * \param packet Wire format of the packet. + * + * \return Flags with only the RD bit according to its setting in the packet. + */ +static inline uint8_t knot_wire_get_rd(const uint8_t *packet) +{ + return *(packet + KNOT_WIRE_OFFSET_FLAGS1) & KNOT_WIRE_RD_MASK; +} + +/*! + * \brief Sets the RD bit in the wire format of the packet. + * + * \param packet Wire format of the packet. + */ +static inline void knot_wire_set_rd(uint8_t *packet) +{ + *(packet + KNOT_WIRE_OFFSET_FLAGS1) |= KNOT_WIRE_RD_MASK; +} + +/*! + * \brief Clears the RD bit in the wire format of the packet. + * + * \param packet Wire format of the packet. + */ +static inline void knot_wire_clear_rd(uint8_t *packet) +{ + *(packet + KNOT_WIRE_OFFSET_FLAGS1) &= ~KNOT_WIRE_RD_MASK; +} + +/*! + * \brief Returns the TC bit from wire format of the packet. + * + * \param packet Wire format of the packet. + * + * \return Flags with only the TC bit according to its setting in the packet. + */ +static inline uint8_t knot_wire_get_tc(const uint8_t *packet) +{ + return *(packet + KNOT_WIRE_OFFSET_FLAGS1) & KNOT_WIRE_TC_MASK; +} + +/*! + * \brief Sets the TC bit in the wire format of the packet. + * + * \param packet Wire format of the packet. + */ +static inline void knot_wire_set_tc(uint8_t *packet) +{ + *(packet + KNOT_WIRE_OFFSET_FLAGS1) |= KNOT_WIRE_TC_MASK; +} + +/*! + * \brief Clears the TC bit in the wire format of the packet. + * + * \param packet Wire format of the packet. + */ +static inline void knot_wire_clear_tc(uint8_t *packet) +{ + *(packet + KNOT_WIRE_OFFSET_FLAGS1) &= ~KNOT_WIRE_TC_MASK; +} + +/*! + * \brief Returns the AA bit from wire format of the packet. + * + * \param packet Wire format of the packet. + * + * \return Flags with only the AA bit according to its setting in the packet. + */ +static inline uint8_t knot_wire_get_aa(const uint8_t *packet) +{ + return *(packet + KNOT_WIRE_OFFSET_FLAGS1) & KNOT_WIRE_AA_MASK; +} + +/*! + * \brief Sets the AA bit in the wire format of the packet. + * + * \param packet Wire format of the packet. + */ +static inline void knot_wire_set_aa(uint8_t *packet) +{ + *(packet + KNOT_WIRE_OFFSET_FLAGS1) |= KNOT_WIRE_AA_MASK; +} + +/*! + * \brief Clears the AA bit in the wire format of the packet. + * + * \param packet Wire format of the packet. + */ +static inline void knot_wire_clear_aa(uint8_t *packet) +{ + *(packet + KNOT_WIRE_OFFSET_FLAGS1) &= ~KNOT_WIRE_AA_MASK; +} + +/*! + * \brief Returns the OPCODE from wire format of the packet. + * + * \param packet Wire format of the packet. + * + * \return OPCODE of the packet. + */ +static inline uint8_t knot_wire_get_opcode(const uint8_t *packet) +{ + return (*(packet + KNOT_WIRE_OFFSET_FLAGS1) + & KNOT_WIRE_OPCODE_MASK) >> KNOT_WIRE_OPCODE_SHIFT; +} + +/*! + * \brief Sets the OPCODE in the wire format of the packet. + * + * \param packet Wire format of the packet. + * \param opcode OPCODE to set. + */ +static inline void knot_wire_set_opcode(uint8_t *packet, short opcode) +{ + uint8_t *flags1 = packet + KNOT_WIRE_OFFSET_FLAGS1; + *flags1 = (*flags1 & ~KNOT_WIRE_OPCODE_MASK) + | ((opcode) << KNOT_WIRE_OPCODE_SHIFT); +} + +/*! + * \brief Returns the QR bit from wire format of the packet. + * + * \param packet Wire format of the packet. + * + * \return Nonzero for responses and zero for queries. + */ +static inline uint8_t knot_wire_get_qr(const uint8_t *packet) +{ + return *(packet + KNOT_WIRE_OFFSET_FLAGS1) & KNOT_WIRE_QR_MASK; +} + +/*! + * \brief Sets the QR bit in the wire format of the packet. + * + * \param packet Wire format of the packet. + */ +static inline void knot_wire_set_qr(uint8_t *packet) +{ + *(packet + KNOT_WIRE_OFFSET_FLAGS1) |= KNOT_WIRE_QR_MASK; +} + +/*! + * \brief Clears the QR bit in the wire format of the packet. + * + * \param packet Wire format of the packet. + */ +static inline void knot_wire_clear_qr(uint8_t *packet) +{ + *(packet + KNOT_WIRE_OFFSET_FLAGS1) &= ~KNOT_WIRE_QR_MASK; +} + +/*! + * \brief Returns the RCODE from wire format of the packet. + * + * \param packet Wire format of the packet. + * + * \return RCODE of the packet. + */ +static inline uint8_t knot_wire_get_rcode(const uint8_t *packet) +{ + return *(packet + KNOT_WIRE_OFFSET_FLAGS2) + & KNOT_WIRE_RCODE_MASK; +} + +/*! + * \brief Sets the RCODE in the wire format of the packet. + * + * \param packet Wire format of the packet. + * \param rcode RCODE to set. + */ +static inline void knot_wire_set_rcode(uint8_t *packet, short rcode) +{ + uint8_t *flags2 = packet + KNOT_WIRE_OFFSET_FLAGS2; + *flags2 = (*flags2 & ~KNOT_WIRE_RCODE_MASK) | (rcode); +} + +/*! + * \brief Returns the CD bit from wire format of the packet. + * + * \param packet Wire format of the packet. + * + * \return Flags with only the CD bit according to its setting in the packet. + */ +static inline uint8_t knot_wire_get_cd(const uint8_t *packet) +{ + return *(packet + KNOT_WIRE_OFFSET_FLAGS2) & KNOT_WIRE_CD_MASK; +} + +/*! + * \brief Sets the CD bit in the wire format of the packet. + * + * \param packet Wire format of the packet. + */ +static inline void knot_wire_set_cd(uint8_t *packet) +{ + *(packet + KNOT_WIRE_OFFSET_FLAGS2) |= KNOT_WIRE_CD_MASK; +} + +/*! + * \brief Clears the CD bit in the wire format of the packet. + * + * \param packet Wire format of the packet. + */ +static inline void knot_wire_clear_cd(uint8_t *packet) +{ + *(packet + KNOT_WIRE_OFFSET_FLAGS2) &= ~KNOT_WIRE_CD_MASK; +} + +/*! + * \brief Returns the AD bit from wire format of the packet. + * + * \param packet Wire format of the packet. + * + * \return Flags with only the AD bit according to its setting in the packet. + */ +static inline uint8_t knot_wire_get_ad(const uint8_t *packet) +{ + return *(packet + KNOT_WIRE_OFFSET_FLAGS2) & KNOT_WIRE_AD_MASK; +} + +/*! + * \brief Sets the AD bit in the wire format of the packet. + * + * \param packet Wire format of the packet. + */ +static inline void knot_wire_set_ad(uint8_t *packet) +{ + *(packet + KNOT_WIRE_OFFSET_FLAGS2) |= KNOT_WIRE_AD_MASK; +} + +/*! + * \brief Clears the AD bit in the wire format of the packet. + * + * \param packet Wire format of the packet. + */ +static inline void knot_wire_clear_ad(uint8_t *packet) +{ + *(packet + KNOT_WIRE_OFFSET_FLAGS2) &= ~KNOT_WIRE_AD_MASK; +} + +/*! + * \brief Returns the Zero bit from wire format of the packet. + * + * \param packet Wire format of the packet. + * + * \return Flags with only the Zero bit according to its setting in the packet. + */ +static inline uint8_t knot_wire_get_z(const uint8_t *packet) +{ + return *(packet + KNOT_WIRE_OFFSET_FLAGS2) & KNOT_WIRE_Z_MASK; +} + +/*! + * \brief Sets the Zero bit in the wire format of the packet. + * + * \param packet Wire format of the packet. + */ +static inline void knot_wire_set_z(uint8_t *packet) +{ + *(packet + KNOT_WIRE_OFFSET_FLAGS2) |= KNOT_WIRE_Z_MASK; +} + +/*! + * \brief Clears the Zero bit in the wire format of the packet. + * + * \param packet Wire format of the packet. + */ +static inline void knot_wire_clear_z(uint8_t *packet) +{ + *(packet + KNOT_WIRE_OFFSET_FLAGS2) &= ~KNOT_WIRE_Z_MASK; +} + +/*! + * \brief Returns the RA bit from wire format of the packet. + * + * \param packet Wire format of the packet. + * + * \return Flags with only the RA bit according to its setting in the packet. + */ +static inline uint8_t knot_wire_get_ra(const uint8_t *packet) +{ + return *(packet + KNOT_WIRE_OFFSET_FLAGS2) & KNOT_WIRE_RA_MASK; +} + +/*! + * \brief Sets the RA bit in the wire format of the packet. + * + * \param packet Wire format of the packet. + */ +static inline void knot_wire_set_ra(uint8_t *packet) +{ + *(packet + KNOT_WIRE_OFFSET_FLAGS2) |= KNOT_WIRE_RA_MASK; +} + +/*! + * \brief Clears the RA bit in the wire format of the packet. + * + * \param packet Wire format of the packet. + */ +static inline void knot_wire_clear_ra(uint8_t *packet) +{ + *(packet + KNOT_WIRE_OFFSET_FLAGS2) &= ~KNOT_WIRE_RA_MASK; +} + +/* + * Functions for getting / setting / clearing flags in flags variable + */ + +/*! + * \brief Returns the RD bit from the first byte of flags. + * + * \param flags1 First byte of DNS header flags. + * + * \return Flags byte with only the RD bit according to its setting in + * \a flags1. + */ +static inline uint8_t knot_wire_flags_get_rd(uint8_t flags1) +{ + return flags1 & KNOT_WIRE_RD_MASK; +} + +/*! + * \brief Sets the RD bit in the first byte of flags. + * + * \param flags1 First byte of DNS header flags. + */ +static inline void knot_wire_flags_set_rd(uint8_t *flags1) +{ + *flags1 |= KNOT_WIRE_RD_MASK; +} + +/*! + * \brief Clears the RD bit in the first byte of flags. + * + * \param flags1 First byte of DNS header flags. + */ +static inline void knot_wire_flags_clear_rd(uint8_t *flags1) +{ + *flags1 &= ~KNOT_WIRE_RD_MASK; +} + +/*! + * \brief Returns the TC bit from the first byte of flags. + * + * \param flags1 First byte of DNS header flags. + * + * \return Flags byte with only the TC bit according to its setting in + * \a flags1. + */ +static inline uint8_t knot_wire_flags_get_tc(uint8_t flags1) +{ + return flags1 & KNOT_WIRE_TC_MASK; +} + +/*! + * \brief Sets the TC bit in the first byte of flags. + * + * \param flags1 First byte of DNS header flags. + */ +static inline void knot_wire_flags_set_tc(uint8_t *flags1) +{ + *flags1 |= KNOT_WIRE_TC_MASK; +} + +/*! + * \brief Clears the TC bit in the first byte of flags. + * + * \param flags1 First byte of DNS header flags. + */ +static inline void knot_wire_flags_clear_tc(uint8_t *flags1) +{ + *flags1 &= ~KNOT_WIRE_TC_MASK; +} + +/*! + * \brief Returns the AA bit from the first byte of flags. + * + * \param flags1 First byte of DNS header flags. + * + * \return Flags byte with only the AA bit according to its setting in + * \a flags1. + */ +static inline uint8_t knot_wire_flags_get_aa(uint8_t flags1) +{ + return flags1 & KNOT_WIRE_AA_MASK; +} + +/*! + * \brief Sets the AA bit in the first byte of flags. + * + * \param flags1 First byte of DNS header flags. + */ +static inline void knot_wire_flags_set_aa(uint8_t *flags1) +{ + *flags1 |= KNOT_WIRE_AA_MASK; +} + +/*! + * \brief Clears the AA bit in the first byte of flags. + * + * \param flags1 First byte of DNS header flags. + */ +static inline void knot_wire_flags_clear_aa(uint8_t *flags1) +{ + *flags1 &= ~KNOT_WIRE_AA_MASK; +} + +/*! + * \brief Returns the OPCODE from the first byte of flags. + * + * \param flags1 First byte of DNS header flags. + * + * \return OPCODE + */ +static inline uint8_t knot_wire_flags_get_opcode(uint8_t flags1) +{ + return (flags1 & KNOT_WIRE_OPCODE_MASK) + >> KNOT_WIRE_OPCODE_SHIFT; +} + +/*! + * \brief Sets the OPCODE in the first byte of flags. + * + * \param flags1 First byte of DNS header flags. + * \param opcode OPCODE to set. + */ +static inline void knot_wire_flags_set_opcode(uint8_t *flags1, short opcode) +{ + *flags1 = (*flags1 & ~KNOT_WIRE_OPCODE_MASK) + | ((opcode) << KNOT_WIRE_OPCODE_SHIFT); +} + +/*! + * \brief Returns the QR bit from the first byte of flags. + * + * \param flags1 First byte of DNS header flags. + * + * \return Flags byte with only the QR bit according to its setting in + * \a flags1. + */ +static inline uint8_t knot_wire_flags_get_qr(uint8_t flags1) +{ + return flags1 & KNOT_WIRE_QR_MASK; +} + +/*! + * \brief Sets the QR bit in the first byte of flags. + * + * \param flags1 First byte of DNS header flags. + */ +static inline void knot_wire_flags_set_qr(uint8_t *flags1) +{ + *flags1 |= KNOT_WIRE_QR_MASK; +} + +/*! + * \brief Clears the QR bit in the first byte of flags. + * + * \param flags1 First byte of DNS header flags. + */ +static inline void knot_wire_flags_clear_qr(uint8_t *flags1) +{ + *flags1 &= ~KNOT_WIRE_QR_MASK; +} + +/*! + * \brief Returns the RCODE from the second byte of flags. + * + * \param flags2 First byte of DNS header flags. + * + * \return RCODE + */ +static inline uint8_t knot_wire_flags_get_rcode(uint8_t flags2) +{ + return flags2 & KNOT_WIRE_RCODE_MASK; +} + +/*! + * \brief Sets the RCODE in the second byte of flags. + * + * \param flags2 Second byte of DNS header flags. + * \param rcode RCODE to set. + */ +static inline void knot_wire_flags_set_rcode(uint8_t *flags2, short rcode) +{ + *flags2 = (*flags2 & ~KNOT_WIRE_RCODE_MASK) | (rcode); +} + +/*! + * \brief Returns the CD bit from the second byte of flags. + * + * \param flags2 Second byte of DNS header flags. + * + * \return Flags byte with only the CD bit according to its setting in + * \a flags2. + */ +static inline uint8_t knot_wire_flags_get_cd(uint8_t flags2) +{ + return flags2 & KNOT_WIRE_CD_MASK; +} + +/*! + * \brief Sets the CD bit in the second byte of flags. + * + * \param flags2 Second byte of DNS header flags. + */ +static inline void knot_wire_flags_set_cd(uint8_t *flags2) +{ + *flags2 |= KNOT_WIRE_CD_MASK; +} + +/*! + * \brief Clears the CD bit in the second byte of flags. + * + * \param flags2 Second byte of DNS header flags. + */ +static inline void knot_wire_flags_clear_cd(uint8_t *flags2) +{ + *flags2 &= ~KNOT_WIRE_CD_MASK; +} + +/*! + * \brief Returns the AD bit from the second byte of flags. + * + * \param flags2 Second byte of DNS header flags. + * + * \return Flags byte with only the AD bit according to its setting in + * \a flags2. + */ +static inline uint8_t knot_wire_flags_get_ad(uint8_t flags2) +{ + return flags2 & KNOT_WIRE_AD_MASK; +} + +/*! + * \brief Sets the AD bit in the second byte of flags. + * + * \param flags2 Second byte of DNS header flags. + */ +static inline void knot_wire_flags_set_ad(uint8_t *flags2) +{ + *flags2 |= KNOT_WIRE_AD_MASK; +} + +/*! + * \brief Clears the AD bit in the second byte of flags. + * + * \param flags2 Second byte of DNS header flags. + */ +static inline void knot_wire_flags_clear_ad(uint8_t *flags2) +{ + *flags2 &= ~KNOT_WIRE_AD_MASK; +} + +/*! + * \brief Returns the Zero bit from the second byte of flags. + * + * \param flags2 Second byte of DNS header flags. + * + * \return Flags byte with only the Zero bit according to its setting in + * \a flags2. + */ +static inline uint8_t knot_wire_flags_get_z(uint8_t flags2) +{ + return flags2 & KNOT_WIRE_Z_MASK; +} + +/*! + * \brief Sets the Zero bit in the second byte of flags. + * + * \param flags2 Second byte of DNS header flags. + */ +static inline void knot_wire_flags_set_z(uint8_t *flags2) +{ + *flags2 |= KNOT_WIRE_Z_MASK; +} + +/*! + * \brief Clears the Zero bit in the second byte of flags. + * + * \param flags2 Second byte of DNS header flags. + */ +static inline void knot_wire_flags_clear_z(uint8_t *flags2) +{ + *flags2 &= ~KNOT_WIRE_Z_MASK; +} + +/*! + * \brief Returns the RA bit from the second byte of flags. + * + * \param flags2 Second byte of DNS header flags. + * + * \return Flags byte with only the RA bit according to its setting in + * \a flags2. + */ +static inline uint8_t knot_wire_flags_get_ra(uint8_t flags2) +{ + return flags2 & KNOT_WIRE_RA_MASK; +} + +/*! + * \brief Sets the RA bit in the second byte of flags. + * + * \param flags2 Second byte of DNS header flags. + */ +static inline void knot_wire_flags_set_ra(uint8_t *flags2) +{ + *flags2 |= KNOT_WIRE_RA_MASK; +} + +/*! + * \brief Clears the RA bit in the second byte of flags. + * + * \param flags2 Second byte of DNS header flags. + */ +static inline void knot_wire_flags_clear_ra(uint8_t *flags2) +{ + *flags2 &= ~KNOT_WIRE_RA_MASK; +} + +/* + * Pointer manipulation + */ + +enum knot_wire_pointer_consts { + /*! \brief DNS packet pointer designation (first two bits set to 1). */ + KNOT_WIRE_PTR = (uint8_t)0xC0, + /*! \brief DNS packet minimal pointer (KNOT_WIRE_PTR + 1 zero byte). */ + KNOT_WIRE_PTR_BASE = (uint16_t)0xC000, + /*! \brief DNS packet maximal offset (KNOT_WIRE_BASE complement). */ + KNOT_WIRE_PTR_MAX = (uint16_t)0x3FFF +}; + +static inline int knot_wire_is_pointer(const uint8_t *pos) +{ + return pos && ((pos[0] & KNOT_WIRE_PTR) == KNOT_WIRE_PTR); +} + +/*! + * \brief Creates a DNS packet pointer and stores it in wire format. + * + * \param pos Position where tu put the pointer. + * \param ptr Relative position of the item to which the pointer should point in + * the wire format of the packet. + */ +static inline void knot_wire_put_pointer(uint8_t *pos, uint16_t ptr) +{ + knot_wire_write_u16(pos, ptr); // Write pointer offset. + assert((pos[0] & KNOT_WIRE_PTR) == 0); // Check for maximal offset. + pos[0] |= KNOT_WIRE_PTR; // Add pointer mark. +} + +static inline uint16_t knot_wire_get_pointer(const uint8_t *pos) +{ + assert(knot_wire_is_pointer(pos)); // Check pointer. + return (knot_wire_read_u16(pos) - KNOT_WIRE_PTR_BASE); // Return offset. +} + +_pure_ _mustcheck_ +static inline const uint8_t *knot_wire_seek_label(const uint8_t *lp, const uint8_t *wire) +{ + while (knot_wire_is_pointer(lp)) { + if (!wire) + return NULL; + lp = wire + knot_wire_get_pointer(lp); + } + return lp; +} + +_pure_ _mustcheck_ +static inline const uint8_t *knot_wire_next_label(const uint8_t *lp, const uint8_t *wire) +{ + if (!lp || !lp[0]) /* No label after final label. */ + return NULL; + return knot_wire_seek_label(lp + (lp[0] + sizeof(uint8_t)), wire); +} + +/*! @} */ diff --git a/src/libknot/rdata.h b/src/libknot/rdata.h new file mode 100644 index 0000000..e3a818b --- /dev/null +++ b/src/libknot/rdata.h @@ -0,0 +1,97 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \file + * + * \brief API for manipulating rdata. + * + * \addtogroup rr + * @{ + */ + +#pragma once + +#include <assert.h> +#include <stddef.h> +#include <stdint.h> +#include <string.h> + +/*!< \brief Maximum rdata length. */ +#define KNOT_RDATA_MAXLEN 65535 + +/*! + * \brief Structure holding single rdata payload. + */ +typedef struct { + uint16_t len; + uint8_t data[]; +} knot_rdata_t; + +/*! + * \brief Inits rdata structure. + * + * \param rdata Rdata structure to be initialized. At least knot_rdata_size bytes + * must fit into it! + * \param len Rdata length. + * \param data Rdata itself. + */ +inline static void knot_rdata_init(knot_rdata_t *rdata, uint16_t len, const uint8_t *data) +{ + assert(rdata); + rdata->len = len; + if (rdata->len > 0) { + assert(data); + memcpy(rdata->data, data, len); + } +} + +/*! + * \brief Returns actual size of the rdata structure for given rdata length. + * + * \param len Rdata length. + * + * \return Actual structure size. + */ +inline static size_t knot_rdata_size(uint16_t len) +{ + return sizeof(uint16_t) + len + (len & 1); +} + +/*! + * \brief Canonical comparison of two rdata structures. + * + * \param rdata1 First rdata to compare. + * \param rdata2 Second rdata to compare. + * + * \retval = 0 if rdata1 == rdata2. + * \retval < 0 if rdata1 < rdata2. + * \retval > 0 if rdata1 > rdata2. + */ +inline static int knot_rdata_cmp(const knot_rdata_t *rdata1, const knot_rdata_t *rdata2) +{ + assert(rdata1); + assert(rdata2); + + size_t common_len = (rdata1->len <= rdata2->len) ? rdata1->len : rdata2->len; + + int cmp = memcmp(rdata1->data, rdata2->data, common_len); + if (cmp == 0 && rdata1->len != rdata2->len) { + cmp = rdata1->len < rdata2->len ? -1 : 1; + } + return cmp; +} + +/*! @} */ diff --git a/src/libknot/rdataset.c b/src/libknot/rdataset.c new file mode 100644 index 0000000..4767bf9 --- /dev/null +++ b/src/libknot/rdataset.c @@ -0,0 +1,368 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> +#include <string.h> +#include <stdlib.h> + +#include "libknot/attribute.h" +#include "libknot/rdataset.h" +#include "libknot/errcode.h" +#include "contrib/mempattern.h" + +static knot_rdata_t *rr_seek(const knot_rdataset_t *rrs, uint16_t pos) +{ + assert(rrs); + assert(0 < rrs->count); + assert(pos < rrs->count); + + uint8_t *raw = (uint8_t *)(rrs->rdata); + for (uint16_t i = 0; i < pos; ++i) { + raw += knot_rdata_size(((knot_rdata_t *)raw)->len); + } + + return (knot_rdata_t *)raw; +} + +static int find_rr_pos(const knot_rdataset_t *rrs, const knot_rdata_t *rr) +{ + knot_rdata_t *search_rr = rrs->rdata; + for (uint16_t i = 0; i < rrs->count; ++i) { + if (knot_rdata_cmp(rr, search_rr) == 0) { + return i; + } + search_rr = knot_rdataset_next(search_rr); + } + + return KNOT_ENOENT; +} + +static int add_rr_at(knot_rdataset_t *rrs, const knot_rdata_t *rr, uint16_t pos, + knot_mm_t *mm) +{ + assert(rrs); + assert(rr); + assert(pos <= rrs->count); + + if (rrs->count == UINT16_MAX) { + return KNOT_ESPACE; + } + + size_t total_size = knot_rdataset_size(rrs); + size_t new_size = knot_rdata_size(rr->len); + + // Realloc RDATA. + knot_rdata_t *tmp = mm_realloc(mm, rrs->rdata, total_size + new_size, + total_size); + if (tmp == NULL) { + return KNOT_ENOMEM; + } else { + rrs->rdata = tmp; + } + + if (rrs->count == 0 || pos == rrs->count) { + // No need to rearange RDATA. + rrs->count++; + knot_rdata_t *new_rr = rr_seek(rrs, pos); + knot_rdata_init(new_rr, rr->len, rr->data); + return KNOT_EOK; + } + + // RDATA have to be rearanged. + knot_rdata_t *old_rr = rr_seek(rrs, pos); + knot_rdata_t *last_rr = rr_seek(rrs, rrs->count - 1); + + // Make space for new RDATA by moving the array. + uint8_t *dst = (uint8_t *)old_rr + new_size; + assert(old_rr <= last_rr); + size_t len = ((uint8_t *)last_rr - (uint8_t *)old_rr) + + knot_rdata_size(last_rr->len); + memmove(dst, old_rr, len); + + // Set new RDATA. + knot_rdata_init(old_rr, rr->len, rr->data); + rrs->count++; + + return KNOT_EOK; +} + +static int remove_rr_at(knot_rdataset_t *rrs, uint16_t pos, knot_mm_t *mm) +{ + assert(rrs); + assert(0 < rrs->count); + assert(pos < rrs->count); + + knot_rdata_t *old_rr = rr_seek(rrs, pos); + knot_rdata_t *last_rr = rr_seek(rrs, rrs->count - 1); + + size_t total_size = knot_rdataset_size(rrs); + size_t old_size = knot_rdata_size(old_rr->len); + + // Move RDATA. + uint8_t *old_threshold = (uint8_t *)old_rr + old_size; + uint8_t *last_threshold = (uint8_t *)last_rr + knot_rdata_size(last_rr->len); + assert(old_threshold <= last_threshold); + memmove(old_rr, old_threshold, last_threshold - old_threshold); + + if (rrs->count > 1) { + // Realloc RDATA. + knot_rdata_t *tmp = mm_realloc(mm, rrs->rdata, total_size - old_size, + total_size); + if (tmp == NULL) { + return KNOT_ENOMEM; + } else { + rrs->rdata = tmp; + } + } else { + // Free RDATA. + mm_free(mm, rrs->rdata); + rrs->rdata = NULL; + } + rrs->count--; + + return KNOT_EOK; +} + +_public_ +void knot_rdataset_clear(knot_rdataset_t *rrs, knot_mm_t *mm) +{ + if (rrs == NULL) { + return; + } + + mm_free(mm, rrs->rdata); + knot_rdataset_init(rrs); +} + +_public_ +int knot_rdataset_copy(knot_rdataset_t *dst, const knot_rdataset_t *src, knot_mm_t *mm) +{ + if (dst == NULL || src == NULL) { + return KNOT_EINVAL; + } + + dst->count = src->count; + size_t src_size = knot_rdataset_size(src); + dst->rdata = mm_alloc(mm, src_size); + if (dst->rdata == NULL) { + return KNOT_ENOMEM; + } + + memcpy(dst->rdata, src->rdata, src_size); + + return KNOT_EOK; +} + +_public_ +knot_rdata_t *knot_rdataset_at(const knot_rdataset_t *rrs, uint16_t pos) +{ + if (rrs == NULL || rrs->count == 0 || pos >= rrs->count) { + return NULL; + } + + return rr_seek(rrs, pos); +} + +_public_ +size_t knot_rdataset_size(const knot_rdataset_t *rrs) +{ + if (rrs == NULL || rrs->count == 0) { + return 0; + } + + const knot_rdata_t *last = rr_seek(rrs, rrs->count - 1); + return (uint8_t *)last + knot_rdata_size(last->len) - (uint8_t *)rrs->rdata; +} + +_public_ +int knot_rdataset_add(knot_rdataset_t *rrs, const knot_rdata_t *rr, knot_mm_t *mm) +{ + if (rrs == NULL || rr == NULL) { + return KNOT_EINVAL; + } + + // First insert to empty rdataset. + if (rrs->count == 0) { + return add_rr_at(rrs, rr, 0, mm); + } + + for (int i = rrs->count - 1; i >= 0; --i) { + const knot_rdata_t *rrset_rr = rr_seek(rrs, i); + int cmp = knot_rdata_cmp(rrset_rr, rr); + if (cmp == 0) { + // Duplicate - no need to add this RR. + return KNOT_EOK; + } else if (cmp < 0) { + // Found position to insert. + return add_rr_at(rrs, rr, i + 1, mm); + } + } + + // If flow gets here, it means that we should insert at the first position. + return add_rr_at(rrs, rr, 0, mm); +} + +_public_ +int knot_rdataset_reserve(knot_rdataset_t *rrs, uint16_t size, knot_mm_t *mm) +{ + if (rrs == NULL) { + return KNOT_EINVAL; + } else if (rrs->count == UINT16_MAX) { + return KNOT_ESPACE; + } + + size_t old_size = knot_rdataset_size(rrs); + size_t new_size = old_size + knot_rdata_size(size); + + knot_rdata_t *tmp = mm_realloc(mm, rrs->rdata, new_size, old_size); + if (tmp == NULL) { + return KNOT_ENOMEM; + } + rrs->rdata = tmp; + rrs->count++; + + // We have to initialise the 'size' field in the reserved space. + rr_seek(rrs, rrs->count - 1)->len = size; + + return KNOT_EOK; +} + +_public_ +int knot_rdataset_unreserve(knot_rdataset_t *rrs, knot_mm_t *mm) +{ + if (rrs == NULL || rrs->count == 0) { + return KNOT_EINVAL; + } + + return remove_rr_at(rrs, rrs->count - 1, mm); +} + +_public_ +bool knot_rdataset_eq(const knot_rdataset_t *rrs1, const knot_rdataset_t *rrs2) +{ + if (rrs1 == NULL || rrs2 == NULL || rrs1->count != rrs2->count) { + return false; + } + + knot_rdata_t *rr1 = rrs1->rdata; + knot_rdata_t *rr2 = rrs2->rdata; + for (uint16_t i = 0; i < rrs1->count; ++i) { + if (knot_rdata_cmp(rr1, rr2) != 0) { + return false; + } + rr1 = knot_rdataset_next(rr1); + rr2 = knot_rdataset_next(rr2); + } + + return true; +} + +_public_ +bool knot_rdataset_member(const knot_rdataset_t *rrs, const knot_rdata_t *rr) +{ + if (rrs == NULL) { + return false; + } + + knot_rdata_t *cmp_rr = rrs->rdata; + for (uint16_t i = 0; i < rrs->count; ++i) { + int cmp = knot_rdata_cmp(cmp_rr, rr); + if (cmp == 0) { + // Match. + return true; + } else if (cmp > 0) { + // 'Greater' RR present, no need to continue. + return false; + } + cmp_rr = knot_rdataset_next(cmp_rr); + } + + return false; +} + +_public_ +int knot_rdataset_merge(knot_rdataset_t *rrs1, const knot_rdataset_t *rrs2, + knot_mm_t *mm) +{ + if (rrs1 == NULL || rrs2 == NULL) { + return KNOT_EINVAL; + } + + knot_rdata_t *rr2 = rrs2->rdata; + for (uint16_t i = 0; i < rrs2->count; ++i) { + int ret = knot_rdataset_add(rrs1, rr2, mm); + if (ret != KNOT_EOK) { + return ret; + } + rr2 = knot_rdataset_next(rr2); + } + + return KNOT_EOK; +} + +_public_ +int knot_rdataset_intersect(const knot_rdataset_t *rrs1, const knot_rdataset_t *rrs2, + knot_rdataset_t *out, knot_mm_t *mm) +{ + if (rrs1 == NULL || rrs2 == NULL || out == NULL) { + return KNOT_EINVAL; + } + + knot_rdataset_init(out); + knot_rdata_t *rr1 = rrs1->rdata; + for (uint16_t i = 0; i < rrs1->count; ++i) { + if (knot_rdataset_member(rrs2, rr1)) { + // Add RR into output intersection RRSet. + int ret = knot_rdataset_add(out, rr1, mm); + if (ret != KNOT_EOK) { + knot_rdataset_clear(out, mm); + return ret; + } + } + rr1 = knot_rdataset_next(rr1); + } + + return KNOT_EOK; +} + +_public_ +int knot_rdataset_subtract(knot_rdataset_t *from, const knot_rdataset_t *what, + knot_mm_t *mm) +{ + if (from == NULL || what == NULL) { + return KNOT_EINVAL; + } + + if (from->rdata == what->rdata) { + knot_rdataset_clear(from, mm); + knot_rdataset_init((knot_rdataset_t *) what); + return KNOT_EOK; + } + + knot_rdata_t *to_remove = what->rdata; + for (uint16_t i = 0; i < what->count; ++i) { + int pos_to_remove = find_rr_pos(from, to_remove); + if (pos_to_remove >= 0) { + int ret = remove_rr_at(from, pos_to_remove, mm); + if (ret != KNOT_EOK) { + return ret; + } + } + to_remove = knot_rdataset_next(to_remove); + } + + return KNOT_EOK; +} diff --git a/src/libknot/rdataset.h b/src/libknot/rdataset.h new file mode 100644 index 0000000..fa1d4df --- /dev/null +++ b/src/libknot/rdataset.h @@ -0,0 +1,201 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \file + * + * \brief API for manipulating RR arrays. + * + * \addtogroup rr + * @{ + */ + +#pragma once + +#include <assert.h> +#include <stddef.h> +#include <stdint.h> +#include <stdbool.h> + +#include "libknot/mm_ctx.h" +#include "libknot/rdata.h" + +/*!< \brief Set of RRs. */ +typedef struct { + uint16_t count; /*!< \brief Count of RRs stored in the structure. */ + knot_rdata_t *rdata; /*!< \brief Serialized rdata, canonically sorted. */ +} knot_rdataset_t; + +/*! + * \brief Initializes RRS structure. + * + * \param rrs Structure to be initialized. + */ +inline static void knot_rdataset_init(knot_rdataset_t *rrs) +{ + if (rrs != NULL) { + rrs->count = 0; + rrs->rdata = NULL; + } +} + +/*! + * \brief Advance to the next rdata in a rdataset. + * + * Useful for iteration. + * + * \note Ensure that this operation makes sense! + * + * \param rr Current RR. + * + * \return Next RR. + */ +static inline knot_rdata_t *knot_rdataset_next(knot_rdata_t *rr) +{ + assert(rr); + return (knot_rdata_t *)((uint8_t *)rr + knot_rdata_size(rr->len)); +} + +/*! + * \brief Frees data initialized by RRS structure, but not the structure itself. + * + * \param rrs Structure to be cleared. + * \param mm Memory context used to create allocations. + */ +void knot_rdataset_clear(knot_rdataset_t *rrs, knot_mm_t *mm); + +/*! + * \brief Deep copies RRS structure. All data are duplicated. + * + * \param dst Copy destination. + * \param src Copy source. + * \param mm Memory context. + * + * \return KNOT_E* + */ +int knot_rdataset_copy(knot_rdataset_t *dst, const knot_rdataset_t *src, knot_mm_t *mm); + +/*! + * \brief Gets RR from RRS structure, using given position. + * + * \param rrs RRS structure to get RR from. + * \param pos Position to use (counted from 0). + * + * \return Pointer to RR at \a pos position. + */ +knot_rdata_t *knot_rdataset_at(const knot_rdataset_t *rrs, uint16_t pos); + +/*! + * \brief Returns size of the structures holding the RR set. + * + * \param rrs RR array. + * + * \return Array size. + */ +size_t knot_rdataset_size(const knot_rdataset_t *rrs); + +/*! + * \brief Adds single RR into RRS structure. All data are copied. + * + * \param rrs RRS structure to add RR into. + * \param rr RR to add. + * \param mm Memory context. + * + * \return KNOT_E* + */ +int knot_rdataset_add(knot_rdataset_t *rrs, const knot_rdata_t *rr, knot_mm_t *mm); + +/*! + * \brief Reserves space at the end of the RRS structure. + * + * \param rrs RRS structure to reserve space at. + * \param size How much space to reserve. + * \param mm Memory context. + * + * \return KNOT_E* + */ +int knot_rdataset_reserve(knot_rdataset_t *rrs, uint16_t size, knot_mm_t *mm); + +/*! + * \brief Removes the last RR from RRS structure, i.e. does the opposite of _reserve. + * + * \param rrs RRS structure to remove RR from. + * \param mm Memory context. + * + * \return KNOT_E* + */ +int knot_rdataset_unreserve(knot_rdataset_t *rrs, knot_mm_t *mm); + +/*! + * \brief RRS equality check. + * + * \param rrs1 First RRS to be compared. + * \param rrs2 Second RRS to be compared. + * + * \retval true if rrs1 == rrs2. + * \retval false if rrs1 != rrs2. + */ +bool knot_rdataset_eq(const knot_rdataset_t *rrs1, const knot_rdataset_t *rrs2); + +/*! + * \brief Returns true if \a rr is present in \a rrs, false otherwise. + * + * \param rrs RRS to search in. + * \param rr RR to compare with. + * + * \retval true if \a rr is present in \a rrs. + * \retval false if \a rr is not present in \a rrs. + */ +bool knot_rdataset_member(const knot_rdataset_t *rrs, const knot_rdata_t *rr); + +/*! + * \brief Merges two RRS into the first one. Second RRS is left intact. + * Canonical order is preserved. + * + * \param rrs1 Destination RRS (merge here). + * \param rrs2 RRS to be merged (merge from). + * \param mm Memory context. + * + * \return KNOT_E* + */ +int knot_rdataset_merge(knot_rdataset_t *rrs1, const knot_rdataset_t *rrs2, + knot_mm_t *mm); + +/*! + * \brief RRS set-like intersection. Full compare is done. + * + * \param rrs1 First RRS to intersect. + * \param rrs2 Second RRS to intersect. + * \param out Output RRS with intersection, RDATA are created anew. + * \param mm Memory context. Will be used to create new RDATA. + * + * \return KNOT_E* + */ +int knot_rdataset_intersect(const knot_rdataset_t *rrs1, const knot_rdataset_t *rrs2, + knot_rdataset_t *out, knot_mm_t *mm); + +/*! + * \brief Does set-like RRS subtraction. \a from RRS is changed. + * + * \param from RRS to subtract from. + * \param what RRS to subtract. + * \param mm Memory context use to reallocated \a from data. + * + * \return KNOT_E* + */ +int knot_rdataset_subtract(knot_rdataset_t *from, const knot_rdataset_t *what, + knot_mm_t *mm); + +/*! @} */ diff --git a/src/libknot/rrset-dump.c b/src/libknot/rrset-dump.c new file mode 100644 index 0000000..a1c410a --- /dev/null +++ b/src/libknot/rrset-dump.c @@ -0,0 +1,2014 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <arpa/inet.h> +#include <inttypes.h> +#include <math.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <netinet/in.h> +#include <sys/socket.h> + +#include "libdnssec/binary.h" +#include "libdnssec/keytag.h" +#include "libknot/attribute.h" +#include "libknot/rrset-dump.h" +#include "libknot/codes.h" +#include "libknot/consts.h" +#include "libknot/descriptor.h" +#include "libknot/errcode.h" +#include "libknot/lookup.h" +#include "libknot/rrtype/rrsig.h" +#include "libknot/wire.h" +#include "contrib/base32hex.h" +#include "contrib/base64.h" +#include "contrib/ctype.h" +#include "contrib/wire_ctx.h" + +#define RRSET_DUMP_LIMIT (2 * 1024 * 1024) + +#define TAB_WIDTH 8 +#define BLOCK_WIDTH 40 +#define BLOCK_INDENT "\n\t\t\t\t" + +#define LOC_ZERO 2147483648 // 2^31 + +/*! \brief macros with repetitive (mostly error-checking) code of methods from first section of this file */ +#define CHECK_PRET if (p->ret < 0) return; +#define CHECK_INMAX(mininmax) if (p->in_max < (mininmax)) { p->ret = -1; return; } +#define CHECK_RET_OUTMAX_SNPRINTF if (ret <= 0 || (size_t)ret >= p->out_max) { p->ret = -1; return; } +#define STRING_TERMINATION if (p->out_max > 0) { *p->out = '\0'; } else { p->ret = -1; return; } +#define FILL_IN_INPUT(pdata) if (memcpy(&(pdata), p->in, in_len) == NULL) { p->ret = -1; return; } +#define CHECK_RET_POSITIVE if (ret <= 0) { p->ret = -1; return; } + +typedef struct { + const knot_dump_style_t *style; + const uint8_t *in; + size_t in_max; + char *out; + size_t out_max; + size_t total; + int ret; +} rrset_dump_params_t; + +_public_ +const knot_dump_style_t KNOT_DUMP_STYLE_DEFAULT = { + .wrap = false, + .show_class = false, + .show_ttl = true, + .verbose = false, + .original_ttl = true, + .empty_ttl = false, + .human_ttl = false, + .human_tmstamp = true, + .hide_crypto = false, + .ascii_to_idn = NULL +}; + +static void dump_string(rrset_dump_params_t *p, const char *str) +{ + CHECK_PRET + + size_t in_len = strlen(str); + + // Check input size (+ 1 termination). + if (in_len >= p->out_max) { + p->ret = -1; + return; + } + + // Copy string including termination '\0'! + if (memcpy(p->out, str, in_len + 1) == NULL) { + p->ret = -1; + return; + } + + // Fill in output. + p->out += in_len; + p->out_max -= in_len; + p->total += in_len; +} + +static void wire_num8_to_str(rrset_dump_params_t *p) +{ + CHECK_PRET + + uint8_t data = *(p->in); + size_t in_len = sizeof(data); + size_t out_len = 0; + + CHECK_INMAX(in_len) + + // Write number. + int ret = snprintf(p->out, p->out_max, "%u", data); + CHECK_RET_OUTMAX_SNPRINTF + out_len = ret; + + // Fill in output. + p->in += in_len; + p->in_max -= in_len; + p->out += out_len; + p->out_max -= out_len; + p->total += out_len; +} + +static void wire_num16_to_str(rrset_dump_params_t *p) +{ + CHECK_PRET + + uint16_t data; + size_t in_len = sizeof(data); + size_t out_len = 0; + + CHECK_INMAX(in_len) + + // Fill in input data. + data = knot_wire_read_u16(p->in); + + // Write number. + int ret = snprintf(p->out, p->out_max, "%u", data); + CHECK_RET_OUTMAX_SNPRINTF + out_len = ret; + + // Fill in output. + p->in += in_len; + p->in_max -= in_len; + p->out += out_len; + p->out_max -= out_len; + p->total += out_len; +} + +static void wire_num32_to_str(rrset_dump_params_t *p) +{ + CHECK_PRET + + uint32_t data; + size_t in_len = sizeof(data); + size_t out_len = 0; + + CHECK_INMAX(in_len) + + // Fill in input data. + data = knot_wire_read_u32(p->in); + + // Write number. + int ret = snprintf(p->out, p->out_max, "%u", data); + CHECK_RET_OUTMAX_SNPRINTF + out_len = ret; + + // Fill in output. + p->in += in_len; + p->in_max -= in_len; + p->out += out_len; + p->out_max -= out_len; + p->total += out_len; +} + +static void wire_num48_to_str(rrset_dump_params_t *p) +{ + CHECK_PRET + + uint64_t data; + size_t in_len = 6; + size_t out_len = 0; + + CHECK_INMAX(in_len) + + // Fill in input data. + data = knot_wire_read_u48(p->in); + + // Write number. + int ret = snprintf(p->out, p->out_max, "%"PRIu64"", data); + CHECK_RET_OUTMAX_SNPRINTF + out_len = ret; + + // Fill in output. + p->in += in_len; + p->in_max -= in_len; + p->out += out_len; + p->out_max -= out_len; + p->total += out_len; +} + +static void wire_ipv4_to_str(rrset_dump_params_t *p) +{ + CHECK_PRET + + struct in_addr addr4; + size_t in_len = sizeof(addr4.s_addr); + size_t out_len = 0; + + CHECK_INMAX(in_len) + + FILL_IN_INPUT(addr4.s_addr) + + // Write address. + if (inet_ntop(AF_INET, &addr4, p->out, p->out_max) == NULL) { + p->ret = -1; + return; + } + out_len = strlen(p->out); + + // Fill in output. + p->in += in_len; + p->in_max -= in_len; + p->out += out_len; + p->out_max -= out_len; + p->total += out_len; +} + +static void wire_ipv6_to_str(rrset_dump_params_t *p) +{ + CHECK_PRET + + struct in6_addr addr6; + size_t in_len = sizeof(addr6.s6_addr); + size_t out_len = 0; + + CHECK_INMAX(in_len) + + FILL_IN_INPUT(addr6.s6_addr) + + // Write address. + if (inet_ntop(AF_INET6, &addr6, p->out, p->out_max) == NULL) { + p->ret = -1; + return; + } + out_len = strlen(p->out); + + // Fill in output. + p->in += in_len; + p->in_max -= in_len; + p->out += out_len; + p->out_max -= out_len; + p->total += out_len; +} + +static void wire_type_to_str(rrset_dump_params_t *p) +{ + CHECK_PRET + + char type[32]; + uint16_t data; + size_t in_len = sizeof(data); + size_t out_len = 0; + + CHECK_INMAX(in_len) + + FILL_IN_INPUT(data) + + // Get record type name string. + int ret = knot_rrtype_to_string(ntohs(data), type, sizeof(type)); + CHECK_RET_POSITIVE + + // Write string. + ret = snprintf(p->out, p->out_max, "%s", type); + CHECK_RET_OUTMAX_SNPRINTF + out_len = ret; + + // Fill in output. + p->in += in_len; + p->in_max -= in_len; + p->out += out_len; + p->out_max -= out_len; + p->total += out_len; +} + +static int hex_encode(const uint8_t *in, + const uint32_t in_len, + uint8_t *out, + const uint32_t out_len) +{ + static const char hex[] = "0123456789ABCDEF"; + + if (out_len < 2 * in_len) { + return -1; + } + + for (uint32_t i = 0; i < in_len; i++) { + out[2 * i] = hex[in[i] / 16]; + out[2 * i + 1] = hex[in[i] % 16]; + } + + return 2 * in_len; +} + +static int hex_encode_alloc(const uint8_t *in, + const uint32_t in_len, + uint8_t **out) +{ + uint32_t out_len = 2 * in_len; + + // Allocating output buffer. + *out = malloc(out_len); + + if (*out == NULL) { + return -1; + } + + // Encoding data. + return hex_encode(in, in_len, *out, out_len); +} + +static int num48_encode(const uint8_t *in, + const uint32_t in_len, + uint8_t *out, + const uint32_t out_len) +{ + if (in_len != 6) { + return -1; + } + + uint64_t data = knot_wire_read_u48(in); + + int ret = snprintf((char *)out, out_len, "%"PRIu64"", data); + if (ret <= 0 || (size_t)ret >= out_len) { + return -1; + } + + return ret; +} + +typedef int (*encode_t)(const uint8_t *in, const uint32_t in_len, + uint8_t *out, const uint32_t out_len); + +typedef int (*encode_alloc_t)(const uint8_t *in, const uint32_t in_len, + uint8_t **out); + +static void wire_data_encode_to_str(rrset_dump_params_t *p, + encode_t enc, encode_alloc_t enc_alloc) +{ + CHECK_PRET + + int ret; + size_t in_len = p->in_max; + + // One-line vs multi-line mode. + if (p->style->wrap == false) { + // Encode data directly to the output. + ret = enc(p->in, in_len, (uint8_t *)(p->out), p->out_max); + CHECK_RET_POSITIVE + size_t out_len = ret; + + p->out += out_len; + p->out_max -= out_len; + p->total += out_len; + } else { + int src_begin; + uint8_t *buf; + + // Encode data to the temporary buffer. + ret = enc_alloc(p->in, in_len, &buf); + CHECK_RET_POSITIVE + + // Loop which wraps base64 block in more lines. + for (src_begin = 0; src_begin < ret; src_begin += BLOCK_WIDTH) { + if (src_begin > 0) { + // Write indent block. + dump_string(p, BLOCK_INDENT); + if (p->ret < 0) { + free(buf); + return; + } + } + + // Compute block length (the last one can be shorter). + int src_len = (ret - src_begin) < BLOCK_WIDTH ? + (ret - src_begin) : BLOCK_WIDTH; + + if ((size_t)src_len > p->out_max) { + free(buf); + p->ret = -1; + return; + } + + // Write data block. + memcpy(p->out, buf + src_begin, src_len); + + p->out += src_len; + p->out_max -= src_len; + p->total += src_len; + } + + // Destroy temporary buffer. + free(buf); + } + + STRING_TERMINATION + + // Fill in output. + p->in += in_len; + p->in_max -= in_len; +} + +static void wire_len_data_encode_to_str(rrset_dump_params_t *p, + encode_t enc, + const size_t len_len, + const bool print_len, + const char *empty_str) +{ + CHECK_PRET + + size_t in_len; + + // First len_len bytes are data length. + CHECK_INMAX(len_len) + + // Read data length. + switch (len_len) { + case 1: + in_len = *(p->in); + break; + case 2: + in_len = knot_wire_read_u16(p->in); + break; + case 4: + in_len = knot_wire_read_u32(p->in); + break; + default: + p->ret = -1; + return; + } + + // If required print data length. + if (print_len == true) { + switch (len_len) { + case 1: + wire_num8_to_str(p); + break; + case 2: + wire_num16_to_str(p); + break; + case 4: + wire_num32_to_str(p); + break; + } + + CHECK_PRET + + // If something follows, print one space character. + if (in_len > 0 || *empty_str != '\0') { + dump_string(p, " "); + CHECK_PRET + } + } else { + p->in += len_len; + p->in_max -= len_len; + } + + if (in_len > 0) { + // Encode data directly to the output. + int ret = enc(p->in, in_len, (uint8_t *)(p->out), p->out_max); + CHECK_RET_POSITIVE + p->out += ret; + p->out_max -= ret; + p->total += ret; + + STRING_TERMINATION + + // Fill in output. + p->in += in_len; + p->in_max -= in_len; + } else if (*empty_str != '\0') { + dump_string(p, empty_str); + CHECK_PRET + } +} + +static void wire_data_omit(rrset_dump_params_t *p) +{ + CHECK_PRET + + const char *omit_message = "[omitted]"; + const size_t omlen = strlen(omit_message); + + if (p->out_max < omlen) { + p->ret = -1; + return; + } + + memcpy(p->out, omit_message, omlen); + p->out += omlen; + p->out_max -= omlen; + p->total += omlen; + + STRING_TERMINATION + + p->in += p->in_max; + p->in_max = 0; +} + +static void wire_dnskey_to_tag(rrset_dump_params_t *p) +{ + CHECK_PRET + + int key_pos = -4; // we expect that key flags, 3 and algorithm + // have been already dumped + + uint16_t key_tag = 0; + const dnssec_binary_t rdata_bin = { + .data = (uint8_t *)(p->in + key_pos), + .size = p->in_max - key_pos + }; + dnssec_keytag(&rdata_bin, &key_tag); + + int ret = snprintf(p->out, p->out_max, "[id = %hu]", key_tag); + CHECK_RET_OUTMAX_SNPRINTF + + p->in += p->in_max; + p->in_max = 0; + p->out += ret; + p->out_max -= ret; + p->total += ret; +} + +static void wire_unknown_to_str(rrset_dump_params_t *p) +{ + CHECK_PRET + + int ret; + size_t in_len = p->in_max; + size_t out_len = 0; + + // Write unknown length header. + if (in_len > 0) { + ret = snprintf(p->out, p->out_max, "\\# %zu ", in_len); + } else { + ret = snprintf(p->out, p->out_max, "\\# 0"); + } + CHECK_RET_OUTMAX_SNPRINTF + out_len = ret; + + // Fill in output. + p->out += out_len; + p->out_max -= out_len; + p->total += out_len; + + // Write hex data if any. + if (in_len > 0) { + // If wrap mode wrap line. + if (p->style->wrap) { + dump_string(p, BLOCK_INDENT); + CHECK_PRET + } + + wire_data_encode_to_str(p, &hex_encode, &hex_encode_alloc); + CHECK_PRET + } +} + +static void wire_text_to_str(rrset_dump_params_t *p, bool quote, bool with_header) +{ + CHECK_PRET + + size_t in_len = 0; + + if (with_header) { + // First byte is string length. + CHECK_INMAX(1) + in_len = *(p->in); + p->in++; + p->in_max--; + + // Check if the given length makes sense. + CHECK_INMAX(in_len) + } else { + in_len = p->in_max; + } + + // Check if quotation can ever be disabled (parser protection fallback). + if (!quote) { + for (size_t i = 0; i < in_len; i++) { + if (p->in[i] == ' ') { // Other WS characters are encoded. + quote = true; + break; + } + } + } + + // Opening quotation. + if (quote) { + dump_string(p, "\""); + CHECK_PRET + } + + // Loop over all characters. + for (size_t i = 0; i < in_len; i++) { + uint8_t ch = p->in[i]; + + if (is_print(ch)) { + // For special character print leading slash. + if (ch == '\\' || ch == '"') { + dump_string(p, "\\"); + CHECK_PRET + } + + // Print text character. + if (p->out_max == 0) { + p->ret = -1; + return; + } + + *p->out = ch; + p->out++; + p->out_max--; + p->total++; + } else { + // Unprintable character encode via \ddd notation. + int ret = snprintf(p->out, p->out_max,"\\%03u", ch); + CHECK_RET_OUTMAX_SNPRINTF + + p->out += ret; + p->out_max -= ret; + p->total += ret; + } + } + + // Closing quotation. + if (quote) { + dump_string(p, "\""); + CHECK_PRET + } + + STRING_TERMINATION + + // Fill in output. + p->in += in_len; + p->in_max -= in_len; +} + +static void wire_timestamp_to_str(rrset_dump_params_t *p) +{ + CHECK_PRET + + uint32_t data; + size_t in_len = sizeof(data); + size_t out_len = 0; + int ret; + + CHECK_INMAX(in_len) + + FILL_IN_INPUT(data) + + time_t timestamp = ntohl(data); + + if (p->style->human_tmstamp) { + struct tm result; + // Write timestamp in YYYYMMDDhhmmss format. + ret = strftime(p->out, p->out_max, "%Y%m%d%H%M%S", + gmtime_r(×tamp, &result)); + CHECK_RET_POSITIVE + } else { + // Write timestamp only. + ret = snprintf(p->out, p->out_max, "%u", ntohl(data)); + CHECK_RET_OUTMAX_SNPRINTF + } + out_len = ret; + + // Fill in output. + p->in += in_len; + p->in_max -= in_len; + p->out += out_len; + p->out_max -= out_len; + p->total += out_len; +} + +static int time_to_human_str(char *out, + const size_t out_len, + uint32_t data) +{ + size_t total_len = 0; + uint32_t num; + int ret; + +#define tths_process(unit_name, unit_size) \ + num = data / (unit_size); \ + if (num > 0) { \ + ret = snprintf(out + total_len, out_len - total_len, \ + "%u%s", num, (unit_name)); \ + if (ret <= 0 || (size_t)ret >= out_len - total_len) { \ + return -1; \ + } \ + total_len += ret; \ + data -= num * (unit_size); \ + } + + tths_process("d", 86400); + tths_process("h", 3600); + tths_process("m", 60); + tths_process("s", 1); + +#undef tths_process + + return total_len > 0 ? total_len : -1; +} + +static void wire_ttl_to_str(rrset_dump_params_t *p) +{ + CHECK_PRET + + uint32_t data; + size_t in_len = sizeof(data); + size_t out_len = 0; + int ret; + + CHECK_INMAX(in_len) + + FILL_IN_INPUT(data) + + if (p->style->human_ttl) { + // Write time in human readable format. + ret = time_to_human_str(p->out, p->out_max, ntohl(data)); + CHECK_RET_POSITIVE + } else { + // Write timestamp only. + ret = snprintf(p->out, p->out_max, "%u", ntohl(data)); + CHECK_RET_OUTMAX_SNPRINTF + } + out_len = ret; + + // Fill in output. + p->in += in_len; + p->in_max -= in_len; + p->out += out_len; + p->out_max -= out_len; + p->total += out_len; +} + +static void wire_bitmap_to_str(rrset_dump_params_t *p) +{ + CHECK_PRET + + int ret; + char type[32]; + size_t i = 0; + size_t in_len = p->in_max; + size_t out_len = 0; + + // Loop over bitmap window array (can be empty). + while (i < in_len) { + // First byte is window number. + uint8_t win = p->in[i++]; + + // Check window length (length must follow). + if (i >= in_len) { + p->ret = -1; + return; + } + + // Second byte is window length. + uint8_t bitmap_len = p->in[i++]; + + // Check window length (len bytes must follow). + if (i + bitmap_len > in_len) { + p->ret = -1; + return; + } + + // Bitmap processing. + for (size_t j = 0; j < (bitmap_len * 8); j++) { + if ((p->in[i + j / 8] & (128 >> (j % 8))) != 0) { + uint16_t type_num = win * 256 + j; + + ret = knot_rrtype_to_string(type_num, type, sizeof(type)); + CHECK_RET_POSITIVE + + // Print type name to type list. + if (out_len > 0) { + ret = snprintf(p->out, p->out_max, + " %s", type); + } else { + ret = snprintf(p->out, p->out_max, + "%s", type); + } + CHECK_RET_OUTMAX_SNPRINTF + out_len += ret; + p->out += ret; + p->out_max -= ret; + } + } + + i += bitmap_len; + } + + // Fill in output. + p->in += in_len; + p->in_max -= in_len; + p->total += out_len; +} + +static void wire_dname_to_str(rrset_dump_params_t *p) +{ + CHECK_PRET + + size_t in_len = knot_dname_size(p->in); + size_t out_len = 0; + + CHECK_INMAX(in_len) + + // Write dname string. + if (p->style->ascii_to_idn == NULL) { + char *dname_str = knot_dname_to_str(p->out, p->in, p->out_max); + if (dname_str == NULL) { + p->ret = -1; + return; + } + out_len = strlen(dname_str); + } else { + char *dname_str = knot_dname_to_str_alloc(p->in); + p->style->ascii_to_idn(&dname_str); + + int ret = snprintf(p->out, p->out_max, "%s", dname_str); + free(dname_str); + CHECK_RET_OUTMAX_SNPRINTF + out_len = ret; + } + + // Fill in output. + p->in += in_len; + p->in_max -= in_len; + p->out += out_len; + p->out_max -= out_len; + p->total += out_len; +} + +static void wire_apl_to_str(rrset_dump_params_t *p) +{ + CHECK_PRET + + struct in_addr addr4; + struct in6_addr addr6; + int ret; + size_t out_len = 0; + + // Input check: family(2B) + prefix(1B) + afdlen(1B). + CHECK_INMAX(4) + + // Read fixed size values. + uint16_t family = knot_wire_read_u16(p->in); + uint8_t prefix = *(p->in + 2); + uint8_t negation = *(p->in + 3) >> 7; + uint8_t afdlen = *(p->in + 3) & 0x7F; + p->in += 4; + p->in_max -= 4; + + // Write negation mark. + if (negation != 0) { + dump_string(p, "!"); + CHECK_PRET + } + + // Write address family with colon. + ret = snprintf(p->out, p->out_max, "%u:", family); + CHECK_RET_OUTMAX_SNPRINTF + p->out += ret; + p->out_max -= ret; + p->total += ret; + + // Write address. + switch (family) { + case 1: + memset(&addr4, 0, sizeof(addr4)); + + if (afdlen > sizeof(addr4.s_addr) || afdlen > p->in_max) { + p->ret = -1; + return; + } + + if (memcpy(&(addr4.s_addr), p->in, afdlen) == NULL) { + p->ret = -1; + return; + } + + // Write address. + if (inet_ntop(AF_INET, &addr4, p->out, p->out_max) == NULL) { + p->ret = -1; + return; + } + out_len = strlen(p->out); + + break; + case 2: + memset(&addr6, 0, sizeof(addr6)); + + if (afdlen > sizeof(addr6.s6_addr) || afdlen > p->in_max) { + p->ret = -1; + return; + } + + if (memcpy(&(addr6.s6_addr), p->in, afdlen) == NULL) { + p->ret = -1; + return; + } + + // Write address. + if (inet_ntop(AF_INET6, &addr6, p->out, p->out_max) == NULL) { + p->ret = -1; + return; + } + out_len = strlen(p->out); + + break; + default: + p->ret = -1; + return; + } + p->in += afdlen; + p->in_max -= afdlen; + p->out += out_len; + p->out_max -= out_len; + p->total += out_len; + + // Write prefix length with forward slash. + ret = snprintf(p->out, p->out_max, "/%u", prefix); + CHECK_RET_OUTMAX_SNPRINTF + p->out += ret; + p->out_max -= ret; + p->total += ret; +} + +static void wire_loc_to_str(rrset_dump_params_t *p) +{ + CHECK_PRET + + // Read values. + wire_ctx_t wire = wire_ctx_init_const(p->in, p->in_max); + uint8_t version = wire_ctx_read_u8(&wire); + + // Version check. + if (version != 0) { + wire_unknown_to_str(p); + p->ret = -1; + return; + } + + // Continue to read values. + uint8_t size_w = wire_ctx_read_u8(&wire); + uint8_t hpre_w = wire_ctx_read_u8(&wire); + uint8_t vpre_w = wire_ctx_read_u8(&wire); + uint32_t lat_w = wire_ctx_read_u32(&wire); + uint32_t lon_w = wire_ctx_read_u32(&wire); + uint32_t alt_w = wire_ctx_read_u32(&wire); + + // Check if all reads are correct. + if (wire.error != KNOT_EOK) { + p->ret = -1; + return; + } + + p->in += wire_ctx_offset(&wire); + p->in_max = wire_ctx_available(&wire); + + // Latitude calculation. + char lat_mark; + uint32_t lat; + if (lat_w >= LOC_ZERO) { + lat_mark = 'N'; + lat = lat_w - LOC_ZERO; + } else { + lat_mark = 'S'; + lat = LOC_ZERO - lat_w; + } + + uint32_t d1 = lat / 3600000; + uint32_t m1 = (lat - 3600000 * d1) / 60000; + double s1 = 0.001 * (lat - 3600000 * d1 - 60000 * m1); + + // Longitude calculation. + char lon_mark; + uint32_t lon; + if (lon_w >= LOC_ZERO) { + lon_mark = 'E'; + lon = lon_w - LOC_ZERO; + } else { + lon_mark = 'W'; + lon = LOC_ZERO - lon_w; + } + + uint32_t d2 = lon / 3600000; + uint32_t m2 = (lon - 3600000 * d2) / 60000; + double s2 = 0.001 * (lon - 3600000 * d2 - 60000 * m2); + + // Write latitude and longitude. + int ret = snprintf(p->out, p->out_max, "%u %u %.*f %c %u %u %.*f %c", + d1, m1, (uint32_t)s1 != s1 ? 3 : 0, s1, lat_mark, + d2, m2, (uint32_t)s2 != s2 ? 3 : 0, s2, lon_mark); + CHECK_RET_OUTMAX_SNPRINTF + p->out += ret; + p->out_max -= ret; + p->total += ret; + + // Altitude calculation. + double alt = 0.01 * alt_w - 100000.0; + + // Compute mantisa and exponent for each size. + uint8_t size_m = size_w >> 4; + uint8_t size_e = size_w & 0xF; + uint8_t hpre_m = hpre_w >> 4; + uint8_t hpre_e = hpre_w & 0xF; + uint8_t vpre_m = vpre_w >> 4; + uint8_t vpre_e = vpre_w & 0xF; + + // Sizes check. + if (size_m > 9 || size_e > 9 || hpre_m > 9 || hpre_e > 9 || + vpre_m > 9 || vpre_e > 9) { + p->ret = -1; + return; + } + + // Size and precisions calculation. + double size = 0.01 * size_m * pow(10, size_e); + double hpre = 0.01 * hpre_m * pow(10, hpre_e); + double vpre = 0.01 * vpre_m * pow(10, vpre_e); + + // Write altitude and precisions. + ret = snprintf(p->out, p->out_max, " %.*fm %.*fm %.*fm %.*fm", + (int32_t)alt != alt ? 2 : 0, alt, + (uint32_t)size != size ? 2 : 0, size, + (uint32_t)hpre != hpre ? 2 : 0, hpre, + (uint32_t)vpre != vpre ? 2 : 0, vpre); + CHECK_RET_OUTMAX_SNPRINTF + p->out += ret; + p->out_max -= ret; + p->total += ret; +} + +static void wire_gateway_to_str(rrset_dump_params_t *p) +{ + CHECK_PRET + + // Input check: type(1B) + algo(1B). + CHECK_INMAX(2) + + uint8_t type = *p->in; + uint8_t alg = *(p->in + 1); + + // Write gateway type. + wire_num8_to_str(p); + CHECK_PRET + + // Write space. + dump_string(p, " "); + CHECK_PRET + + // Write algorithm number. + wire_num8_to_str(p); + CHECK_PRET + + // Write space. + dump_string(p, " "); + CHECK_PRET + + // Write appropriate gateway. + switch (type) { + case 0: + dump_string(p, "."); + break; + case 1: + wire_ipv4_to_str(p); + break; + case 2: + wire_ipv6_to_str(p); + break; + case 3: + wire_dname_to_str(p); + break; + default: + p->ret = -1; + } + CHECK_PRET + + if (alg > 0) { + // If wrap mode wrap line. + if (p->style->wrap) { + dump_string(p, BLOCK_INDENT); + } else { + dump_string(p, " "); + } + CHECK_PRET + + // Write ipsec key. + wire_data_encode_to_str(p, &base64_encode, &base64_encode_alloc); + CHECK_PRET + } +} + +static void wire_l64_to_str(rrset_dump_params_t *p) +{ + CHECK_PRET + + // Check input size (64-bit identifier). + if (p->in_max != 8) { + p->ret = -1; + return; + } + + // Write identifier (2-byte) labels separated with a colon. + while (p->in_max > 0) { + int ret = hex_encode(p->in, 2, (uint8_t *)(p->out), p->out_max); + CHECK_RET_POSITIVE + p->in += 2; + p->in_max -= 2; + p->out += ret; + p->out_max -= ret; + p->total += ret; + + // Write separation character. + if (p->in_max > 0) { + dump_string(p, ":"); + CHECK_PRET + } + } +} + +static void wire_eui_to_str(rrset_dump_params_t *p) +{ + CHECK_PRET + + CHECK_INMAX(2) + + // Write EUI hexadecimal pairs. + while (p->in_max > 0) { + int ret = hex_encode(p->in, 1, (uint8_t *)(p->out), p->out_max); + CHECK_RET_POSITIVE + p->in++; + p->in_max--; + p->out += ret; + p->out_max -= ret; + p->total += ret; + + // Write separation character. + if (p->in_max > 0) { + dump_string(p, "-"); + CHECK_PRET + } + } +} + +static void wire_tsig_rcode_to_str(rrset_dump_params_t *p) +{ + CHECK_PRET + + uint16_t data; + size_t in_len = sizeof(data); + const char *rcode_str = "Unknown"; + + CHECK_INMAX(in_len) + + // Fill in input data. + data = knot_wire_read_u16(p->in); + + // Find RCODE name. + const knot_lookup_t *rcode = NULL; + rcode = knot_lookup_by_id(knot_tsig_rcode_names, data); + if (rcode == NULL) { + rcode = knot_lookup_by_id(knot_rcode_names, data); + } + if (rcode != NULL) { + rcode_str = rcode->name; + } + + // Dump RCODE name. + dump_string(p, rcode_str); + CHECK_PRET + + // Fill in output. + p->in += in_len; + p->in_max -= in_len; +} + +static size_t dnskey_len(const uint8_t *rdata, + const size_t rdata_len) +{ + // Check for empty rdata and empty key. + if (rdata_len <= 4) { + return 0; + } + + const uint8_t *key = rdata + 4; + const size_t len = rdata_len - 4; + + switch (rdata[3]) { + case KNOT_DNSSEC_ALG_DSA: + case KNOT_DNSSEC_ALG_DSA_NSEC3_SHA1: + // RFC 2536, key size ~ bit-length of 'modulus' P. + return (64 + 8 * key[0]) * 8; + case KNOT_DNSSEC_ALG_RSAMD5: + case KNOT_DNSSEC_ALG_RSASHA1: + case KNOT_DNSSEC_ALG_RSASHA1_NSEC3_SHA1: + case KNOT_DNSSEC_ALG_RSASHA256: + case KNOT_DNSSEC_ALG_RSASHA512: + // RFC 3110, key size ~ bit-length of 'modulus'. + if (key[0] == 0) { + if (len < 3) { + return 0; + } + uint16_t exp; + memcpy(&exp, key + 1, sizeof(uint16_t)); + return (len - 3 - ntohs(exp)) * 8; + } else { + return (len - 1 - key[0]) * 8; + } + case KNOT_DNSSEC_ALG_ECC_GOST: + // RFC 5933, key size of GOST public keys MUST be 512 bits. + return 512; + case KNOT_DNSSEC_ALG_ECDSAP256SHA256: + // RFC 6605. + return 256; + case KNOT_DNSSEC_ALG_ECDSAP384SHA384: + // RFC 6605. + return 384; + case KNOT_DNSSEC_ALG_ED25519: + // RFC 8080. + return 256; + case KNOT_DNSSEC_ALG_ED448: + // RFC 8080. + return 456; + default: + return 0; + } +} + +static void dnskey_info(const uint8_t *rdata, + const size_t rdata_len, + char *out, + const size_t out_len) +{ + // TODO: migrate key info to libdnssec + + const uint8_t sep = *(rdata + 1) & 0x01; + uint16_t key_tag = 0; + const size_t key_len = dnskey_len(rdata, rdata_len); + const uint8_t alg_id = rdata[3]; + + const dnssec_binary_t rdata_bin = { .data = (uint8_t *)rdata, + .size = rdata_len }; + dnssec_keytag(&rdata_bin, &key_tag); + + const knot_lookup_t *alg = NULL; + alg = knot_lookup_by_id(knot_dnssec_alg_names, alg_id); + + int ret = snprintf(out, out_len, "%s, %s (%zub), id = %u", + sep ? "KSK" : "ZSK", + alg ? alg->name : "UNKNOWN", + key_len, key_tag ); + + if (ret <= 0) { // Truncated return is acceptable. Just check for errors. + out[0] = '\0'; + } +} + +#define DUMP_PARAMS rrset_dump_params_t *const p +#define DUMP_END return (p->in_max == 0 ? (int)p->total : KNOT_EPARSEFAIL); + +#define CHECK_RET(p) if (p->ret < 0) return p->ret; + +#define WRAP_INIT dump_string(p, "(" BLOCK_INDENT); CHECK_RET(p); +#define WRAP_END dump_string(p, BLOCK_INDENT ")"); CHECK_RET(p); +#define WRAP_LINE dump_string(p, BLOCK_INDENT); CHECK_RET(p); + +#define COMMENT(s) if (p->style->verbose) { \ + dump_string(p, " ; "); CHECK_RET(p); \ + dump_string(p, s); CHECK_RET(p); \ + } + +#define DUMP_SPACE dump_string(p, " "); CHECK_RET(p); +#define DUMP_NUM8 wire_num8_to_str(p); CHECK_RET(p); +#define DUMP_NUM16 wire_num16_to_str(p); CHECK_RET(p); +#define DUMP_NUM32 wire_num32_to_str(p); CHECK_RET(p); +#define DUMP_NUM48 wire_num48_to_str(p); CHECK_RET(p); +#define DUMP_DNAME wire_dname_to_str(p); CHECK_RET(p); +#define DUMP_TIME wire_ttl_to_str(p); CHECK_RET(p); +#define DUMP_TIMESTAMP wire_timestamp_to_str(p); CHECK_RET(p); +#define DUMP_IPV4 wire_ipv4_to_str(p); CHECK_RET(p); +#define DUMP_IPV6 wire_ipv6_to_str(p); CHECK_RET(p); +#define DUMP_TYPE wire_type_to_str(p); CHECK_RET(p); +#define DUMP_HEX wire_data_encode_to_str(p, &hex_encode, \ + &hex_encode_alloc); CHECK_RET(p); +#define DUMP_BASE64 wire_data_encode_to_str(p, &base64_encode, \ + &base64_encode_alloc); CHECK_RET(p); +#define DUMP_HASH wire_len_data_encode_to_str(p, &base32hex_encode, \ + 1, false, ""); CHECK_RET(p); +#define DUMP_SALT wire_len_data_encode_to_str(p, &hex_encode, \ + 1, false, "-"); CHECK_RET(p); +#define DUMP_TSIG_DGST wire_len_data_encode_to_str(p, &base64_encode, \ + 2, true, ""); CHECK_RET(p); +#define DUMP_TSIG_DATA wire_len_data_encode_to_str(p, &num48_encode, \ + 2, true, ""); CHECK_RET(p); +#define DUMP_OMIT wire_data_omit(p); CHECK_RET(p); +#define DUMP_KEY_OMIT wire_dnskey_to_tag(p); CHECK_RET(p); +#define DUMP_TEXT wire_text_to_str(p, true, true); CHECK_RET(p); +#define DUMP_LONG_TEXT wire_text_to_str(p, true, false); CHECK_RET(p); +#define DUMP_UNQUOTED wire_text_to_str(p, false, true); CHECK_RET(p); +#define DUMP_BITMAP wire_bitmap_to_str(p); CHECK_RET(p); +#define DUMP_APL wire_apl_to_str(p); CHECK_RET(p); +#define DUMP_LOC wire_loc_to_str(p); CHECK_RET(p); +#define DUMP_GATEWAY wire_gateway_to_str(p); CHECK_RET(p); +#define DUMP_L64 wire_l64_to_str(p); CHECK_RET(p); +#define DUMP_EUI wire_eui_to_str(p); CHECK_RET(p); +#define DUMP_TSIG_RCODE wire_tsig_rcode_to_str(p); CHECK_RET(p); +#define DUMP_UNKNOWN wire_unknown_to_str(p); CHECK_RET(p); + +static int dump_a(DUMP_PARAMS) +{ + DUMP_IPV4; + + DUMP_END; +} + +static int dump_ns(DUMP_PARAMS) +{ + DUMP_DNAME; + + DUMP_END; +} + +static int dump_soa(DUMP_PARAMS) +{ + if (p->style->wrap) { + DUMP_DNAME; DUMP_SPACE; + DUMP_DNAME; DUMP_SPACE; WRAP_INIT; + DUMP_NUM32; COMMENT("serial"); WRAP_LINE; + DUMP_TIME; COMMENT("refresh"); WRAP_LINE; + DUMP_TIME; COMMENT("retry"); WRAP_LINE; + DUMP_TIME; COMMENT("expire"); WRAP_LINE; + DUMP_TIME; COMMENT("minimum"); WRAP_END; + } else { + DUMP_DNAME; DUMP_SPACE; + DUMP_DNAME; DUMP_SPACE; + DUMP_NUM32; DUMP_SPACE; + DUMP_TIME; DUMP_SPACE; + DUMP_TIME; DUMP_SPACE; + DUMP_TIME; DUMP_SPACE; + DUMP_TIME; + } + + DUMP_END; +} + +static int dump_hinfo(DUMP_PARAMS) +{ + DUMP_TEXT; DUMP_SPACE; + DUMP_TEXT; + + DUMP_END; +} + +static int dump_minfo(DUMP_PARAMS) +{ + DUMP_DNAME; DUMP_SPACE; + DUMP_DNAME; + + DUMP_END; +} + +static int dump_mx(DUMP_PARAMS) +{ + DUMP_NUM16; DUMP_SPACE; + DUMP_DNAME; + + DUMP_END; +} + +static int dump_txt(DUMP_PARAMS) +{ + // First text string. + DUMP_TEXT; + + // Other text strings if any. + while (p->in_max > 0) { + DUMP_SPACE; DUMP_TEXT; + } + + DUMP_END; +} + +static int dump_dnskey(DUMP_PARAMS) +{ + if (p->style->wrap) { + char info[512] = ""; + dnskey_info(p->in, p->in_max, info, sizeof(info)); + + DUMP_NUM16; DUMP_SPACE; + DUMP_NUM8; DUMP_SPACE; + DUMP_NUM8; DUMP_SPACE; + if (p->style->hide_crypto) { + DUMP_OMIT; + WRAP_LINE; + } else { + WRAP_INIT; + DUMP_BASE64; + WRAP_END; + } + COMMENT(info); + } else { + DUMP_NUM16; DUMP_SPACE; + DUMP_NUM8; DUMP_SPACE; + DUMP_NUM8; DUMP_SPACE; + if (p->style->hide_crypto) { + DUMP_KEY_OMIT; + } else { + DUMP_BASE64; + } + } + + DUMP_END; +} + +static int dump_aaaa(DUMP_PARAMS) +{ + DUMP_IPV6; + + DUMP_END; +} + +static int dump_loc(DUMP_PARAMS) +{ + DUMP_LOC; + + DUMP_END; +} + +static int dump_srv(DUMP_PARAMS) +{ + DUMP_NUM16; DUMP_SPACE; + DUMP_NUM16; DUMP_SPACE; + DUMP_NUM16; DUMP_SPACE; + DUMP_DNAME; + + DUMP_END; +} + +static int dump_naptr(DUMP_PARAMS) +{ + DUMP_NUM16; DUMP_SPACE; + DUMP_NUM16; DUMP_SPACE; + DUMP_TEXT; DUMP_SPACE; + DUMP_TEXT; DUMP_SPACE; + DUMP_TEXT; DUMP_SPACE; + DUMP_DNAME; + + DUMP_END; +} + +static int dump_cert(DUMP_PARAMS) +{ + if (p->style->wrap) { + DUMP_NUM16; DUMP_SPACE; + DUMP_NUM16; DUMP_SPACE; + DUMP_NUM8; DUMP_SPACE; WRAP_INIT; + DUMP_BASE64; + WRAP_END; + } else { + DUMP_NUM16; DUMP_SPACE; + DUMP_NUM16; DUMP_SPACE; + DUMP_NUM8; DUMP_SPACE; + DUMP_BASE64; + } + + DUMP_END; +} + +static int dump_apl(DUMP_PARAMS) +{ + // Print list of APLs (empty list is allowed). + while (p->in_max > 0) { + if (p->total > 0) { + DUMP_SPACE; + } + DUMP_APL; + } + + DUMP_END; +} + +static int dump_ds(DUMP_PARAMS) +{ + if (p->style->wrap) { + DUMP_NUM16; DUMP_SPACE; + DUMP_NUM8; DUMP_SPACE; + DUMP_NUM8; DUMP_SPACE; WRAP_INIT; + DUMP_HEX; + WRAP_END; + } else { + DUMP_NUM16; DUMP_SPACE; + DUMP_NUM8; DUMP_SPACE; + DUMP_NUM8; DUMP_SPACE; + DUMP_HEX; + } + + DUMP_END; +} + +static int dump_sshfp(DUMP_PARAMS) +{ + if (p->style->wrap) { + DUMP_NUM8; DUMP_SPACE; + DUMP_NUM8; DUMP_SPACE; WRAP_INIT; + DUMP_HEX; + WRAP_END; + } else { + DUMP_NUM8; DUMP_SPACE; + DUMP_NUM8; DUMP_SPACE; + DUMP_HEX; + } + + DUMP_END; +} + +static int dump_ipseckey(DUMP_PARAMS) +{ + if (p->style->wrap) { + DUMP_NUM8; DUMP_SPACE; WRAP_INIT; + DUMP_GATEWAY; + WRAP_END; + } else { + DUMP_NUM8; DUMP_SPACE; + DUMP_GATEWAY; + } + + DUMP_END; +} + +static int dump_rrsig(DUMP_PARAMS) +{ + DUMP_TYPE; DUMP_SPACE; + DUMP_NUM8; DUMP_SPACE; + DUMP_NUM8; DUMP_SPACE; + DUMP_NUM32; DUMP_SPACE; + DUMP_TIMESTAMP; DUMP_SPACE; + if (p->style->wrap) { + WRAP_INIT; + } + DUMP_TIMESTAMP; DUMP_SPACE; + DUMP_NUM16; DUMP_SPACE; + DUMP_DNAME; DUMP_SPACE; + if (p->style->wrap) { + WRAP_LINE; + } + if (p->style->hide_crypto) { + DUMP_OMIT; + } else { + DUMP_BASE64; + } + if (p->style->wrap) { + WRAP_END; + } + DUMP_END; +} + +static int dump_nsec(DUMP_PARAMS) +{ + DUMP_DNAME; DUMP_SPACE; + DUMP_BITMAP; + + DUMP_END; +} + +static int dump_dhcid(DUMP_PARAMS) +{ + if (p->style->wrap) { + WRAP_INIT; + DUMP_BASE64; + WRAP_END; + } else { + DUMP_BASE64; + } + + DUMP_END; +} + +static int dump_nsec3(DUMP_PARAMS) +{ + if (p->style->wrap) { + DUMP_NUM8; DUMP_SPACE; + DUMP_NUM8; DUMP_SPACE; + DUMP_NUM16; DUMP_SPACE; + DUMP_SALT; DUMP_SPACE; WRAP_INIT; + DUMP_HASH; DUMP_SPACE; WRAP_LINE; + DUMP_BITMAP; + WRAP_END; + } else { + DUMP_NUM8; DUMP_SPACE; + DUMP_NUM8; DUMP_SPACE; + DUMP_NUM16; DUMP_SPACE; + DUMP_SALT; DUMP_SPACE; + DUMP_HASH; DUMP_SPACE; + DUMP_BITMAP; + } + + DUMP_END; +} + +static int dump_nsec3param(DUMP_PARAMS) +{ + DUMP_NUM8; DUMP_SPACE; + DUMP_NUM8; DUMP_SPACE; + DUMP_NUM16; DUMP_SPACE; + DUMP_SALT; + + DUMP_END; +} + +static int dump_tlsa(DUMP_PARAMS) +{ + if (p->style->wrap) { + DUMP_NUM8; DUMP_SPACE; + DUMP_NUM8; DUMP_SPACE; + DUMP_NUM8; DUMP_SPACE; WRAP_INIT; + DUMP_HEX; + WRAP_END; + } else { + DUMP_NUM8; DUMP_SPACE; + DUMP_NUM8; DUMP_SPACE; + DUMP_NUM8; DUMP_SPACE; + DUMP_HEX; + } + + DUMP_END; +} + +static int dump_l64(DUMP_PARAMS) +{ + DUMP_NUM16; DUMP_SPACE; + DUMP_L64; + + DUMP_END; +} + +static int dump_l32(DUMP_PARAMS) +{ + DUMP_NUM16; DUMP_SPACE; + DUMP_IPV4; + + DUMP_END; +} + +static int dump_eui(DUMP_PARAMS) +{ + DUMP_EUI; + + DUMP_END; +} + +static int dump_tsig(DUMP_PARAMS) +{ + if (p->style->wrap) { + DUMP_DNAME; DUMP_SPACE; + DUMP_NUM48; DUMP_SPACE; + DUMP_NUM16; DUMP_SPACE; WRAP_INIT; + DUMP_TSIG_DGST; DUMP_SPACE; WRAP_LINE; + DUMP_NUM16; DUMP_SPACE; + DUMP_TSIG_RCODE; DUMP_SPACE; + DUMP_TSIG_DATA; + WRAP_END; + } else { + DUMP_DNAME; DUMP_SPACE; + DUMP_NUM48; DUMP_SPACE; + DUMP_NUM16; DUMP_SPACE; + DUMP_TSIG_DGST; DUMP_SPACE; + DUMP_NUM16; DUMP_SPACE; + DUMP_TSIG_RCODE; DUMP_SPACE; + DUMP_TSIG_DATA; + } + + DUMP_END; +} + +static int dump_uri(DUMP_PARAMS) +{ + DUMP_NUM16; DUMP_SPACE; + DUMP_NUM16; DUMP_SPACE; + DUMP_LONG_TEXT; DUMP_SPACE; + + DUMP_END; +} + +static int dump_caa(DUMP_PARAMS) +{ + DUMP_NUM8; DUMP_SPACE; + DUMP_UNQUOTED; DUMP_SPACE; + DUMP_LONG_TEXT; DUMP_SPACE; + + DUMP_END; +} + +static int dump_unknown(DUMP_PARAMS) +{ + if (p->style->wrap) { + WRAP_INIT; + DUMP_UNKNOWN; + WRAP_END; + } else { + DUMP_UNKNOWN; + } + + DUMP_END; +} + +static int txt_dump_data(rrset_dump_params_t *p, uint16_t type) +{ + switch (type) { + case KNOT_RRTYPE_A: + return dump_a(p); + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + return dump_ns(p); + case KNOT_RRTYPE_SOA: + return dump_soa(p); + case KNOT_RRTYPE_HINFO: + return dump_hinfo(p); + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + return dump_minfo(p); + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + return dump_mx(p); + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + return dump_txt(p); + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + return dump_dnskey(p); + case KNOT_RRTYPE_AAAA: + return dump_aaaa(p); + case KNOT_RRTYPE_LOC: + return dump_loc(p); + case KNOT_RRTYPE_SRV: + return dump_srv(p); + case KNOT_RRTYPE_NAPTR: + return dump_naptr(p); + case KNOT_RRTYPE_CERT: + return dump_cert(p); + case KNOT_RRTYPE_APL: + return dump_apl(p); + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + return dump_ds(p); + case KNOT_RRTYPE_SSHFP: + return dump_sshfp(p); + case KNOT_RRTYPE_IPSECKEY: + return dump_ipseckey(p); + case KNOT_RRTYPE_RRSIG: + return dump_rrsig(p); + case KNOT_RRTYPE_NSEC: + return dump_nsec(p); + case KNOT_RRTYPE_DHCID: + return dump_dhcid(p); + case KNOT_RRTYPE_NSEC3: + return dump_nsec3(p); + case KNOT_RRTYPE_NSEC3PARAM: + return dump_nsec3param(p); + case KNOT_RRTYPE_TLSA: + return dump_tlsa(p); + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + return dump_l64(p); + case KNOT_RRTYPE_L32: + return dump_l32(p); + case KNOT_RRTYPE_EUI48: + case KNOT_RRTYPE_EUI64: + return dump_eui(p); + case KNOT_RRTYPE_TSIG: + return dump_tsig(p); + case KNOT_RRTYPE_URI: + return dump_uri(p); + case KNOT_RRTYPE_CAA: + return dump_caa(p); + default: + return dump_unknown(p); + } +} + +_public_ +int knot_rrset_txt_dump_data(const knot_rrset_t *rrset, + const size_t pos, + char *dst, + const size_t maxlen, + const knot_dump_style_t *style) +{ + if (rrset == NULL || dst == NULL || style == NULL) { + return KNOT_EINVAL; + } + + knot_rdata_t *rr_data = knot_rdataset_at(&rrset->rrs, pos); + if (rr_data == NULL) { + return KNOT_EINVAL; /* bad pos or rrset->rrs */ + } + + uint8_t *data = rr_data->data; + uint16_t data_len = rr_data->len; + + rrset_dump_params_t p = { + .style = style, + .in = data, + .in_max = data_len, + .out = dst, + .out_max = maxlen, + .total = 0, + .ret = 0 + }; + + int ret; + + // Allow empty rdata with the CH class (knsupdate). + if (data_len == 0 && rrset->rclass != KNOT_CLASS_IN) { + ret = 0; + } else if (style->generic) { + ret = dump_unknown(&p); + } else { + ret = txt_dump_data(&p, rrset->type); + } + + // Terminate the string just in case. + if (ret < 0 || ret >= maxlen) { + return KNOT_ESPACE; + } + dst[ret] = '\0'; + + return ret; +} + +#define SNPRINTF_CHECK(ret, max_len) \ + if ((ret) < 0 || (size_t)(ret) >= (max_len)) { \ + return KNOT_ESPACE; \ + } + +_public_ +int knot_rrset_txt_dump_header(const knot_rrset_t *rrset, + const uint32_t ttl, + char *dst, + const size_t maxlen, + const knot_dump_style_t *style) +{ + if (rrset == NULL || dst == NULL || style == NULL) { + return KNOT_EINVAL; + } + + size_t len = 0; + char buf[32]; + int ret; + + // Dump rrset owner. + char *name = knot_dname_to_str_alloc(rrset->owner); + if (style->ascii_to_idn != NULL) { + style->ascii_to_idn(&name); + } + char sep = strlen(name) < 4 * TAB_WIDTH ? '\t' : ' '; + ret = snprintf(dst + len, maxlen - len, "%-20s%c", name, sep); + free(name); + SNPRINTF_CHECK(ret, maxlen - len); + len += ret; + + // Set white space separation character. + sep = style->wrap ? ' ' : '\t'; + + // Dump rrset ttl. + if (style->show_ttl) { + if (style->empty_ttl) { + ret = snprintf(dst + len, maxlen - len, "%c", sep); + } else if (style->human_ttl) { + // Create human readable ttl string. + if (time_to_human_str(buf, sizeof(buf), ttl) < 0) { + return KNOT_ESPACE; + } + ret = snprintf(dst + len, maxlen - len, "%s%c", + buf, sep); + } else { + ret = snprintf(dst + len, maxlen - len, "%u%c", ttl, sep); + } + SNPRINTF_CHECK(ret, maxlen - len); + len += ret; + } + + // Dump rrset class. + if (style->show_class) { + if (knot_rrclass_to_string(rrset->rclass, buf, sizeof(buf)) < 0) { + return KNOT_ESPACE; + } + ret = snprintf(dst + len, maxlen - len, "%-2s%c", buf, sep); + SNPRINTF_CHECK(ret, maxlen - len); + len += ret; + } + + // Dump rrset type. + if (style->generic) { + if (snprintf(buf, sizeof(buf), "TYPE%u", rrset->type) < 0) { + return KNOT_ESPACE; + } + } else if (knot_rrtype_to_string(rrset->type, buf, sizeof(buf)) < 0) { + return KNOT_ESPACE; + } + if (rrset->rrs.count > 0) { + ret = snprintf(dst + len, maxlen - len, "%s%c", buf, sep); + } else { + ret = snprintf(dst + len, maxlen - len, "%s", buf); + } + SNPRINTF_CHECK(ret, maxlen - len); + len += ret; + + return len; +} + +static int rrset_txt_dump(const knot_rrset_t *rrset, + char *dst, + const size_t maxlen, + const knot_dump_style_t *style) +{ + if (rrset == NULL || dst == NULL || style == NULL) { + return KNOT_EINVAL; + } + + size_t len = 0; + + dst[0] = '\0'; + + // Loop over rdata in rrset. + uint16_t rr_count = rrset->rrs.count; + knot_rdata_t *rr = rrset->rrs.rdata; + for (uint16_t i = 0; i < rr_count; i++) { + // Dump rdata owner, class, ttl and type. + uint32_t ttl = ((style->original_ttl && rrset->type == KNOT_RRTYPE_RRSIG) ? + knot_rrsig_original_ttl(rr) : rrset->ttl); + + int ret = knot_rrset_txt_dump_header(rrset, ttl, dst + len, + maxlen - len, style); + if (ret < 0) { + return KNOT_ESPACE; + } + len += ret; + + // Dump rdata as such. + ret = knot_rrset_txt_dump_data(rrset, i, dst + len, + maxlen - len, style); + if (ret < 0) { + return KNOT_ESPACE; + } + len += ret; + + // Terminate line. + if (len >= maxlen - 1) { + return KNOT_ESPACE; + } + dst[len++] = '\n'; + dst[len] = '\0'; + + rr = knot_rdataset_next(rr); + } + + return len; +} + +_public_ +int knot_rrset_txt_dump(const knot_rrset_t *rrset, + char **dst, + size_t *dst_size, + const knot_dump_style_t *style) +{ + if (dst == NULL || dst_size == NULL) { + return KNOT_EINVAL; + } + + while (1) { + int ret = rrset_txt_dump(rrset, *dst, *dst_size, style); + if (ret != KNOT_ESPACE) { + return ret; + } + + size_t new_dst_size = 2 * (*dst_size); + if (new_dst_size > RRSET_DUMP_LIMIT) { + return KNOT_ESPACE; + } + + char * new_dst = malloc(new_dst_size); + if (new_dst == NULL) { + return KNOT_ENOMEM; + } + + free(*dst); + *dst = new_dst; + *dst_size = new_dst_size; + } +} diff --git a/src/libknot/rrset-dump.h b/src/libknot/rrset-dump.h new file mode 100644 index 0000000..3bd05de --- /dev/null +++ b/src/libknot/rrset-dump.h @@ -0,0 +1,112 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \file + * + * \brief RRset text dump facility. + * + * \addtogroup rr + * @{ + */ + +#pragma once + +#include <stdbool.h> + +#include "libknot/rrset.h" + +/*! \brief Text output settings. */ +typedef struct { + /*!< Wrap long records. */ + bool wrap; + /*!< Show class. */ + bool show_class; + /*!< Show TTL. */ + bool show_ttl; + /*!< Print extra information. */ + bool verbose; + /*!< Print RRSIG original TTL instead of rrset TTL. */ + bool original_ttl; + /*!< Show empty TTL value (keep indentation). */ + bool empty_ttl; + /*!< Format TTL as DHMS. */ + bool human_ttl; + /*!< Format timestamp as YYYYMMDDHHmmSS. */ + bool human_tmstamp; + /*!< Force generic data representation. */ + bool generic; + /*!< Hide binary parts of RRSIGs and DNSKEYs. */ + bool hide_crypto; + /*!< ASCII string to IDN string transformation callback. */ + void (*ascii_to_idn)(char **name); +} knot_dump_style_t; + +/*! \brief Default dump style. */ +extern const knot_dump_style_t KNOT_DUMP_STYLE_DEFAULT; + +/*! + * \brief Dumps rrset header. + * + * \param rrset RRset to dump. + * \param ttl TTL to dump. + * \param dst Output buffer. + * \param maxlen Output buffer size. + * \param style Output style. + * + * \retval output length if success. + * \retval < 0 if error. + */ +int knot_rrset_txt_dump_header(const knot_rrset_t *rrset, + const uint32_t ttl, + char *dst, + const size_t maxlen, + const knot_dump_style_t *style); + +/*! + * \brief Dumps rrset data. + * + * \param rrset RRset to dump. + * \param pos Position of the record to dump. + * \param dst Output buffer. + * \param maxlen Output buffer size. + * \param style Output style. + * + * \retval output length if success. + * \retval < 0 if error. + */ +int knot_rrset_txt_dump_data(const knot_rrset_t *rrset, + const size_t pos, + char *dst, + const size_t maxlen, + const knot_dump_style_t *style); + +/*! + * \brief Dumps rrset, re-allocates dst to double (4x, 8x, ...) if too small. + * + * \param rrset RRset to dump. + * \param dst Output buffer. + * \param dst_size Output buffer size (changed if *dst re-allocated). + * \param style Output style. + * + * \retval output length if success. + * \retval < 0 if error. + */ +int knot_rrset_txt_dump(const knot_rrset_t *rrset, + char **dst, + size_t *dst_size, + const knot_dump_style_t *style); + +/*! @} */ diff --git a/src/libknot/rrset.c b/src/libknot/rrset.c new file mode 100644 index 0000000..67d7aed --- /dev/null +++ b/src/libknot/rrset.c @@ -0,0 +1,227 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> +#include <stdbool.h> +#include <stdint.h> + +#include "libknot/attribute.h" +#include "libknot/errcode.h" +#include "libknot/rrset.h" +#include "libknot/rrtype/naptr.h" +#include "libknot/rrtype/rrsig.h" +#include "contrib/mempattern.h" + +_public_ +knot_rrset_t *knot_rrset_new(const knot_dname_t *owner, uint16_t type, + uint16_t rclass, uint32_t ttl, knot_mm_t *mm) +{ + knot_dname_t *owner_cpy = knot_dname_copy(owner, mm); + if (owner_cpy == NULL) { + return NULL; + } + + knot_rrset_t *ret = mm_alloc(mm, sizeof(knot_rrset_t)); + if (ret == NULL) { + knot_dname_free(owner_cpy, mm); + return NULL; + } + + knot_rrset_init(ret, owner_cpy, type, rclass, ttl); + + return ret; +} + +_public_ +knot_rrset_t *knot_rrset_copy(const knot_rrset_t *src, knot_mm_t *mm) +{ + if (src == NULL) { + return NULL; + } + + knot_rrset_t *rrset = knot_rrset_new(src->owner, src->type, + src->rclass, src->ttl, mm); + if (rrset == NULL) { + return NULL; + } + + int ret = knot_rdataset_copy(&rrset->rrs, &src->rrs, mm); + if (ret != KNOT_EOK) { + knot_rrset_free(rrset, mm); + return NULL; + } + + return rrset; +} + +_public_ +void knot_rrset_free(knot_rrset_t *rrset, knot_mm_t *mm) +{ + if (rrset == NULL) { + return; + } + + knot_rrset_clear(rrset, mm); + mm_free(mm, rrset); +} + +_public_ +void knot_rrset_clear(knot_rrset_t *rrset, knot_mm_t *mm) +{ + if (rrset == NULL) { + return; + } + + knot_rdataset_clear(&rrset->rrs, mm); + knot_dname_free(rrset->owner, mm); + rrset->owner = NULL; +} + +_public_ +int knot_rrset_add_rdata(knot_rrset_t *rrset, const uint8_t *data, uint16_t len, + knot_mm_t *mm) +{ + if (rrset == NULL || (data == NULL && len > 0)) { + return KNOT_EINVAL; + } + + uint8_t buf[knot_rdata_size(len)]; + knot_rdata_t *rdata = (knot_rdata_t *)buf; + knot_rdata_init(rdata, len, data); + + return knot_rdataset_add(&rrset->rrs, rdata, mm); +} + +_public_ +bool knot_rrset_equal(const knot_rrset_t *r1, + const knot_rrset_t *r2, + knot_rrset_compare_type_t cmp) +{ + if (cmp == KNOT_RRSET_COMPARE_PTR) { + return r1 == r2; + } + + if (r1->type != r2->type) { + return false; + } + + if (r1->owner && r2->owner) { + if (!knot_dname_is_equal(r1->owner, r2->owner)) { + return false; + } + } else if (r1->owner != r2->owner) { // At least one is NULL. + return false; + } + + if (cmp == KNOT_RRSET_COMPARE_WHOLE) { + return knot_rdataset_eq(&r1->rrs, &r2->rrs); + } + + return true; +} + +_public_ +bool knot_rrset_is_nsec3rel(const knot_rrset_t *rr) +{ + if (rr == NULL) { + return false; + } + + /* Is NSEC3 or non-empty RRSIG covering NSEC3. */ + return ((rr->type == KNOT_RRTYPE_NSEC3) || + (rr->type == KNOT_RRTYPE_RRSIG + && knot_rrsig_type_covered(rr->rrs.rdata) == KNOT_RRTYPE_NSEC3)); +} + +_public_ +int knot_rrset_rr_to_canonical(knot_rrset_t *rrset) +{ + if (rrset == NULL || rrset->rrs.count != 1) { + return KNOT_EINVAL; + } + + /* Convert owner for all RRSets. */ + knot_dname_to_lower(rrset->owner); + + /* Convert DNAMEs in RDATA only for RFC4034 types. */ + if (!knot_rrtype_should_be_lowercased(rrset->type)) { + return KNOT_EOK; + } + + const knot_rdata_descriptor_t *desc = knot_get_rdata_descriptor(rrset->type); + if (desc->type_name == NULL) { + desc = knot_get_obsolete_rdata_descriptor(rrset->type); + } + + uint16_t rdlen = rrset->rrs.rdata->len; + uint8_t *pos = rrset->rrs.rdata->data; + uint8_t *endpos = pos + rdlen; + + /* No RDATA */ + if (rdlen == 0) { + return KNOT_EOK; + } + + /* Otherwise, whole and not malformed RDATA are expected. */ + for (int i = 0; desc->block_types[i] != KNOT_RDATA_WF_END; ++i) { + int type = desc->block_types[i]; + switch (type) { + case KNOT_RDATA_WF_COMPRESSIBLE_DNAME: + case KNOT_RDATA_WF_DECOMPRESSIBLE_DNAME: + case KNOT_RDATA_WF_FIXED_DNAME: + knot_dname_to_lower(pos); + pos += knot_dname_size(pos); + break; + case KNOT_RDATA_WF_NAPTR_HEADER: + ; int ret = knot_naptr_header_size(pos, endpos); + if (ret < 0) { + return ret; + } + + pos += ret; + break; + case KNOT_RDATA_WF_REMAINDER: + break; + default: + /* Fixed size block */ + assert(type > 0); + pos += type; + } + } + + return KNOT_EOK; +} + +_public_ +size_t knot_rrset_size(const knot_rrset_t *rrset) +{ + if (rrset == NULL) { + return 0; + } + + uint16_t rr_count = rrset->rrs.count; + + size_t total_size = knot_dname_size(rrset->owner) * rr_count; + + knot_rdata_t *rr = rrset->rrs.rdata; + for (size_t i = 0; i < rr_count; ++i) { + /* 10B = TYPE + CLASS + TTL + RDLENGTH */ + total_size += rr->len + 10; + rr = knot_rdataset_next(rr); + } + + return total_size; +} diff --git a/src/libknot/rrset.h b/src/libknot/rrset.h new file mode 100644 index 0000000..aac9a4c --- /dev/null +++ b/src/libknot/rrset.h @@ -0,0 +1,200 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \file + * + * \brief RRSet structure and API for manipulating it. + * + * \addtogroup rr + * @{ + */ + +#pragma once + +#include <stdint.h> +#include <stdbool.h> + +#include "libknot/dname.h" +#include "libknot/descriptor.h" +#include "libknot/mm_ctx.h" +#include "libknot/rdataset.h" + +/*! + * \brief Structure for representing RRSet. + * + * For RRSet definition see RFC2181, Section 5. + */ +typedef struct { + knot_dname_t *owner; /*!< Domain name being the owner of the RRSet. */ + uint32_t ttl; /*!< TTL of the RRset. */ + uint16_t type; /*!< TYPE of the RRset. */ + uint16_t rclass; /*!< CLASS of the RRSet. */ + knot_rdataset_t rrs; /*!< RRSet's RRs */ + /* Optional fields. */ + void *additional; /*!< Additional records. */ +} knot_rrset_t; + +/*! \todo Documentation */ +typedef enum { + KNOT_RRSET_COMPARE_PTR, + KNOT_RRSET_COMPARE_HEADER, + KNOT_RRSET_COMPARE_WHOLE +} knot_rrset_compare_type_t; + +/*! + * \brief Creates a new RRSet with the given properties. + * + * The created RRSet contains no RDATAs (i.e. is actually empty). + * + * \param owner OWNER of the RRSet. + * \param type TYPE of the RRSet. + * \param rclass CLASS of the RRSet. + * \param ttl TTL of the RRSet. + * \param mm Memory context. + * + * \return New RRSet structure or NULL if an error occurred. + */ +knot_rrset_t *knot_rrset_new(const knot_dname_t *owner, uint16_t type, + uint16_t rclass, uint32_t ttl, knot_mm_t *mm); + +/*! + * \brief Initializes RRSet structure with given data. + * + * \param rrset RRSet to init. + * \param owner RRSet owner to use. + * \param type RR type to use. + * \param rclass Class to use. + * \param ttl TTL to use. + */ +inline static void knot_rrset_init(knot_rrset_t *rrset, knot_dname_t *owner, + uint16_t type, uint16_t rclass, uint32_t ttl) +{ + if (rrset != NULL) { + rrset->owner = owner; + rrset->type = type; + rrset->rclass = rclass; + rrset->ttl = ttl; + knot_rdataset_init(&rrset->rrs); + rrset->additional = NULL; + } +} + +/*! + * \brief Initializes given RRSet structure. + * + * \param rrset RRSet to init. + */ +inline static void knot_rrset_init_empty(knot_rrset_t *rrset) +{ + knot_rrset_init(rrset, NULL, 0, KNOT_CLASS_IN, 0); +} + +/*! + * \brief Creates new RRSet from \a src RRSet. + * + * \param src Source RRSet. + * \param mm Memory context. + * + * \retval Pointer to new RRSet if all went OK. + * \retval NULL on error. + */ +knot_rrset_t *knot_rrset_copy(const knot_rrset_t *src, knot_mm_t *mm); + +/*! + * \brief Destroys the RRSet structure and all its substructures. + * + * \param rrset RRset to be destroyed. + * \param mm Memory context. + */ +void knot_rrset_free(knot_rrset_t *rrset, knot_mm_t *mm); + +/*! + * \brief Frees structures inside RRSet, but not the RRSet itself. + * + * \param rrset RRSet to be cleared. + * \param mm Memory context used for allocations. + */ +void knot_rrset_clear(knot_rrset_t *rrset, knot_mm_t *mm); + +/*! + * \brief Adds the given RDATA to the RRSet. + * + * \param rrset RRSet to add the RDATA to. + * \param data RDATA to add to the RRSet. + * \param len Length of RDATA. + * \param mm Memory context. + * + * \return KNOT_E* + */ +int knot_rrset_add_rdata(knot_rrset_t *rrset, const uint8_t *data, uint16_t len, + knot_mm_t *mm); + +/*! + * \brief Compares two RRSets for equality. + * + * \param r1 First RRSet. + * \param r2 Second RRSet. + * \param cmp Type of comparison to perform. + * + * \retval True if RRSets are equal. + * \retval False if RRSets are not equal. + */ +bool knot_rrset_equal(const knot_rrset_t *r1, const knot_rrset_t *r2, + knot_rrset_compare_type_t cmp); + +/*! + * \brief Checks whether RRSet is empty. + * + * \param rrset RRSet to check. + * + * \retval True if RRSet is empty. + * \retval False if RRSet is not empty. + */ +inline static bool knot_rrset_empty(const knot_rrset_t *rrset) +{ + return rrset == NULL || rrset->rrs.count == 0; +} + +/*! + * \brief Return whether the RR type is NSEC3 related (NSEC3 or RRSIG). + */ +bool knot_rrset_is_nsec3rel(const knot_rrset_t *rr); + +/*! + * \brief Convert one RR into canonical format. + * + * Owner is always converted to lowercase. RDATA domain names are converted only + * for types listed in RFC 4034, Section 6.2, except for NSEC (updated by + * RFC 6840, Section 5.1) and A6 (not supported). + * + * \note If RRSet with more RRs is given to this function, only the first RR + * will be converted. + * \warning This function expects either empty RDATA or full, not malformed + * RDATA. If malformed RRSet is passed to this function, memory errors + * may occur. + * + * \param rrset RR to convert. + */ +int knot_rrset_rr_to_canonical(knot_rrset_t *rrset); + +/*! + * \brief Size of rrset in wire format. + * + * \retval size in bytes + */ +size_t knot_rrset_size(const knot_rrset_t *rrset); + +/*! @} */ diff --git a/src/libknot/rrtype/dnskey.h b/src/libknot/rrtype/dnskey.h new file mode 100644 index 0000000..ff6f4b9 --- /dev/null +++ b/src/libknot/rrtype/dnskey.h @@ -0,0 +1,71 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \file + * + * \addtogroup rrtype + * @{ + */ + +#pragma once + +#include "libknot/rdata.h" +#include "libknot/wire.h" + +/*! See https://www.iana.org/assignments/dnskey-flags */ +/*! /brief "Secure entry point" marks KSK and CSK in practice. */ +#define KNOT_DNSKEY_FLAG_SEP 1 +/*! /brief The key is ALLOWED to be used for zone contents signing. */ +#define KNOT_DNSKEY_FLAG_ZONE 256 +/*! /brief The key MUST NOT be used for validation. */ +#define KNOT_DNSKEY_FLAG_REVOKE 128 + +static inline +uint16_t knot_dnskey_flags(const knot_rdata_t *rdata) +{ + assert(rdata); + return knot_wire_read_u16(rdata->data); +} + +static inline +uint8_t knot_dnskey_proto(const knot_rdata_t *rdata) +{ + assert(rdata); + return *(rdata->data + 2); +} + +static inline +uint8_t knot_dnskey_alg(const knot_rdata_t *rdata) +{ + assert(rdata); + return *(rdata->data + 3); +} + +static inline +uint16_t knot_dnskey_key_len(const knot_rdata_t *rdata) +{ + assert(rdata); + return rdata->len - 4; +} + +static inline +const uint8_t *knot_dnskey_key(const knot_rdata_t *rdata) +{ + assert(rdata); + return rdata->data + 4; +} + +/*! @} */ diff --git a/src/libknot/rrtype/ds.h b/src/libknot/rrtype/ds.h new file mode 100644 index 0000000..d447929 --- /dev/null +++ b/src/libknot/rrtype/ds.h @@ -0,0 +1,63 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \file + * + * \addtogroup rrtype + * @{ + */ + +#pragma once + +#include "libknot/rdata.h" +#include "libknot/wire.h" + +static inline +uint16_t knot_ds_key_tag(const knot_rdata_t *rdata) +{ + assert(rdata); + return knot_wire_read_u16(rdata->data); +} + +static inline +uint8_t knot_ds_alg(const knot_rdata_t *rdata) +{ + assert(rdata); + return *(rdata->data + 2); +} + +static inline +uint8_t knot_ds_digest_type(const knot_rdata_t *rdata) +{ + assert(rdata); + return *(rdata->data + 3); +} + +static inline +uint16_t knot_ds_digest_len(const knot_rdata_t *rdata) +{ + assert(rdata); + return rdata->len - 4; +} + +static inline +const uint8_t *knot_ds_digest(const knot_rdata_t *rdata) +{ + assert(rdata); + return rdata->data + 4; +} + +/*! @} */ diff --git a/src/libknot/rrtype/naptr.c b/src/libknot/rrtype/naptr.c new file mode 100644 index 0000000..ec513ca --- /dev/null +++ b/src/libknot/rrtype/naptr.c @@ -0,0 +1,47 @@ +/* Copyright (C) 2015 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <assert.h> +#include <stdint.h> + +#include "libknot/attribute.h" +#include "libknot/rrtype/naptr.h" +#include "contrib/wire_ctx.h" + +_public_ +int knot_naptr_header_size(const uint8_t *naptr, const uint8_t *maxp) +{ + if (!naptr || !maxp || naptr >= maxp) { + return KNOT_EINVAL; + } + + wire_ctx_t wire = wire_ctx_init_const(naptr, maxp - naptr); + + /* Fixed fields size (order, preference) */ + wire_ctx_skip(&wire, 2 * sizeof(uint16_t)); + + /* Variable fields size (flags, services, regexp) */ + for (int i = 0; i < 3; i++) { + uint8_t size = wire_ctx_read_u8(&wire); + wire_ctx_skip(&wire, size); + } + + if (wire.error != KNOT_EOK) { + return KNOT_EMALF; + } + + return wire_ctx_offset(&wire); +} diff --git a/src/libknot/rrtype/naptr.h b/src/libknot/rrtype/naptr.h new file mode 100644 index 0000000..a067f64 --- /dev/null +++ b/src/libknot/rrtype/naptr.h @@ -0,0 +1,40 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \file + * + * \addtogroup rrtype + * @{ + */ + +#pragma once + +#include <stdint.h> + +/*! + * \brief Counts the size of the NAPTR RDATA before the Replacement domain name. + * + * See RFC 2915. + * + * \param naptr Wire format of NAPTR record. + * \param maxp Limit of the wire format. + * + * \retval KNOT_EMALF if the record is malformed. + * \retval Size of the RDATA before the Replacement domain name. + */ +int knot_naptr_header_size(const uint8_t *naptr, const uint8_t *maxp); + +/*! @} */ diff --git a/src/libknot/rrtype/nsec.h b/src/libknot/rrtype/nsec.h new file mode 100644 index 0000000..896ab3a --- /dev/null +++ b/src/libknot/rrtype/nsec.h @@ -0,0 +1,49 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \file + * + * \addtogroup rrtype + * @{ + */ + +#pragma once + +#include "libknot/dname.h" +#include "libknot/rdata.h" + +static inline +const knot_dname_t *knot_nsec_next(const knot_rdata_t *rdata) +{ + assert(rdata); + return rdata->data; +} + +static inline +uint16_t knot_nsec_bitmap_len(const knot_rdata_t *rdata) +{ + assert(rdata); + return rdata->len - knot_dname_size(knot_nsec_next(rdata)); +} + +static inline +const uint8_t *knot_nsec_bitmap(const knot_rdata_t *rdata) +{ + assert(rdata); + return rdata->data + knot_dname_size(knot_nsec_next(rdata)); +} + +/*! @} */ diff --git a/src/libknot/rrtype/nsec3.h b/src/libknot/rrtype/nsec3.h new file mode 100644 index 0000000..f69c774 --- /dev/null +++ b/src/libknot/rrtype/nsec3.h @@ -0,0 +1,97 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \file + * + * \addtogroup rrtype + * @{ + */ + +#pragma once + +#include "libknot/rdata.h" +#include "libknot/wire.h" + +/*! + * \brief NSEC3 rdata constants. + */ +#define KNOT_NSEC3_ALGORITHM_SHA1 1 +#define KNOT_NSEC3_FLAG_OPT_OUT 1 + +static inline +uint8_t knot_nsec3_alg(const knot_rdata_t *rdata) +{ + assert(rdata); + return *(rdata->data); +} + +static inline +uint8_t knot_nsec3_flags(const knot_rdata_t *rdata) +{ + assert(rdata); + return *(rdata->data + 1); +} + +static inline +uint16_t knot_nsec3_iters(const knot_rdata_t *rdata) +{ + assert(rdata); + return knot_wire_read_u16(rdata->data + 2); +} + +static inline +uint8_t knot_nsec3_salt_len(const knot_rdata_t *rdata) +{ + assert(rdata); + return *(rdata->data + 4); +} + +static inline +const uint8_t *knot_nsec3_salt(const knot_rdata_t *rdata) +{ + assert(rdata); + return rdata->data + 5; +} + +static inline +uint8_t knot_nsec3_next_len(const knot_rdata_t *rdata) +{ + assert(rdata); + return *(rdata->data + 5 + knot_nsec3_salt_len(rdata)); +} + +static inline +const uint8_t *knot_nsec3_next(const knot_rdata_t *rdata) +{ + assert(rdata); + return rdata->data + 6 + knot_nsec3_salt_len(rdata); +} + +static inline +uint16_t knot_nsec3_bitmap_len(const knot_rdata_t *rdata) +{ + assert(rdata); + return rdata->len - 6 - knot_nsec3_salt_len(rdata) - knot_nsec3_next_len(rdata); +} + +static inline +const uint8_t *knot_nsec3_bitmap(const knot_rdata_t *rdata) +{ + assert(rdata); + return rdata->data + 6 + knot_nsec3_salt_len(rdata) + knot_nsec3_next_len(rdata); +} + +/*! @} */ diff --git a/src/libknot/rrtype/nsec3param.h b/src/libknot/rrtype/nsec3param.h new file mode 100644 index 0000000..239271a --- /dev/null +++ b/src/libknot/rrtype/nsec3param.h @@ -0,0 +1,63 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \file + * + * \addtogroup rrtype + * @{ + */ + +#pragma once + +#include "libknot/rdata.h" +#include "libknot/wire.h" + +static inline +uint8_t knot_nsec3param_alg(const knot_rdata_t *rdata) +{ + assert(rdata); + return *(rdata->data); +} + +static inline +uint8_t knot_nsec3param_flags(const knot_rdata_t *rdata) +{ + assert(rdata); + return *(rdata->data + 1); +} + +static inline +uint16_t knot_nsec3param_iters(const knot_rdata_t *rdata) +{ + assert(rdata); + return knot_wire_read_u16(rdata->data + 2); +} + +static inline +uint8_t knot_nsec3param_salt_len(const knot_rdata_t *rdata) +{ + assert(rdata); + return *(rdata->data + 4); +} + +static inline +const uint8_t *knot_nsec3param_salt(const knot_rdata_t *rdata) +{ + assert(rdata); + return rdata->data + 5; +} + +/*! @} */ diff --git a/src/libknot/rrtype/opt.c b/src/libknot/rrtype/opt.c new file mode 100644 index 0000000..3784858 --- /dev/null +++ b/src/libknot/rrtype/opt.c @@ -0,0 +1,675 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <netinet/in.h> +#include <sys/socket.h> + +#include "libknot/attribute.h" +#include "libknot/consts.h" +#include "libknot/descriptor.h" +#include "libknot/rrtype/opt.h" +#include "libknot/packet/pkt.h" +#include "contrib/mempattern.h" +#include "contrib/wire_ctx.h" + +/*! \brief Some implementation-related constants. */ +enum { + /*! \brief Byte offset of the extended RCODE field in TTL. */ + EDNS_OFFSET_RCODE = 0, + /*! \brief Byte offset of the version field in TTL. */ + EDNS_OFFSET_VERSION = 1, + + /*! \brief Byte offset of the family field in option data. */ + EDNS_OFFSET_CLIENT_SUBNET_FAMILY = 0, + /*! \brief Byte offset of the source mask field in option data. */ + EDNS_OFFSET_CLIENT_SUBNET_SRC_MASK = 2, + /*! \brief Byte offset of the destination mask field in option data. */ + EDNS_OFFSET_CLIENT_SUBNET_DST_MASK = 3, + /*! \brief Byte offset of the address field in option data. */ + EDNS_OFFSET_CLIENT_SUBNET_ADDR = 4, +}; + +_public_ +int knot_edns_init(knot_rrset_t *opt_rr, uint16_t max_pld, + uint8_t ext_rcode, uint8_t ver, knot_mm_t *mm) +{ + if (opt_rr == NULL) { + return KNOT_EINVAL; + } + + /* Initialize RRSet. */ + knot_dname_t *owner = knot_dname_copy((const uint8_t *)"", mm); + if (owner == NULL) { + return KNOT_ENOMEM; + } + + knot_rrset_init(opt_rr, owner, KNOT_RRTYPE_OPT, max_pld, 0); + + /* Create empty RDATA */ + int ret = knot_rrset_add_rdata(opt_rr, NULL, 0, mm); + if (ret == KNOT_EOK) { + knot_edns_set_ext_rcode(opt_rr, ext_rcode); + knot_edns_set_version(opt_rr, ver); + } + + return ret; +} + +_public_ +uint8_t knot_edns_get_ext_rcode(const knot_rrset_t *opt_rr) +{ + assert(opt_rr != NULL); + uint32_t ttl = 0; + wire_ctx_t w = wire_ctx_init((uint8_t *)&ttl, sizeof(ttl)); + // TTL is stored in machine byte order. Convert it to wire order first. + wire_ctx_write_u32(&w, opt_rr->ttl); + wire_ctx_set_offset(&w, EDNS_OFFSET_RCODE); + return wire_ctx_read_u8(&w); +} + +static void set_value_to_ttl(knot_rrset_t *opt_rr, size_t offset, uint8_t value) +{ + uint32_t ttl = 0; + wire_ctx_t w = wire_ctx_init((uint8_t *)&ttl, sizeof(ttl)); + // TTL is stored in machine byte order. Convert it to wire order first. + wire_ctx_write_u32(&w, opt_rr->ttl); + // Set the Extended RCODE in the converted TTL + wire_ctx_set_offset(&w, offset); + wire_ctx_write_u8(&w, value); + // Convert it back to machine byte order + wire_ctx_set_offset(&w, 0); + uint32_t ttl_local = wire_ctx_read_u32(&w); + // Store the TTL to the RDATA + opt_rr->ttl = ttl_local; +} + +_public_ +void knot_edns_set_ext_rcode(knot_rrset_t *opt_rr, uint8_t ext_rcode) +{ + assert(opt_rr != NULL); + set_value_to_ttl(opt_rr, EDNS_OFFSET_RCODE, ext_rcode); +} + +_public_ +uint8_t knot_edns_get_version(const knot_rrset_t *opt_rr) +{ + assert(opt_rr != NULL); + uint32_t ttl = 0; + wire_ctx_t w = wire_ctx_init((uint8_t *)&ttl, sizeof(ttl)); + // TTL is stored in machine byte order. Convert it to wire order first. + wire_ctx_write_u32(&w, opt_rr->ttl); + wire_ctx_set_offset(&w, EDNS_OFFSET_VERSION); + return wire_ctx_read_u8(&w); +} + +_public_ +void knot_edns_set_version(knot_rrset_t *opt_rr, uint8_t version) +{ + assert(opt_rr != NULL); + set_value_to_ttl(opt_rr, EDNS_OFFSET_VERSION, version); +} + +/*! + * \brief Add new EDNS option by replacing RDATA of OPT RR. + * + * \param opt OPT RR structure to add the Option to. + * \param code Option code. + * \param size Option data length in bytes. + * \param mm Memory context. + * + * \return Pointer to uninitialized option data. + */ +static uint8_t *edns_add(knot_rrset_t *opt, uint16_t code, uint16_t size, + knot_mm_t *mm) +{ + assert(opt->rrs.count == 1); + + // extract old RDATA + + uint8_t *old_data = opt->rrs.rdata->data; + uint16_t old_data_len = opt->rrs.rdata->len; + + // construct new RDATA + + uint16_t new_data_len = old_data_len + KNOT_EDNS_OPTION_HDRLEN + size; + uint8_t new_data[new_data_len]; + + wire_ctx_t wire = wire_ctx_init(new_data, new_data_len); + wire_ctx_write(&wire, old_data, old_data_len); + wire_ctx_write_u16(&wire, code); + wire_ctx_write_u16(&wire, size); + + // prepare EDNS option data + + size_t offset = wire_ctx_offset(&wire); + wire_ctx_clear(&wire, size); + + assert(wire_ctx_available(&wire) == 0); + assert(wire.error == KNOT_EOK); + + // replace RDATA + + knot_rdataset_clear(&opt->rrs, mm); + if (knot_rrset_add_rdata(opt, new_data, new_data_len, mm) != KNOT_EOK) { + return NULL; + } + + return opt->rrs.rdata->data + offset; +} + +_public_ +int knot_edns_reserve_option(knot_rrset_t *opt_rr, uint16_t code, + uint16_t size, uint8_t **wire_ptr, knot_mm_t *mm) +{ + if (!opt_rr) { + return KNOT_EINVAL; + } + + uint8_t *wire = edns_add(opt_rr, code, size, mm); + if (!wire) { + return KNOT_ENOMEM; + } + + if (wire_ptr) { + *wire_ptr = wire; + } + + return KNOT_EOK; +} + +_public_ +int knot_edns_add_option(knot_rrset_t *opt_rr, uint16_t code, + uint16_t size, const uint8_t *data, knot_mm_t *mm) +{ + if (!opt_rr || (size > 0 && !data)) { + return KNOT_EINVAL; + } + + uint8_t *wire = edns_add(opt_rr, code, size, mm); + if (!wire) { + return KNOT_ENOMEM; + } + + memcpy(wire, data, size); + + return KNOT_EOK; +} + +_public_ +uint8_t *knot_edns_get_option(const knot_rrset_t *opt_rr, uint16_t code) +{ + if (opt_rr == NULL) { + return NULL; + } + + knot_rdata_t *rdata = opt_rr->rrs.rdata; + if (rdata == NULL || rdata->len == 0) { + return NULL; + } + + wire_ctx_t wire = wire_ctx_init_const(rdata->data, rdata->len); + + while (wire_ctx_available(&wire) > 0 && wire.error == KNOT_EOK) { + uint8_t *pos = wire.position; + uint16_t opt_code = wire_ctx_read_u16(&wire); + uint16_t opt_len = wire_ctx_read_u16(&wire); + wire_ctx_skip(&wire, opt_len); + if (wire.error == KNOT_EOK && opt_code == code) { + return pos; + } + } + + return NULL; +} + +_public_ +int knot_edns_get_options(knot_rrset_t *opt_rr, knot_edns_options_t **out, + knot_mm_t *mm) +{ + if (opt_rr == NULL || opt_rr->rrs.count > 1 || out == NULL) { + return KNOT_EINVAL; + } + + knot_rdata_t *rdata = opt_rr->rrs.rdata; + if (rdata == NULL || rdata->len == 0) { + return KNOT_EOK; + } + + knot_edns_options_t *options = mm_calloc(mm, 1, sizeof(*options)); + + wire_ctx_t wire = wire_ctx_init_const(rdata->data, rdata->len); + + while (wire_ctx_available(&wire) > 0 && wire.error == KNOT_EOK) { + uint8_t *pos = wire.position; + uint16_t opt_code = wire_ctx_read_u16(&wire); + uint16_t opt_len = wire_ctx_read_u16(&wire); + wire_ctx_skip(&wire, opt_len); + if (wire.error == KNOT_EOK && opt_code <= KNOT_EDNS_MAX_OPTION_CODE) { + options->ptr[opt_code] = pos; + } + } + + if (wire.error != KNOT_EOK) { + mm_free(mm, options); + return wire.error; + } + + *out = options; + return KNOT_EOK; +} + +_public_ +int knot_edns_alignment_size(size_t current_pkt_size, + size_t current_opt_size, + size_t block_size) +{ + if (current_opt_size == 0 || block_size == 0) { + return -1; + } + + size_t current_size = current_pkt_size + current_opt_size; + if (current_size % block_size == 0) { + return -1; + } + + size_t modulo = (current_size + KNOT_EDNS_OPTION_HDRLEN) % block_size; + + return (modulo == 0) ? 0 : block_size - modulo; +} + +/*! + * \brief EDNS Client Subnet family data. + */ +typedef struct { + int platform; //!< Platform family identifier. + uint16_t iana; //!< IANA family identifier. + size_t offset; //!< Socket address offset. + size_t size; //!< Socket address size. +} ecs_family_t; + +#define ECS_INIT(platform, iana, type, member) \ + { platform, iana, offsetof(type, member), sizeof(((type *)0)->member) } + +/*! + * \brief Supported EDNS Client Subnet families. + * + * http://www.iana.org/assignments/address-family-numbers/address-family-numbers.xml + */ +static const ecs_family_t ECS_FAMILIES[] = { + ECS_INIT(AF_INET, KNOT_ADDR_FAMILY_IPV4, struct sockaddr_in, sin_addr), + ECS_INIT(AF_INET6, KNOT_ADDR_FAMILY_IPV6, struct sockaddr_in6, sin6_addr), + { 0 } +}; + +/*! + * \brief Lookup ECS family by platform identifier. + */ +static const ecs_family_t *ecs_family_by_platform(int family) +{ + for (const ecs_family_t *f = ECS_FAMILIES; f->size > 0; f++) { + if (f->platform == family) { + return f; + } + } + + return NULL; +} + +/*! + * \brief Lookup ECS family by IANA identifier. + */ +static const ecs_family_t *ecs_family_by_iana(uint16_t family) +{ + for (const ecs_family_t *f = ECS_FAMILIES; f->size > 0; f++) { + if (f->iana == family) { + return f; + } + } + + return NULL; +} + +/*! + * \brief Get ECS address prefix size in bytes. + */ +static uint16_t ecs_prefix_size(uint8_t prefix) +{ + return (prefix + 7) / 8; +} + +static uint8_t ecs_prefix_lsb_mask(uint8_t prefix) +{ + int modulo = prefix % 8; + if (modulo == 0) { + return 0xff; + } else { + return 0xff << (8 - modulo); + } +} + +/*! + * \brief Write raw network address prefix and clear the rest of the buffer. + */ +static void ecs_write_address(wire_ctx_t *dst, wire_ctx_t *src, int8_t prefix) +{ + size_t count = ecs_prefix_size(prefix); + uint8_t lsb_mask = ecs_prefix_lsb_mask(prefix); + + if (count > 0) { + wire_ctx_copy(dst, src, count); + if (dst->error != KNOT_EOK) { + return; + } + dst->position[-1] &= lsb_mask; + } + + size_t blank = wire_ctx_available(dst); + wire_ctx_memset(dst, 0, blank); +} + +/*! + * \brief Check if ECS parameters are valid. + */ +static bool ecs_is_valid(const knot_edns_client_subnet_t *ecs) +{ + if (ecs == NULL) { + return false; + } + + const ecs_family_t *f = ecs_family_by_iana(ecs->family); + + return f != NULL && // known family check + (ecs->source_len <= f->size * 8) && // family address maximum check + (ecs->scope_len <= f->size * 8); // family address maximum check +} + +_public_ +uint16_t knot_edns_client_subnet_size(const knot_edns_client_subnet_t *ecs) +{ + if (!ecs_is_valid(ecs)) { + return 0; + } + + return sizeof(ecs->family) + + sizeof(ecs->source_len) + + sizeof(ecs->scope_len) + + ecs_prefix_size(ecs->source_len); +} + +_public_ +int knot_edns_client_subnet_write(uint8_t *option, uint16_t option_len, + const knot_edns_client_subnet_t *ecs) +{ + if (option == NULL || ecs == NULL) { + return KNOT_EINVAL; + } + + if (!ecs_is_valid(ecs)) { + return KNOT_EINVAL; + } + + wire_ctx_t wire = wire_ctx_init(option, option_len); + wire_ctx_t addr = wire_ctx_init_const(ecs->address, sizeof(ecs->address)); + + wire_ctx_write_u16(&wire, ecs->family); + wire_ctx_write_u8(&wire, ecs->source_len); + wire_ctx_write_u8(&wire, ecs->scope_len); + ecs_write_address(&wire, &addr, ecs->source_len); + + if (wire.error != KNOT_EOK) { + return wire.error; + } + + return KNOT_EOK; +} + +_public_ +int knot_edns_client_subnet_parse(knot_edns_client_subnet_t *ecs, + const uint8_t *option, uint16_t option_len) +{ + if (ecs == NULL || option == NULL) { + return KNOT_EINVAL; + } + + knot_edns_client_subnet_t result = { 0 }; + + wire_ctx_t wire = wire_ctx_init_const(option, option_len); + wire_ctx_t addr = wire_ctx_init(result.address, sizeof(result.address)); + + result.family = wire_ctx_read_u16(&wire); + result.source_len = wire_ctx_read_u8(&wire); + result.scope_len = wire_ctx_read_u8(&wire); + ecs_write_address(&addr, &wire, result.source_len); + + if (addr.error != KNOT_EOK || wire.error != KNOT_EOK) { + return KNOT_EMALF; + } + + if (!ecs_is_valid(&result)) { + return KNOT_EMALF; + } + + *ecs = result; + return KNOT_EOK; +} + +_public_ +int knot_edns_client_subnet_set_addr(knot_edns_client_subnet_t *ecs, + const struct sockaddr_storage *addr) +{ + if (ecs == NULL || addr == NULL) { + return KNOT_EINVAL; + } + + const ecs_family_t *f = ecs_family_by_platform(addr->ss_family); + if (f == NULL) { + return KNOT_ENOTSUP; + } + + ecs->family = f->iana; + ecs->source_len = f->size * 8; + ecs->scope_len = 0; + + wire_ctx_t dst = wire_ctx_init(ecs->address, sizeof(ecs->address)); + wire_ctx_t src = wire_ctx_init_const((uint8_t *)addr + f->offset, f->size); + ecs_write_address(&dst, &src, ecs->source_len); + + assert(dst.error == KNOT_EOK); + + return KNOT_EOK; +} + +_public_ +int knot_edns_client_subnet_get_addr(struct sockaddr_storage *addr, + const knot_edns_client_subnet_t *ecs) +{ + if (addr == NULL || ecs == NULL) { + return KNOT_EINVAL; + } + + const ecs_family_t *f = ecs_family_by_iana(ecs->family); + if (f == NULL) { + return KNOT_ENOTSUP; + } + + addr->ss_family = f->platform; + + wire_ctx_t dst = wire_ctx_init((uint8_t *)addr + f->offset, f->size); + wire_ctx_t src = wire_ctx_init_const(ecs->address, sizeof(ecs->address)); + ecs_write_address(&dst, &src, ecs->source_len); + + assert(dst.error == KNOT_EOK); + + return KNOT_EOK; +} + +_public_ +uint16_t knot_edns_keepalive_size(uint16_t timeout) +{ + return (timeout > 0) ? sizeof(uint16_t) : 0; +} + +_public_ +int knot_edns_keepalive_write(uint8_t *option, uint16_t option_len, uint16_t timeout) +{ + if (option == NULL) { + return KNOT_EINVAL; + } + + if (timeout == 0) { + return KNOT_EOK; + } + + wire_ctx_t wire = wire_ctx_init(option, option_len); + wire_ctx_write_u16(&wire, timeout); + + return wire.error; +} + +_public_ +int knot_edns_keepalive_parse(uint16_t *timeout, const uint8_t *option, + uint16_t option_len) +{ + if (timeout == NULL || option == NULL) { + return KNOT_EINVAL; + } + + *timeout = 0; + + if (option_len > 0) { + wire_ctx_t wire = wire_ctx_init_const(option, option_len); + *timeout = wire_ctx_read_u16(&wire); + + if (wire.error != KNOT_EOK) { + return KNOT_EMALF; + } + } + + return KNOT_EOK; +} + +_public_ +uint16_t knot_edns_chain_size(const knot_dname_t *point) +{ + return knot_dname_size(point); +} + +_public_ +int knot_edns_chain_write(uint8_t *option, uint16_t option_len, + const knot_dname_t *point) +{ + if (option == NULL || point == NULL) { + return KNOT_EINVAL; + } + + wire_ctx_t wire = wire_ctx_init(option, option_len); + wire_ctx_write(&wire, point, knot_dname_size(point)); + + return wire.error; +} + +_public_ +int knot_edns_chain_parse(knot_dname_t **point, const uint8_t *option, + uint16_t option_len, knot_mm_t *mm) +{ + if (point == NULL || option == NULL) { + return KNOT_EINVAL; + } + + int ret = knot_dname_wire_check(option, option + option_len, NULL); + if (ret <= 0) { + return KNOT_EMALF; + } + + *point = knot_dname_copy(option, mm); + if (*point == NULL) { + return KNOT_ENOMEM; + } + + return KNOT_EOK; +} + +_public_ +uint16_t knot_edns_cookie_size(const knot_edns_cookie_t *cc, + const knot_edns_cookie_t *sc) +{ + if (cc == NULL || cc->len != KNOT_EDNS_COOKIE_CLNT_SIZE) { + return 0; + } else if (sc == NULL || sc->len == 0) { + return KNOT_EDNS_COOKIE_CLNT_SIZE; + } else if (sc->len < KNOT_EDNS_COOKIE_SRVR_MIN_SIZE || + sc->len > KNOT_EDNS_COOKIE_SRVR_MAX_SIZE) { + return 0; + } else { + return cc->len + sc->len; + } +} + +_public_ +int knot_edns_cookie_write(uint8_t *option, uint16_t option_len, + const knot_edns_cookie_t *cc, + const knot_edns_cookie_t *sc) +{ + if (option == NULL || cc == NULL || cc->len != KNOT_EDNS_COOKIE_CLNT_SIZE) { + return KNOT_EINVAL; + } + + wire_ctx_t wire = wire_ctx_init(option, option_len); + wire_ctx_write(&wire, cc->data, cc->len); + + if (sc != NULL && sc->len > 0) { + if (sc->len < KNOT_EDNS_COOKIE_SRVR_MIN_SIZE || + sc->len > KNOT_EDNS_COOKIE_SRVR_MAX_SIZE) { + return KNOT_EINVAL; + } + wire_ctx_write(&wire, sc->data, sc->len); + } + + return wire.error; +} + +_public_ +int knot_edns_cookie_parse(knot_edns_cookie_t *cc, knot_edns_cookie_t *sc, + const uint8_t *option, uint16_t option_len) +{ + if (cc == NULL || sc == NULL || option == NULL) { + return KNOT_EINVAL; + } + + if (option_len != KNOT_EDNS_COOKIE_CLNT_SIZE && + (option_len < KNOT_EDNS_COOKIE_CLNT_SIZE + KNOT_EDNS_COOKIE_SRVR_MIN_SIZE || + option_len > KNOT_EDNS_COOKIE_CLNT_SIZE + KNOT_EDNS_COOKIE_SRVR_MAX_SIZE)) { + return KNOT_EMALF; + } + assert(option_len >= KNOT_EDNS_COOKIE_CLNT_SIZE); + + memcpy(cc->data, option, KNOT_EDNS_COOKIE_CLNT_SIZE); + cc->len = KNOT_EDNS_COOKIE_CLNT_SIZE; + + size_t sc_len = option_len - KNOT_EDNS_COOKIE_CLNT_SIZE; + if (sc_len == 0) { + sc->len = 0; + } else { + memcpy(sc->data, option + KNOT_EDNS_COOKIE_CLNT_SIZE, sc_len); + sc->len = sc_len; + } + + return KNOT_EOK; +} diff --git a/src/libknot/rrtype/opt.h b/src/libknot/rrtype/opt.h new file mode 100644 index 0000000..24d80f5 --- /dev/null +++ b/src/libknot/rrtype/opt.h @@ -0,0 +1,574 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \file + * + * \brief Functions for manipulating the EDNS OPT pseudo-RR. + * + * \addtogroup rrtype + * @{ + */ + +#pragma once + +#include <assert.h> +#include <sys/socket.h> + +#include "libknot/consts.h" +#include "libknot/rrset.h" +#include "libknot/wire.h" + +/*! \brief Constants related to EDNS. */ +enum { + /*! \brief Supported EDNS version. */ + KNOT_EDNS_VERSION = 0, + + /*! \brief Bit mask for DO bit. */ + KNOT_EDNS_DO_MASK = (uint32_t)(1 << 15), + + /*! \brief Minimal UDP payload with EDNS enabled. */ + KNOT_EDNS_MIN_UDP_PAYLOAD = 512, + /*! \brief Minimal payload when using DNSSEC (RFC4035/sec.3). */ + KNOT_EDNS_MIN_DNSSEC_PAYLOAD = 1220, + /*! \brief Maximal UDP payload with EDNS enabled. */ + KNOT_EDNS_MAX_UDP_PAYLOAD = 4096, + + /*! \brief Minimum size of EDNS OPT RR in wire format. */ + KNOT_EDNS_MIN_SIZE = 11, + /*! \brief Position of the Ext RCODE field in wire format of OPT RR. */ + KNOT_EDNS_EXT_RCODE_POS = 5, + /*! \brief EDNS OPTION header size. */ + KNOT_EDNS_OPTION_HDRLEN = 4, + + /*! \brief Maximal size of EDNS client subnet address in bytes (IPv6). */ + KNOT_EDNS_CLIENT_SUBNET_ADDRESS_MAXLEN = 16, + + /*! \brief Default EDNS alignment size for a query. */ + KNOT_EDNS_ALIGNMENT_QUERY_DEFALT = 128, + /*! \brief Default EDNS alignment size for a response. */ + KNOT_EDNS_ALIGNMENT_RESPONSE_DEFAULT = 468, + + /*! \brief EDNS client cookie size. */ + KNOT_EDNS_COOKIE_CLNT_SIZE = 8, + /*! \brief EDNS minimum server cookie size. */ + KNOT_EDNS_COOKIE_SRVR_MIN_SIZE = 8, + /*! \brief EDNS maximum server cookie size. */ + KNOT_EDNS_COOKIE_SRVR_MAX_SIZE = 32, + + /*! \brief NSID option code. */ + KNOT_EDNS_OPTION_NSID = 3, + /*! \brief EDNS Client subnet option code. */ + KNOT_EDNS_OPTION_CLIENT_SUBNET = 8, + /*! \brief EDNS DNS Cookie option code. */ + KNOT_EDNS_OPTION_COOKIE = 10, + /*! \brief EDNS TCP Keepalive option code. */ + KNOT_EDNS_OPTION_TCP_KEEPALIVE = 11, + /*! \brief EDNS Padding option code. */ + KNOT_EDNS_OPTION_PADDING = 12, + /*! \brief EDNS Chain query option code. */ + KNOT_EDNS_OPTION_CHAIN = 13, + + /*! \brief Maximal currently relevant option code. */ + KNOT_EDNS_MAX_OPTION_CODE = 14, +}; + +/* Helpers for splitting extended RCODE. */ +#define KNOT_EDNS_RCODE_HI(rc) ((rc >> 4) & 0x00ff) +#define KNOT_EDNS_RCODE_LO(rc) (rc & 0x000f) + +/*! + * \brief Initialize OPT RR. + * + * \param opt_rr OPT RR to initialize. + * \param max_pld Max UDP payload. + * \param ext_rcode Extended RCODE. + * \param ver Version. + * \param mm Memory context. + * + * \return KNOT_EOK or an error + */ +int knot_edns_init(knot_rrset_t *opt_rr, uint16_t max_pld, + uint8_t ext_rcode, uint8_t ver, knot_mm_t *mm); + +/*! + * \brief Returns size of the OPT RR in wire format. + * + * \warning This function does not check the parameter, so ensure to check it + * before calling the function. It must not be NULL. + * + * \param opt_rr OPT RR to count the wire size of. + * + * \return Size of the OPT RR in bytes. + */ +static inline +size_t knot_edns_wire_size(knot_rrset_t *opt_rr) +{ + assert(opt_rr != NULL); + return KNOT_EDNS_MIN_SIZE + opt_rr->rrs.rdata->len; +} + +/*! + * \brief Returns the Max UDP payload value stored in the OPT RR. + * + * \warning This function does not check the parameter, so ensure to check it + * before calling the function. It must not be NULL. + * + * \param opt_rr OPT RR to get the value from. + * + * \return Max UDP payload in bytes. + */ +static inline +uint16_t knot_edns_get_payload(const knot_rrset_t *opt_rr) +{ + assert(opt_rr != NULL); + return opt_rr->rclass; +} + +/*! + * \brief Sets the Max UDP payload field in the OPT RR. + * + * \warning This function does not check the parameter, so ensure to check it + * before calling the function. It must not be NULL. + * + * \param opt_rr OPT RR to set the value to. + * \param payload UDP payload in bytes. + */ +static inline +void knot_edns_set_payload(knot_rrset_t *opt_rr, uint16_t payload) +{ + assert(opt_rr != NULL); + opt_rr->rclass = payload; +} + +/*! + * \brief Returns the Extended RCODE stored in the OPT RR. + * + * \warning This function does not check the parameter, so ensure to check it + * before calling the function. It must not be NULL. + * \note There is an assert() for debug checking of the parameter. + * + * \param opt_rr OPT RR to get the Extended RCODE from. + * + * \return Extended RCODE. + */ +uint8_t knot_edns_get_ext_rcode(const knot_rrset_t *opt_rr); + +/*! + * \brief Concatenates OPT RR Extended RCODE field and normal RCODE to get the + * whole Extended RCODE. + * + * Extended RCODE is created by using the Extended RCODE field from OPT RR as + * higher 8 bits and the RCODE from DNS Header as the lower 4 bits, resulting + * in a 12-bit unsigned integer. (See RFC 6891, Section 6.1.3). + * + * \param ext_rcode Extended RCODE field from OPT RR. + * \param rcode RCODE from DNS Header. + * + * \return 12-bit Extended RCODE. + */ +static inline +uint16_t knot_edns_whole_rcode(uint8_t ext_rcode, uint8_t rcode) +{ + uint16_t high = ext_rcode; + return (high << 4) | rcode; +} + +/*! + * \brief Sets the Extended RCODE field in the OPT RR. + * + * \warning This function does not check the parameter, so ensure to check it + * before calling the function. It must not be NULL. + * \note There is an assert() for debug checking of the parameter. + * + * \param opt_rr OPT RR to set the Extended RCODE to. + * \param ext_rcode Extended RCODE to set. + */ +void knot_edns_set_ext_rcode(knot_rrset_t *opt_rr, uint8_t ext_rcode); + +/*! + * \brief Sets the Extended RCODE field in OPT RR wire. + * + * \param opt_rr Position of the OPT RR in packet. + * \param ext_rcode Higher 8 bits of Extended RCODE. + */ +static inline +void knot_edns_set_ext_rcode_wire(uint8_t *opt_rr, uint8_t ext_rcode) +{ + assert(opt_rr != NULL); + *(opt_rr + KNOT_EDNS_EXT_RCODE_POS) = ext_rcode; +} + +/*! + * \brief Returns the EDNS version stored in the OPT RR. + * + * \warning This function does not check the parameter, so ensure to check it + * before calling the function. It must not be NULL. + * \note There is an assert() for debug checking of the parameter. + * + * \param opt_rr OPT RR to get the EDNS version from. + * + * \return EDNS version. + */ +uint8_t knot_edns_get_version(const knot_rrset_t *opt_rr); + +/*! + * \brief Sets the EDNS version field in the OPT RR. + * + * \warning This function does not check the parameter, so ensure to check it + * before calling the function. It must not be NULL. + * \note There is an assert() for debug checking of the parameter. + * + * \param opt_rr OPT RR to set the EDNS version to. + * \param version EDNS version to set. + */ +void knot_edns_set_version(knot_rrset_t *opt_rr, uint8_t version); + +/*! + * \brief Returns the state of the DO bit in the OPT RR flags. + * + * \warning This function does not check the parameter, so ensure to check it + * before calling the function. It must not be NULL. + * + * \param opt_rr OPT RR to get the DO bit from. + * + * \return true if the DO bit is set. + * \return false if the DO bit is not set. + */ +static inline +bool knot_edns_do(const knot_rrset_t *opt_rr) +{ + assert(opt_rr != NULL); + return opt_rr->ttl & KNOT_EDNS_DO_MASK; +} + +/*! + * \brief Sets the DO bit in the OPT RR. + * + * \warning This function does not check the parameter, so ensure to check it + * before calling the function. It must not be NULL. + * + * \param opt_rr OPT RR to set the DO bit in. + */ +static inline +void knot_edns_set_do(knot_rrset_t *opt_rr) +{ + assert(opt_rr != NULL); + opt_rr->ttl |= KNOT_EDNS_DO_MASK; +} + +/*! + * \brief Add EDNS option into the package with empty (zeroed) content. + * + * \param[in] opt_rr OPT RR structure to reserve the option in. + * \param[in] code Option code. + * \param[in] size Desired option size. + * \param[out] wire_ptr Pointer to reserved option data (can be NULL). + * \param[in] mm Memory context. + * + * \return Error code, KNOT_EOK if successful. + */ +int knot_edns_reserve_option(knot_rrset_t *opt_rr, uint16_t code, + uint16_t size, uint8_t **wire_ptr, knot_mm_t *mm); + +/*! + * \brief Adds EDNS Option to the OPT RR. + * + * \note The function now supports adding empty OPTION (just having its code). + * + * \param opt_rr OPT RR structure to add the Option to. + * \param code Option code. + * \param size Option data length in bytes. + * \param data Option data. + * \param mm Memory context. + * + * \retval KNOT_EOK + * \retval KNOT_ENOMEM + */ +int knot_edns_add_option(knot_rrset_t *opt_rr, uint16_t code, + uint16_t size, const uint8_t *data, knot_mm_t *mm); + +/*! + * \brief Searches the OPT RR for option with the specified code. + * + * \param opt_rr OPT RR structure to search in. + * \param code Option code to search for. + * + * \retval pointer to option if found + * \retval NULL otherwise. + */ +uint8_t *knot_edns_get_option(const knot_rrset_t *opt_rr, uint16_t code); + +/*! + * \brief Pointers to every option in the OPT RR wire. + */ +typedef struct { + uint8_t *ptr[KNOT_EDNS_MAX_OPTION_CODE + 1]; +} knot_edns_options_t; + +/*! + * \brief Initializes pointers to options in a given OPT RR. + * + * \note If the OPT RR has no options, the output is NULL. + * + * \param opt_rr OPT RR structure to be used. + * \param out Structure to be initialized. + * \param mm Memory context. + * + * \return Error code, KNOT_EOK if successful. + */ +int knot_edns_get_options(knot_rrset_t *opt_rr, knot_edns_options_t **out, + knot_mm_t *mm); + +/*! + * \brief Returns the option code. + * + * \param opt EDNS option (including code, length and data portion). + * + * \return EDNS option code + */ +static inline uint16_t knot_edns_opt_get_code(const uint8_t *opt) +{ + assert(opt != NULL); + return knot_wire_read_u16(opt); +} + +/*! + * \brief Returns the option data length. + * + * \param opt EDNS option (including code, length and data portion). + * + * \return EDNS option length + */ +static inline uint16_t knot_edns_opt_get_length(const uint8_t *opt) +{ + assert(opt != NULL); + return knot_wire_read_u16(opt + sizeof(uint16_t)); +} + +/*! + * \brief Returns pointer to option data. + * + * \warning No safety checks are performed on the supplied data. + * + * \param opt EDNS option (including code, length and data portion). + * + * \retval pointer to place where ENDS option data would reside + */ +static inline uint8_t *knot_edns_opt_get_data(uint8_t *opt) +{ + return opt + KNOT_EDNS_OPTION_HDRLEN; +} + +/*! + * \brief Computes additional Padding data length for required packet alignment. + * + * \param current_pkt_size Current packet size. + * \param current_opt_size Current OPT rrset size (OPT must be used). + * \param block_size Required packet block length (must be non-zero). + * + * \return Required padding length or -1 if padding not required. + */ +int knot_edns_alignment_size(size_t current_pkt_size, + size_t current_opt_size, + size_t block_size); + +/*! + * \brief EDNS Client Subnet content. + * + * \see draft-ietf-dnsop-edns-client-subnet + */ +typedef struct { + /*! \brief FAMILY */ + uint16_t family; + /*! \brief SOURCE PREFIX-LENGTH */ + uint8_t source_len; + /*! \brief SCOPE PREFIX-LENGTH */ + uint8_t scope_len; + /*! \brief ADDRESS */ + uint8_t address[KNOT_EDNS_CLIENT_SUBNET_ADDRESS_MAXLEN]; +} knot_edns_client_subnet_t; + +/*! + * \brief Get the wire size of the EDNS Client Subnet option. + * + * \param ecs EDNS Client Subnet data. + * + * \return Size of the EDNS option data. + */ +uint16_t knot_edns_client_subnet_size(const knot_edns_client_subnet_t *ecs); + +/*! + * \brief Write EDNS Client Subnet data from the ECS structure to wire. + * + * \param option EDNS option data buffer. + * \param option_len EDNS option data buffer size. + * \param ecs EDNS Client Subnet data. + * + * \return Error code, KNOT_EOK if successful. + */ +int knot_edns_client_subnet_write(uint8_t *option, uint16_t option_len, + const knot_edns_client_subnet_t *ecs); + +/*! + * \brief Parse EDNS Client Subnet data from wire to the ECS structure. + * + * \param[out] ecs EDNS Client Subnet data. + * \param[in] option EDNS option data. + * \param[in] option_len EDNS option size. + * + * \return Error code, KNOT_EOK if successful. + */ +int knot_edns_client_subnet_parse(knot_edns_client_subnet_t *ecs, + const uint8_t *option, uint16_t option_len); + +/*! + * \brief Set address to the ECS structure. + * + * \note It also resets the lengths. + * + * \param ecs ECS structure to set address into. + * \param addr Address to be set. + * + * \return Error code. KNOT_EOK if successful. + */ +int knot_edns_client_subnet_set_addr(knot_edns_client_subnet_t *ecs, + const struct sockaddr_storage *addr); + +/*! + * \brief Get address from the ECS structure. + * + * Only the family and raw address is set in the structure. The bits not + * covered by the prefix length are cleared. + * + * \param addr Address to be set. + * \param ecs ECS structure to retrieve address from. + */ +int knot_edns_client_subnet_get_addr(struct sockaddr_storage *addr, + const knot_edns_client_subnet_t *ecs); + +/*! + * \brief Get size of the EDNS Keepalive option wire size. + * + * \param[in] timeout EDNS TCP Keepalive timeout. + * + * \return Size of the EDNS option data. + */ +uint16_t knot_edns_keepalive_size(uint16_t timeout); + +/*! + * \brief Writes EDNS TCP Keepalive wire data. + * + * \param[out] option EDNS option data buffer. + * \param[in] option_len EDNS option data buffer size. + * \param[in] timeout EDNS TCP Keepalive timeout. + * + * \return Error code, KNOT_EOK if successful. + */ +int knot_edns_keepalive_write(uint8_t *option, uint16_t option_len, uint16_t timeout); + +/*! + * \brief Parses EDNS TCP Keepalive wire data. + * + * \param[out] timeout EDNS TCP Keepalive timeout. + * \param[in] option EDNS option data. + * \param[in] option_len EDNS option size. + * + * \return Error code, KNOT_EOK if successful. + */ +int knot_edns_keepalive_parse(uint16_t *timeout, const uint8_t *option, + uint16_t option_len); + +/*! + * \brief Get size of the EDNS Chain option wire size. + * + * \param[in] point EDNS Chain closest trusted point. + * + * \return Size of the EDNS option data or 0 if invalid input. + */ +uint16_t knot_edns_chain_size(const knot_dname_t *point); + +/*! + * \brief Writes EDNS Chain wire data. + * + * \param[out] option EDNS option data buffer. + * \param[in] option_len EDNS option data buffer size. + * \param[in] point EDNS Chain closest trusted point. + * + * \return Error code, KNOT_EOK if successful. + */ +int knot_edns_chain_write(uint8_t *option, uint16_t option_len, + const knot_dname_t *point); + +/*! + * \brief Parses EDNS Chain wire data. + * + * \param[out] point EDNS Chain closest trusted point. + * \param[in] option EDNS option data. + * \param[in] option_len EDNS option size. + * \param[in] mm Memory context. + * + * \return Error code, KNOT_EOK if successful. + */ +int knot_edns_chain_parse(knot_dname_t **point, const uint8_t *option, + uint16_t option_len, knot_mm_t *mm); + +/*! + * \brief DNS Cookie content. + */ +typedef struct { + uint8_t data[KNOT_EDNS_COOKIE_SRVR_MAX_SIZE]; /*!< Cookie data. */ + uint16_t len; /*!< Cookie length. */ +} knot_edns_cookie_t; + +/*! + * \brief Get size of the EDNS Cookie option wire size. + * + * \param[in] cc Client cookie. + * \param[in] sc Server cookie (can be NULL). + * + * \return Size of the EDNS option data or 0 if invalid input. + */ +uint16_t knot_edns_cookie_size(const knot_edns_cookie_t *cc, + const knot_edns_cookie_t *sc); + +/*! + * \brief Writes EDNS cookie wire data. + * + * \param[out] option EDNS option data buffer. + * \param[in] option_len EDNS option data buffer size. + * \param[in] cc EDNS client cookie. + * \param[in] sc EDNS server cookie (can be NULL). + * + * \return Error code, KNOT_EOK if successful. + */ +int knot_edns_cookie_write(uint8_t *option, uint16_t option_len, + const knot_edns_cookie_t *cc, + const knot_edns_cookie_t *sc); + +/*! + * \brief Parses EDNS Cookie wire data. + * + * \param[out] cc EDNS client cookie. + * \param[out] sc EDNS server cookie. + * \param[in] option EDNS option data. + * \param[in] option_len EDNS option size. + * + * \return Error code, KNOT_EOK if successful. + */ +int knot_edns_cookie_parse(knot_edns_cookie_t *cc, knot_edns_cookie_t *sc, + const uint8_t *option, uint16_t option_len); + +/*! @} */ diff --git a/src/libknot/rrtype/rdname.h b/src/libknot/rrtype/rdname.h new file mode 100644 index 0000000..4354a6b --- /dev/null +++ b/src/libknot/rrtype/rdname.h @@ -0,0 +1,84 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \file + * + * \addtogroup rrtype + * @{ + */ + +#pragma once + +#include "libknot/descriptor.h" +#include "libknot/dname.h" +#include "libknot/rdata.h" + +static inline +const knot_dname_t *knot_cname_name(const knot_rdata_t *rdata) +{ + assert(rdata); + return rdata->data; +} + +static inline +const knot_dname_t *knot_dname_target(const knot_rdata_t *rdata) +{ + assert(rdata); + return rdata->data; +} + +static inline +const knot_dname_t *knot_ns_name(const knot_rdata_t *rdata) +{ + assert(rdata); + return rdata->data; +} + +static inline +const knot_dname_t *knot_mx_name(const knot_rdata_t *rdata) +{ + assert(rdata); + return rdata->data + 2; +} + +static inline +const knot_dname_t *knot_srv_name(const knot_rdata_t *rdata) +{ + assert(rdata); + return rdata->data + 6; +} + +static inline +const knot_dname_t *knot_rdata_name(const knot_rdata_t *rdata, uint16_t type) +{ + assert(rdata); + switch (type) { + case KNOT_RRTYPE_NS: + return knot_ns_name(rdata); + case KNOT_RRTYPE_MX: + return knot_mx_name(rdata); + case KNOT_RRTYPE_SRV: + return knot_srv_name(rdata); + case KNOT_RRTYPE_CNAME: + return knot_cname_name(rdata); + case KNOT_RRTYPE_DNAME: + return knot_dname_target(rdata); + } + + return NULL; +} + +/*! @} */ diff --git a/src/libknot/rrtype/rrsig.h b/src/libknot/rrtype/rrsig.h new file mode 100644 index 0000000..3a4872f --- /dev/null +++ b/src/libknot/rrtype/rrsig.h @@ -0,0 +1,99 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \file + * + * \addtogroup rrtype + * @{ + */ + +#pragma once + +#include "libknot/dname.h" +#include "libknot/rdata.h" +#include "libknot/wire.h" + +static inline +uint16_t knot_rrsig_type_covered(const knot_rdata_t *rdata) +{ + assert(rdata); + return knot_wire_read_u16(rdata->data); +} + +static inline +uint8_t knot_rrsig_alg(const knot_rdata_t *rdata) +{ + assert(rdata); + return *(rdata->data + 2); +} + +static inline +uint8_t knot_rrsig_labels(const knot_rdata_t *rdata) +{ + assert(rdata); + return *(rdata->data + 3); +} + +static inline +uint32_t knot_rrsig_original_ttl(const knot_rdata_t *rdata) +{ + assert(rdata); + return knot_wire_read_u32(rdata->data + 4); +} + +static inline +uint32_t knot_rrsig_sig_expiration(const knot_rdata_t *rdata) +{ + assert(rdata); + return knot_wire_read_u32(rdata->data + 8); +} + +static inline +uint32_t knot_rrsig_sig_inception(const knot_rdata_t *rdata) +{ + assert(rdata); + return knot_wire_read_u32(rdata->data + 12); +} + +static inline +uint16_t knot_rrsig_key_tag(const knot_rdata_t *rdata) +{ + assert(rdata); + return knot_wire_read_u16(rdata->data + 16); +} + +static inline +const knot_dname_t *knot_rrsig_signer_name(const knot_rdata_t *rdata) +{ + assert(rdata); + return rdata->data + 18; +} + +static inline +uint16_t knot_rrsig_signature_len(const knot_rdata_t *rdata) +{ + assert(rdata); + return rdata->len - 18 - knot_dname_size(knot_rrsig_signer_name(rdata)); +} + +static inline +const uint8_t *knot_rrsig_signature(const knot_rdata_t *rdata) +{ + assert(rdata); + return rdata->data + 18 + knot_dname_size(knot_rrsig_signer_name(rdata)); +} + +/*! @} */ diff --git a/src/libknot/rrtype/soa.h b/src/libknot/rrtype/soa.h new file mode 100644 index 0000000..a7aebfc --- /dev/null +++ b/src/libknot/rrtype/soa.h @@ -0,0 +1,93 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \file + * + * \addtogroup rrtype + * @{ + */ + +#pragma once + +#include "libknot/dname.h" +#include "libknot/rdata.h" +#include "libknot/wire.h" + +static inline +const knot_dname_t *knot_soa_primary(const knot_rdata_t *rdata) +{ + assert(rdata); + return rdata->data; +} + +static inline +const knot_dname_t *knot_soa_mailbox(const knot_rdata_t *rdata) +{ + assert(rdata); + return rdata->data + knot_dname_size(knot_soa_primary(rdata)); +} + +static inline +size_t knot_soa_names_len(const knot_rdata_t *rdata) +{ + assert(rdata); + return knot_dname_size(knot_soa_primary(rdata)) + + knot_dname_size(knot_soa_mailbox(rdata)); +} + +static inline +uint32_t knot_soa_serial(const knot_rdata_t *rdata) +{ + assert(rdata); + return knot_wire_read_u32(rdata->data + knot_soa_names_len(rdata)); +} + +static inline +void knot_soa_serial_set(knot_rdata_t *rdata, uint32_t serial) +{ + assert(rdata); + knot_wire_write_u32(rdata->data + knot_soa_names_len(rdata), serial); +} + +static inline +uint32_t knot_soa_refresh(const knot_rdata_t *rdata) +{ + assert(rdata); + return knot_wire_read_u32(rdata->data + knot_soa_names_len(rdata) + 4); +} + +static inline +uint32_t knot_soa_retry(const knot_rdata_t *rdata) +{ + assert(rdata); + return knot_wire_read_u32(rdata->data + knot_soa_names_len(rdata) + 8); +} + +static inline +uint32_t knot_soa_expire(const knot_rdata_t *rdata) +{ + assert(rdata); + return knot_wire_read_u32(rdata->data + knot_soa_names_len(rdata) + 12); +} + +static inline +uint32_t knot_soa_minimum(const knot_rdata_t *rdata) +{ + assert(rdata); + return knot_wire_read_u32(rdata->data + knot_soa_names_len(rdata) + 16); +} + +/*! @} */ diff --git a/src/libknot/rrtype/tsig.c b/src/libknot/rrtype/tsig.c new file mode 100644 index 0000000..f4b9b71 --- /dev/null +++ b/src/libknot/rrtype/tsig.c @@ -0,0 +1,404 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> +#include <inttypes.h> +#include <string.h> +#include <stdlib.h> +#include <stdint.h> +#include <time.h> + +#include "libdnssec/tsig.h" +#include "libknot/attribute.h" +#include "libknot/rrtype/tsig.h" +#include "libknot/consts.h" +#include "libknot/dname.h" +#include "libknot/errcode.h" +#include "libknot/rrset.h" +#include "libknot/wire.h" +#include "contrib/wire_ctx.h" + +/*! \brief TSIG field offsets. */ +typedef enum tsig_off_t { + TSIG_ALGNAME_O = 0, + TSIG_TSIGNED_O, + TSIG_FUDGE_O, + TSIG_MACLEN_O, + TSIG_MAC_O, + TSIG_ORIGID_O, + TSIG_ERROR_O, + TSIG_OLEN_O, + TSIG_OTHER_O +} tsig_off_t; + +/* Helpers for RDATA offset calculation. */ +#define TSIG_OFF_MACLEN (4 * sizeof(uint16_t)) +#define TSIG_FIXED_RDLEN (8 * sizeof(uint16_t)) +#define TSIG_OTHER_MAXLEN (3 * sizeof(uint16_t)) + +/*! + * \brief Seek offset of a TSIG RR field. + * + * \param rr TSIG RR. + * \param id Field index. + * \param nb Required number of bytes after the offset (for boundaries check). + * \return pointer to field on wire or NULL. + */ +static uint8_t* rdata_seek(const knot_rrset_t *rr, tsig_off_t id, size_t nb) +{ + const knot_rdata_t *rr_data = knot_rdataset_at(&rr->rrs, 0); + if (!rr_data || rr_data->len == 0) { + return NULL; + } + + wire_ctx_t wire = wire_ctx_init_const(rr_data->data, rr_data->len); + + /* TSIG RR names should be already sanitized on parse. */ + size_t alg_len = knot_dname_size(wire.wire); + + /* Not pretty, but fast. */ + switch (id) { + case TSIG_ALGNAME_O: break; + case TSIG_TSIGNED_O: + wire_ctx_skip(&wire, alg_len); break; + case TSIG_FUDGE_O: + wire_ctx_skip(&wire, alg_len + 3 * sizeof(uint16_t)); + break; + case TSIG_MACLEN_O: + wire_ctx_skip(&wire, alg_len + 4 * sizeof(uint16_t)); + break; + case TSIG_MAC_O: + wire_ctx_skip(&wire, alg_len + 5 * sizeof(uint16_t)); + break; + case TSIG_ORIGID_O: + wire_ctx_skip(&wire, alg_len + 4 * sizeof(uint16_t)); + wire_ctx_skip(&wire, wire_ctx_read_u16(&wire)); + break; + case TSIG_ERROR_O: + wire_ctx_skip(&wire, alg_len + 4 * sizeof(uint16_t)); + wire_ctx_skip(&wire, wire_ctx_read_u16(&wire)); + wire_ctx_skip(&wire, sizeof(uint16_t)); + break; + case TSIG_OLEN_O: + wire_ctx_skip(&wire, alg_len + 4 * sizeof(uint16_t)); + wire_ctx_skip(&wire, wire_ctx_read_u16(&wire)); + wire_ctx_skip(&wire, 2 * sizeof(uint16_t)); + break; + case TSIG_OTHER_O: + wire_ctx_skip(&wire, alg_len + 4 * sizeof(uint16_t)); + wire_ctx_skip(&wire, wire_ctx_read_u16(&wire)); + wire_ctx_skip(&wire, 3 * sizeof(uint16_t)); + break; + } + + if (wire.error != KNOT_EOK) { + return NULL; + } + + /* Check remaining bytes. */ + + if (wire_ctx_available(&wire) < nb){ + return NULL; + } + + return wire.position; +} + +static int rdata_set_tsig_error(knot_rrset_t *tsig, uint16_t tsig_error) +{ + uint8_t *rd = rdata_seek(tsig, TSIG_ERROR_O, sizeof(uint16_t)); + if (!rd) { + return KNOT_ERROR; + } + + knot_wire_write_u16(rd, tsig_error); + return KNOT_EOK; +} + +_public_ +int knot_tsig_create_rdata(knot_rrset_t *rr, const knot_dname_t *alg, + uint16_t maclen, uint16_t tsig_err) +{ + if (rr == NULL || alg == NULL) { + return KNOT_EINVAL; + } + + size_t alg_len = knot_dname_size(alg); + size_t rdlen = alg_len + TSIG_FIXED_RDLEN + maclen; + if (tsig_err == KNOT_RCODE_BADTIME) { + rdlen += TSIG_OTHER_MAXLEN; + } + uint8_t rd[rdlen]; + memset(rd, 0, rdlen); + + /* Copy alg name. */ + knot_dname_to_wire(rd, alg, rdlen); + + /* Set MAC variable length in advance. */ + size_t offset = alg_len + TSIG_OFF_MACLEN; + knot_wire_write_u16(rd + offset, maclen); + + int ret = knot_rrset_add_rdata(rr, rd, rdlen, NULL); + if (ret != KNOT_EOK) { + return ret; + } + + /* Set error. */ + rdata_set_tsig_error(rr, tsig_err); + + return KNOT_EOK; +} + +_public_ +int knot_tsig_rdata_set_time_signed(knot_rrset_t *tsig, uint64_t time) +{ + uint8_t *rd = rdata_seek(tsig, TSIG_TSIGNED_O, 3*sizeof(uint16_t)); + if (!rd) { + return KNOT_ERROR; + } + + knot_wire_write_u48(rd, time); + return KNOT_EOK; +} + +_public_ +int knot_tsig_rdata_set_fudge(knot_rrset_t *tsig, uint16_t fudge) +{ + uint8_t *rd = rdata_seek(tsig, TSIG_FUDGE_O, sizeof(uint16_t)); + if (!rd) { + return KNOT_ERROR; + } + + knot_wire_write_u16(rd, fudge); + return KNOT_EOK; +} + +_public_ +int knot_tsig_rdata_set_mac(knot_rrset_t *tsig, uint16_t length, const uint8_t *mac) +{ + uint8_t *rd = rdata_seek(tsig, TSIG_MAC_O, length); + if (!rd) { + return KNOT_ERROR; + } + + /*! \note Cannot change length, as rdata is already preallocd. */ + + /* Copy the actual MAC. */ + memcpy(rd, mac, length); + return KNOT_EOK; +} + +_public_ +int knot_tsig_rdata_set_orig_id(knot_rrset_t *tsig, uint16_t id) +{ + uint8_t *rd = rdata_seek(tsig, TSIG_ORIGID_O, sizeof(uint16_t)); + if (!rd) { + return KNOT_ERROR; + } + + /* Write the length - 2. */ + knot_wire_write_u16(rd, id); + return KNOT_EOK; +} + +_public_ +int knot_tsig_rdata_set_other_data(knot_rrset_t *tsig, uint16_t len, + const uint8_t *other_data) +{ + if (len > TSIG_OTHER_MAXLEN) { + return KNOT_EINVAL; + } + + uint8_t *rd = rdata_seek(tsig, TSIG_OLEN_O, len + sizeof(uint16_t)); + if (!rd) { + return KNOT_ERROR; + } + + /* Write the length. */ + knot_wire_write_u16(rd, len); + + /* Copy the actual data. */ + memcpy(rd + sizeof(uint16_t), other_data, len); + return KNOT_EOK; +} + +_public_ +const knot_dname_t *knot_tsig_rdata_alg_name(const knot_rrset_t *tsig) +{ + return knot_rdataset_at(&tsig->rrs, 0)->data; +} + +_public_ +dnssec_tsig_algorithm_t knot_tsig_rdata_alg(const knot_rrset_t *tsig) +{ + /* Get the algorithm name. */ + const knot_dname_t *alg_name = knot_tsig_rdata_alg_name(tsig); + if (!alg_name) { + return DNSSEC_TSIG_UNKNOWN; + } + + return dnssec_tsig_algorithm_from_dname(alg_name); +} + +_public_ +uint64_t knot_tsig_rdata_time_signed(const knot_rrset_t *tsig) +{ + /*! \todo How to return invalid value? */ + uint8_t *rd = rdata_seek(tsig, TSIG_TSIGNED_O, 3*sizeof(uint16_t)); + if (!rd) { + return 0; + } + return knot_wire_read_u48(rd); +} + +_public_ +uint16_t knot_tsig_rdata_fudge(const knot_rrset_t *tsig) +{ + uint8_t *rd = rdata_seek(tsig, TSIG_FUDGE_O, sizeof(uint16_t)); + if (!rd) { + return 0; + } + return knot_wire_read_u16(rd); +} + +_public_ +const uint8_t *knot_tsig_rdata_mac(const knot_rrset_t *tsig) +{ + uint8_t *rd = rdata_seek(tsig, TSIG_MAC_O, 0); + if (!rd) { + return NULL; + } + return rd; +} + +_public_ +size_t knot_tsig_rdata_mac_length(const knot_rrset_t *tsig) +{ + uint8_t *rd = rdata_seek(tsig, TSIG_MACLEN_O, sizeof(uint16_t)); + if (!rd) { + return 0; + } + return knot_wire_read_u16(rd); +} + +_public_ +uint16_t knot_tsig_rdata_orig_id(const knot_rrset_t *tsig) +{ + uint8_t *rd = rdata_seek(tsig, TSIG_ORIGID_O, sizeof(uint16_t)); + if (!rd) { + return 0; + } + return knot_wire_read_u16(rd); +} + +_public_ +uint16_t knot_tsig_rdata_error(const knot_rrset_t *tsig) +{ + uint8_t *rd = rdata_seek(tsig, TSIG_ERROR_O, sizeof(uint16_t)); + if (!rd) { + return 0; + } + return knot_wire_read_u16(rd); +} + +_public_ +const uint8_t *knot_tsig_rdata_other_data(const knot_rrset_t *tsig) +{ + uint8_t *rd = rdata_seek(tsig, TSIG_OTHER_O, 0); + if (!rd) { + return NULL; + } + return rd; +} + +_public_ +uint16_t knot_tsig_rdata_other_data_length(const knot_rrset_t *tsig) +{ + uint8_t *rd = rdata_seek(tsig, TSIG_OLEN_O, sizeof(uint16_t)); + if (!rd) { + return 0; + } + return knot_wire_read_u16(rd); +} + +_public_ +size_t knot_tsig_rdata_tsig_variables_length(const knot_rrset_t *tsig) +{ + if (tsig == NULL) { + return 0; + } + /* Key name, Algorithm name and Other data have variable lengths. */ + const knot_dname_t *key_name = tsig->owner; + if (!key_name) { + return 0; + } + + const knot_dname_t *alg_name = knot_tsig_rdata_alg_name(tsig); + if (!alg_name) { + return 0; + } + + uint16_t other_data_length = knot_tsig_rdata_other_data_length(tsig); + + return knot_dname_size(key_name) + knot_dname_size(alg_name) + + other_data_length + KNOT_TSIG_VARIABLES_LENGTH; +} + +_public_ +size_t knot_tsig_rdata_tsig_timers_length(void) +{ + /*! \todo Cleanup */ + return KNOT_TSIG_TIMERS_LENGTH; +} + +_public_ +size_t knot_tsig_wire_size(const knot_tsig_key_t *key) +{ + if (key == NULL || key->name == NULL) { + return 0; + } + + return knot_dname_size(key->name) + TSIG_FIXED_RDLEN + + sizeof(uint16_t) + /* TYPE */ + sizeof(uint16_t) + /* CLASS */ + sizeof(uint32_t) + /* TTL */ + sizeof(uint16_t) + /* RDATA length. */ + knot_dname_size(dnssec_tsig_algorithm_to_dname(key->algorithm)) + + dnssec_tsig_algorithm_size(key->algorithm); /* MAC length. */ +} + +_public_ +size_t knot_tsig_wire_maxsize(const knot_tsig_key_t *key) +{ + size_t size = knot_tsig_wire_size(key); + if (size == 0) { + return 0; + } + + /* In case of BADTIME other data. */ + return size + TSIG_OTHER_MAXLEN; +} + +_public_ +bool knot_tsig_rdata_is_ok(const knot_rrset_t *tsig) +{ + /*! \todo Check size, needs to check variable-length fields. */ + return (tsig != NULL + && knot_rdataset_at(&tsig->rrs, 0) != NULL + && rdata_seek(tsig, TSIG_OTHER_O, 0) != NULL + && knot_tsig_rdata_alg_name(tsig) != NULL + && knot_tsig_rdata_time_signed(tsig) != 0); +} diff --git a/src/libknot/rrtype/tsig.h b/src/libknot/rrtype/tsig.h new file mode 100644 index 0000000..2dd3696 --- /dev/null +++ b/src/libknot/rrtype/tsig.h @@ -0,0 +1,121 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \file + * + * \brief TSIG manipulation. + * + * \addtogroup rrtype + * @{ + */ + +#pragma once + +#include <stdint.h> + +#include "libdnssec/binary.h" +#include "libdnssec/tsig.h" +#include "libknot/consts.h" +#include "libknot/rrset.h" +#include "libknot/tsig.h" + +enum tsig_consts { + KNOT_TSIG_ITEM_COUNT = 7, + KNOT_TSIG_VARIABLES_LENGTH = sizeof(uint16_t) // class + + sizeof(uint32_t) // ttl + + 6 // time signed + + sizeof(uint16_t) // fudge + + sizeof(uint16_t) // error + + sizeof(uint16_t),// other data length + KNOT_TSIG_TIMERS_LENGTH = sizeof(uint16_t) //fugde + + 6 // time signed +}; + +/*! + * \brief Create TSIG RDATA. + * + * \param rr TSIG RR to contain created data. + * \param alg Algorithm name. + * \param maclen Algorithm MAC len (may be set to 0 for empty MAC). + * \param tsig_err TSIG error code. + * + * \retval KNOT_EINVAL + * \retval KNOT_EOK + */ +int knot_tsig_create_rdata(knot_rrset_t *rr, const knot_dname_t *alg, + uint16_t maclen, uint16_t tsig_err); + +int knot_tsig_rdata_set_time_signed(knot_rrset_t *tsig, uint64_t time); + +int knot_tsig_rdata_store_current_time(knot_rrset_t *tsig); + +int knot_tsig_rdata_set_fudge(knot_rrset_t *tsig, uint16_t fudge); + +int knot_tsig_rdata_set_mac(knot_rrset_t *tsig, uint16_t length, const uint8_t *mac); + +int knot_tsig_rdata_set_orig_id(knot_rrset_t *tsig, uint16_t id); + +int knot_tsig_rdata_set_other_data(knot_rrset_t *tsig, uint16_t length, + const uint8_t *other_data); + +const knot_dname_t *knot_tsig_rdata_alg_name(const knot_rrset_t *tsig); + +dnssec_tsig_algorithm_t knot_tsig_rdata_alg(const knot_rrset_t *tsig); + +uint64_t knot_tsig_rdata_time_signed(const knot_rrset_t *tsig); + +uint16_t knot_tsig_rdata_fudge(const knot_rrset_t *tsig); + +const uint8_t *knot_tsig_rdata_mac(const knot_rrset_t *tsig); + +size_t knot_tsig_rdata_mac_length(const knot_rrset_t *tsig); + +uint16_t knot_tsig_rdata_orig_id(const knot_rrset_t *tsig); + +uint16_t knot_tsig_rdata_error(const knot_rrset_t *tsig); + +const uint8_t *knot_tsig_rdata_other_data(const knot_rrset_t *tsig); + +uint16_t knot_tsig_rdata_other_data_length(const knot_rrset_t *tsig); + +size_t knot_tsig_rdata_tsig_variables_length(const knot_rrset_t *tsig); + +size_t knot_tsig_rdata_tsig_timers_length(void); + +/*! + * \brief Return standard TSIG RRSET wire size for given algorithm. + * + * \param key Signing key descriptor. + * + * \return RRSET wire size. + */ +size_t knot_tsig_wire_size(const knot_tsig_key_t *key); + +/*! + * \brief Return TSIG RRSET maximum wire size for given algorithm. + * + * This size is reached if error reply with BADTIME. + * + * \param key Signing key descriptor. + * + * \return RRSET wire size. + */ +size_t knot_tsig_wire_maxsize(const knot_tsig_key_t *key); + +/*! \todo Documentation. */ +bool knot_tsig_rdata_is_ok(const knot_rrset_t *tsig); + +/*! @} */ diff --git a/src/libknot/tsig-op.c b/src/libknot/tsig-op.c new file mode 100644 index 0000000..acbcafb --- /dev/null +++ b/src/libknot/tsig-op.c @@ -0,0 +1,683 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> +#include <inttypes.h> +#include <time.h> +#include <stdint.h> + +#include "libdnssec/error.h" +#include "libdnssec/tsig.h" +#include "libknot/attribute.h" +#include "libknot/tsig-op.h" +#include "libknot/errcode.h" +#include "libknot/descriptor.h" +#include "libknot/rrtype/tsig.h" +#include "libknot/packet/wire.h" +#include "libknot/consts.h" +#include "libknot/packet/rrset-wire.h" +#include "libknot/wire.h" +#include "contrib/string.h" + +const int KNOT_TSIG_MAX_DIGEST_SIZE = 64; // size of HMAC-SHA512 digest +const uint16_t KNOT_TSIG_FUDGE_DEFAULT = 300; // default Fudge value + +static int check_algorithm(const knot_rrset_t *tsig_rr) +{ + if (tsig_rr == NULL) { + return KNOT_EINVAL; + } + + const knot_dname_t *alg_name = knot_tsig_rdata_alg_name(tsig_rr); + if (!alg_name) { + return KNOT_EMALF; + } + + dnssec_tsig_algorithm_t alg = dnssec_tsig_algorithm_from_dname(alg_name); + if (alg == DNSSEC_TSIG_UNKNOWN) { + return KNOT_TSIG_EBADKEY; + } + + return KNOT_EOK; +} + +static int check_key(const knot_rrset_t *tsig_rr, const knot_tsig_key_t *tsig_key) +{ + if (tsig_rr == NULL || tsig_key == NULL) { + return KNOT_EINVAL; + } + + const knot_dname_t *tsig_name = tsig_rr->owner; + if (!tsig_name) { + return KNOT_EMALF; + } + + if (!knot_dname_is_equal(tsig_name, tsig_key->name)) { + return KNOT_TSIG_EBADKEY; + } + + return KNOT_EOK; +} + +static int compute_digest(const uint8_t *wire, size_t wire_len, + uint8_t *digest, size_t *digest_len, + const knot_tsig_key_t *key) +{ + if (!wire || !digest || !digest_len || !key) { + return KNOT_EINVAL; + } + + if (!key->name) { + return KNOT_EMALF; + } + + dnssec_tsig_ctx_t *ctx = NULL; + int result = dnssec_tsig_new(&ctx, key->algorithm, &key->secret); + if (result != DNSSEC_EOK) { + return KNOT_TSIG_EBADSIG; + } + + dnssec_binary_t cover = { .data = (uint8_t *)wire, .size = wire_len }; + dnssec_tsig_add(ctx, &cover); + + *digest_len = dnssec_tsig_size(ctx); + dnssec_tsig_write(ctx, digest); + dnssec_tsig_free(ctx); + + return KNOT_EOK; +} + +static int check_time_signed(const knot_rrset_t *tsig_rr, uint64_t prev_time_signed) +{ + if (!tsig_rr) { + return KNOT_EINVAL; + } + + /* Get the time signed and fudge values. */ + uint64_t time_signed = knot_tsig_rdata_time_signed(tsig_rr); + if (time_signed == 0) { + return KNOT_TSIG_EBADTIME; + } + uint16_t fudge = knot_tsig_rdata_fudge(tsig_rr); + if (fudge == 0) { + return KNOT_TSIG_EBADTIME; + } + + /* Get the current time. */ + time_t curr_time = time(NULL); + + /*!< \todo bleeding eyes. */ + double diff = difftime(curr_time, (time_t)time_signed); + + if (diff > fudge || diff < -fudge) { + return KNOT_TSIG_EBADTIME; + } + + diff = difftime((time_t)time_signed, prev_time_signed); + + if (diff < 0) { + return KNOT_TSIG_EBADTIME; + } + + return KNOT_EOK; +} + +static int write_tsig_variables(uint8_t *wire, const knot_rrset_t *tsig_rr) +{ + if (wire == NULL || tsig_rr == NULL) { + return KNOT_EINVAL; + } + + /* Copy TSIG variables - starting with key name. */ + const knot_dname_t *tsig_owner = tsig_rr->owner; + if (!tsig_owner) { + return KNOT_EINVAL; + } + + int offset = 0; + + offset += knot_dname_to_wire(wire + offset, tsig_owner, KNOT_DNAME_MAXLEN); + + /*!< \todo which order? */ + + /* Copy class. */ + knot_wire_write_u16(wire + offset, tsig_rr->rclass); + offset += sizeof(uint16_t); + + /* Copy TTL - always 0. */ + knot_wire_write_u32(wire + offset, tsig_rr->ttl); + offset += sizeof(uint32_t); + + /* Copy alg name. */ + const knot_dname_t *alg_name = knot_tsig_rdata_alg_name(tsig_rr); + if (!alg_name) { + return KNOT_EINVAL; + } + + /* Te algorithm name must be in canonical form, i.e. in lowercase. */ + uint8_t *alg_name_wire = wire + offset; + offset += knot_dname_to_wire(alg_name_wire, alg_name, KNOT_DNAME_MAXLEN); + knot_dname_to_lower(alg_name_wire); + + /* Following data are written in network order. */ + /* Time signed. */ + knot_wire_write_u48(wire + offset, knot_tsig_rdata_time_signed(tsig_rr)); + offset += 6; + /* Fudge. */ + knot_wire_write_u16(wire + offset, knot_tsig_rdata_fudge(tsig_rr)); + offset += sizeof(uint16_t); + /* TSIG error. */ + knot_wire_write_u16(wire + offset, knot_tsig_rdata_error(tsig_rr)); + offset += sizeof(uint16_t); + /* Get other data length. */ + uint16_t other_data_length = knot_tsig_rdata_other_data_length(tsig_rr); + /* Get other data. */ + const uint8_t *other_data = knot_tsig_rdata_other_data(tsig_rr); + if (!other_data) { + return KNOT_EINVAL; + } + + /* + * We cannot write the whole other_data, as it contains its length in + * machine order. + */ + knot_wire_write_u16(wire + offset, other_data_length); + offset += sizeof(uint16_t); + + /* Skip the length. */ + memcpy(wire + offset, other_data, other_data_length); + + return KNOT_EOK; +} + +static int wire_write_timers(uint8_t *wire, const knot_rrset_t *tsig_rr) +{ + if (wire == NULL || tsig_rr == NULL) { + return KNOT_EINVAL; + } + + //write time signed + knot_wire_write_u48(wire, knot_tsig_rdata_time_signed(tsig_rr)); + //write fudge + knot_wire_write_u16(wire + 6, knot_tsig_rdata_fudge(tsig_rr)); + + return KNOT_EOK; +} + +static int create_sign_wire(const uint8_t *msg, size_t msg_len, + const uint8_t *request_mac, size_t request_mac_len, + uint8_t *digest, size_t *digest_len, + const knot_rrset_t *tmp_tsig, + const knot_tsig_key_t *key) +{ + if (!msg || !key || digest_len == NULL) { + return KNOT_EINVAL; + } + + /* Create tmp TSIG. */ + int ret = KNOT_EOK; + + /* + * Create tmp wire, it should contain message + * plus request mac plus tsig varibles. + */ + size_t wire_len = msg_len + request_mac_len + (request_mac_len > 0 ? 2 : 0) + + knot_tsig_rdata_tsig_variables_length(tmp_tsig); + uint8_t *wire = malloc(wire_len); + if (!wire) { + return KNOT_ENOMEM; + } + + memset(wire, 0, wire_len); + + uint8_t *pos = wire; + + /* Copy the request MAC - should work even if NULL. */ + if (request_mac_len > 0) { + knot_wire_write_u16(pos, request_mac_len); + pos += 2; + memcpy(pos, request_mac, request_mac_len); + } + pos += request_mac_len; + /* Copy the original message. */ + memcpy(pos, msg, msg_len); + pos += msg_len; + /* Copy TSIG variables. */ + ret = write_tsig_variables(pos, tmp_tsig); + if (ret != KNOT_EOK) { + free(wire); + return ret; + } + + /* Compute digest. */ + ret = compute_digest(wire, wire_len, digest, digest_len, key); + if (ret != KNOT_EOK) { + *digest_len = 0; + free(wire); + return ret; + } + + free(wire); + + return KNOT_EOK; +} + +static int create_sign_wire_next(const uint8_t *msg, size_t msg_len, + const uint8_t *prev_mac, size_t prev_mac_len, + uint8_t *digest, size_t *digest_len, + const knot_rrset_t *tmp_tsig, + const knot_tsig_key_t *key) +{ + if (!msg || !key || digest_len == NULL) { + return KNOT_EINVAL; + } + + /* Create tmp TSIG. */ + int ret = KNOT_EOK; + + /* + * Create tmp wire, it should contain message + * plus request mac plus tsig varibles. + */ + size_t wire_len = msg_len + prev_mac_len + knot_tsig_rdata_tsig_timers_length() + 2; + uint8_t *wire = malloc(wire_len); + if (!wire) { + return KNOT_ENOMEM; + } + + memset(wire, 0, wire_len); + + /* Copy the request MAC - should work even if NULL. */ + knot_wire_write_u16(wire, prev_mac_len); + memcpy(wire + 2, prev_mac, prev_mac_len); + /* Copy the original message. */ + memcpy(wire + prev_mac_len + 2, msg, msg_len); + /* Copy TSIG variables. */ + + ret = wire_write_timers(wire + prev_mac_len + msg_len + 2, tmp_tsig); + if (ret != KNOT_EOK) { + free(wire); + return ret; + } + + /* Compute digest. */ + ret = compute_digest(wire, wire_len, digest, digest_len, key); + if (ret != KNOT_EOK) { + *digest_len = 0; + free(wire); + return ret; + } + + free(wire); + + return KNOT_EOK; +} + +_public_ +int knot_tsig_sign(uint8_t *msg, size_t *msg_len, size_t msg_max_len, + const uint8_t *request_mac, size_t request_mac_len, + uint8_t *digest, size_t *digest_len, + const knot_tsig_key_t *key, uint16_t tsig_rcode, + uint64_t request_time_signed) +{ + if (!msg || !msg_len || !key || digest == NULL || digest_len == NULL) { + return KNOT_EINVAL; + } + + knot_rrset_t *tmp_tsig = knot_rrset_new(key->name, KNOT_RRTYPE_TSIG, + KNOT_CLASS_ANY, 0, NULL); + if (!tmp_tsig) { + return KNOT_ENOMEM; + } + + /* Create rdata for TSIG RR. */ + uint16_t rdata_rcode = KNOT_RCODE_NOERROR; + if (tsig_rcode == KNOT_RCODE_BADTIME) { + rdata_rcode = tsig_rcode; + } + + const uint8_t *alg_name = dnssec_tsig_algorithm_to_dname(key->algorithm); + size_t alg_size = dnssec_tsig_algorithm_size(key->algorithm); + knot_tsig_create_rdata(tmp_tsig, alg_name, alg_size, rdata_rcode); + + /* Distinguish BADTIME response. */ + if (tsig_rcode == KNOT_RCODE_BADTIME) { + /* Set client's time signed into the time signed field. */ + knot_tsig_rdata_set_time_signed(tmp_tsig, request_time_signed); + + /* Store current time into Other data. */ + uint8_t time_signed[6]; + time_t now = time(NULL); + knot_wire_write_u48(time_signed, now); + + knot_tsig_rdata_set_other_data(tmp_tsig, 6, time_signed); + } else { + knot_tsig_rdata_set_time_signed(tmp_tsig, time(NULL)); + + /* Set other len. */ + knot_tsig_rdata_set_other_data(tmp_tsig, 0, 0); + } + + knot_tsig_rdata_set_fudge(tmp_tsig, KNOT_TSIG_FUDGE_DEFAULT); + + /* Set original ID */ + knot_tsig_rdata_set_orig_id(tmp_tsig, knot_wire_get_id(msg)); + + uint8_t digest_tmp[KNOT_TSIG_MAX_DIGEST_SIZE]; + size_t digest_tmp_len = 0; + + int ret = create_sign_wire(msg, *msg_len, /*msg_max_len,*/ + request_mac, request_mac_len, + digest_tmp, &digest_tmp_len, tmp_tsig, key); + if (ret != KNOT_EOK) { + knot_rrset_free(tmp_tsig, NULL); + return ret; + } + + /* Set the digest. */ + knot_tsig_rdata_set_mac(tmp_tsig, digest_tmp_len, digest_tmp); + + /* Write RRSet to wire */ + ret = knot_rrset_to_wire(tmp_tsig, msg + *msg_len, + msg_max_len - *msg_len, NULL); + if (ret < 0) { + *digest_len = 0; + knot_rrset_free(tmp_tsig, NULL); + return ret; + } + + size_t tsig_wire_len = ret; + + knot_rrset_free(tmp_tsig, NULL); + + *msg_len += tsig_wire_len; + + uint16_t arcount = knot_wire_get_arcount(msg); + knot_wire_set_arcount(msg, ++arcount); + + /* everything went ok, save the digest to the output parameter */ + memcpy(digest, digest_tmp, digest_tmp_len); + *digest_len = digest_tmp_len; + + return KNOT_EOK; +} + +_public_ +int knot_tsig_sign_next(uint8_t *msg, size_t *msg_len, size_t msg_max_len, + const uint8_t *prev_digest, size_t prev_digest_len, + uint8_t *digest, size_t *digest_len, + const knot_tsig_key_t *key, uint8_t *to_sign, + size_t to_sign_len) +{ + if (!msg || !msg_len || !key || !digest || !digest_len) { + return KNOT_EINVAL; + } + + uint8_t digest_tmp[KNOT_TSIG_MAX_DIGEST_SIZE]; + size_t digest_tmp_len = 0; + knot_rrset_t *tmp_tsig = knot_rrset_new(key->name, KNOT_RRTYPE_TSIG, + KNOT_CLASS_ANY, 0, NULL); + if (!tmp_tsig) { + return KNOT_ENOMEM; + } + + /* Create rdata for TSIG RR. */ + const uint8_t *alg_name = dnssec_tsig_algorithm_to_dname(key->algorithm); + size_t alg_size = dnssec_tsig_algorithm_size(key->algorithm); + knot_tsig_create_rdata(tmp_tsig, alg_name, alg_size, 0); + knot_tsig_rdata_set_time_signed(tmp_tsig, time(NULL)); + knot_tsig_rdata_set_fudge(tmp_tsig, KNOT_TSIG_FUDGE_DEFAULT); + + /* Create wire to be signed. */ + size_t wire_len = prev_digest_len + to_sign_len + KNOT_TSIG_TIMERS_LENGTH + 2; + uint8_t *wire = malloc(wire_len); + if (!wire) { + knot_rrset_free(tmp_tsig, NULL); + return KNOT_ENOMEM; + } + memset(wire, 0, wire_len); + + /* Write previous digest length. */ + knot_wire_write_u16(wire, prev_digest_len); + /* Write previous digest. */ + memcpy(wire + 2, prev_digest, prev_digest_len); + /* Write original message. */ + memcpy(wire + prev_digest_len + 2, to_sign, to_sign_len); + /* Write timers. */ + wire_write_timers(wire + prev_digest_len + to_sign_len + 2, tmp_tsig); + + int ret = compute_digest(wire, wire_len, digest_tmp, &digest_tmp_len, key); + free(wire); + if (ret != KNOT_EOK) { + knot_rrset_free(tmp_tsig, NULL); + *digest_len = 0; + return ret; + } + + if (digest_tmp_len > *digest_len) { + knot_rrset_free(tmp_tsig, NULL); + *digest_len = 0; + return KNOT_ESPACE; + } + + /* Set the MAC. */ + knot_tsig_rdata_set_mac(tmp_tsig, digest_tmp_len, digest_tmp); + + /* Set original id. */ + knot_tsig_rdata_set_orig_id(tmp_tsig, knot_wire_get_id(msg)); + + /* Set other data. */ + knot_tsig_rdata_set_other_data(tmp_tsig, 0, NULL); + + ret = knot_rrset_to_wire(tmp_tsig, msg + *msg_len, + msg_max_len - *msg_len, NULL); + if (ret < 0) { + knot_rrset_free(tmp_tsig, NULL); + *digest_len = 0; + return ret; + } + + size_t tsig_wire_size = ret; + + knot_rrset_free(tmp_tsig, NULL); + + *msg_len += tsig_wire_size; + uint16_t arcount = knot_wire_get_arcount(msg); + knot_wire_set_arcount(msg, ++arcount); + + memcpy(digest, digest_tmp, digest_tmp_len); + *digest_len = digest_tmp_len; + + return KNOT_EOK; +} + +static int check_digest(const knot_rrset_t *tsig_rr, + const uint8_t *wire, size_t size, + const uint8_t *request_mac, size_t request_mac_len, + const knot_tsig_key_t *tsig_key, + uint64_t prev_time_signed, int use_times) +{ + if (!wire || !tsig_key) { + return KNOT_EINVAL; + } + + /* No TSIG record means verification failure. */ + if (tsig_rr == NULL) { + return KNOT_TSIG_EBADKEY; + } + + /* Check that libknot knows the algorithm. */ + int ret = check_algorithm(tsig_rr); + if (ret != KNOT_EOK) { + return ret; + } + + /* Check that key is valid, ie. the same as given in args. */ + ret = check_key(tsig_rr, tsig_key); + if (ret != KNOT_EOK) { + return ret; + } + + uint8_t *wire_to_sign = malloc(size); + if (!wire_to_sign) { + return KNOT_ENOMEM; + } + + memcpy(wire_to_sign, wire, size); + + // restore message ID to which the signature had been created with + knot_wire_set_id(wire_to_sign, knot_tsig_rdata_orig_id(tsig_rr)); + + uint8_t digest_tmp[KNOT_TSIG_MAX_DIGEST_SIZE]; + size_t digest_tmp_len = 0; + assert(tsig_rr->rrs.count > 0); + + if (use_times) { + /* Wire is not a single packet, TSIG RRs must be stripped already. */ + ret = create_sign_wire_next(wire_to_sign, size, + request_mac, request_mac_len, + digest_tmp, &digest_tmp_len, + tsig_rr, tsig_key); + } else { + ret = create_sign_wire(wire_to_sign, size, + request_mac, request_mac_len, + digest_tmp, &digest_tmp_len, + tsig_rr, tsig_key); + } + + assert(tsig_rr->rrs.count > 0); + free(wire_to_sign); + + if (ret != KNOT_EOK) { + return ret; + } + + /* Compare MAC from TSIG RR RDATA with just computed digest. */ + + /*!< \todo move to function. */ + const knot_dname_t *alg_name = knot_tsig_rdata_alg_name(tsig_rr); + dnssec_tsig_algorithm_t alg = dnssec_tsig_algorithm_from_dname(alg_name); + + /*! \todo [TSIG] TRUNCATION */ + uint16_t mac_length = knot_tsig_rdata_mac_length(tsig_rr); + const uint8_t *tsig_mac = knot_tsig_rdata_mac(tsig_rr); + + if (mac_length != dnssec_tsig_algorithm_size(alg)) { + return KNOT_TSIG_EBADSIG; + } + + if (const_time_memcmp(tsig_mac, digest_tmp, mac_length) != 0) { + return KNOT_TSIG_EBADSIG; + } + + /* Check TSIG validity period, must be after the signature check! */ + ret = check_time_signed(tsig_rr, prev_time_signed); + if (ret != KNOT_EOK) { + return ret; + } + + return KNOT_EOK; +} + +_public_ +int knot_tsig_server_check(const knot_rrset_t *tsig_rr, + const uint8_t *wire, size_t size, + const knot_tsig_key_t *tsig_key) +{ + return check_digest(tsig_rr, wire, size, NULL, 0, tsig_key, 0, 0); +} + +_public_ +int knot_tsig_client_check(const knot_rrset_t *tsig_rr, + const uint8_t *wire, size_t size, + const uint8_t *request_mac, size_t request_mac_len, + const knot_tsig_key_t *tsig_key, + uint64_t prev_time_signed) +{ + return check_digest(tsig_rr, wire, size, request_mac, request_mac_len, + tsig_key, prev_time_signed, 0); +} + +_public_ +int knot_tsig_client_check_next(const knot_rrset_t *tsig_rr, + const uint8_t *wire, size_t size, + const uint8_t *prev_digest, + size_t prev_digest_len, + const knot_tsig_key_t *tsig_key, + uint64_t prev_time_signed) +{ + return check_digest(tsig_rr, wire, size, prev_digest, + prev_digest_len, tsig_key, prev_time_signed, 1); +} + +_public_ +int knot_tsig_add(uint8_t *msg, size_t *msg_len, size_t msg_max_len, + uint16_t tsig_rcode, const knot_rrset_t *tsig_rr) +{ + /*! \todo Revise!! */ + + if (!msg || !msg_len || !tsig_rr) { + return KNOT_EINVAL; + } + + /*! \todo What key to use, when we do not sign? Does this even work? */ + knot_rrset_t *tmp_tsig = knot_rrset_new(tsig_rr->owner, KNOT_RRTYPE_TSIG, + KNOT_CLASS_ANY, 0, NULL); + if (!tmp_tsig) { + return KNOT_ENOMEM; + } + + assert(tsig_rcode != KNOT_RCODE_BADTIME); + knot_tsig_create_rdata(tmp_tsig, knot_tsig_rdata_alg_name(tsig_rr), 0, tsig_rcode); + knot_tsig_rdata_set_time_signed(tmp_tsig, knot_tsig_rdata_time_signed(tsig_rr)); + + /* Comparing to BIND it was found out that the Fudge should always be + * set to the server's value. + */ + knot_tsig_rdata_set_fudge(tmp_tsig, KNOT_TSIG_FUDGE_DEFAULT); + + /* Set original ID */ + knot_tsig_rdata_set_orig_id(tmp_tsig, knot_wire_get_id(msg)); + + /* Set other len. */ + knot_tsig_rdata_set_other_data(tmp_tsig, 0, 0); + + /* Append TSIG RR. */ + int ret = knot_tsig_append(msg, msg_len, msg_max_len, tmp_tsig); + + /* key_name already referenced in RRSet, no need to free separately. */ + knot_rrset_free(tmp_tsig, NULL); + + return ret; +} + +_public_ +int knot_tsig_append(uint8_t *msg, size_t *msg_len, size_t msg_max_len, + const knot_rrset_t *tsig_rr) +{ + /* Write RRSet to wire */ + int ret = knot_rrset_to_wire(tsig_rr, msg + *msg_len, + msg_max_len - *msg_len, NULL); + if (ret < 0) { + return ret; + } + + *msg_len += ret; + + knot_wire_set_arcount(msg, knot_wire_get_arcount(msg) + 1); + + return KNOT_EOK; +} diff --git a/src/libknot/tsig-op.h b/src/libknot/tsig-op.h new file mode 100644 index 0000000..b3e091d --- /dev/null +++ b/src/libknot/tsig-op.h @@ -0,0 +1,186 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \file + * + * \brief TSIG signing and validating. + * + * \addtogroup knot-tsig + * @{ + */ + +#pragma once + +#include <stdint.h> + +#include "libknot/rrtype/tsig.h" +#include "libknot/rrset.h" + +/*! + * \brief Generate TSIG signature of a message. + * + * This function generates TSIG digest of the given message prepended with the + * given Request MAC (if any) and appended with TSIG Variables. It also appends + * the resulting TSIG RR to the message wire format and accordingly adjusts + * the message size. + * + * \note This function does not save the new digest to the 'digest' parameter + * unless everything went OK. This allows sending the same buffer to + * the 'request_mac' and 'digest' parameters. + * + * \param msg Message to be signed. + * \param msg_len Size of the message in bytes. + * \param msg_max_len Maximum size of the message in bytes. + * \param request_mac Request MAC. (may be NULL). + * \param request_mac_len Size of the request MAC in bytes. + * \param digest Buffer to save the digest in. + * \param digest_len In: size of the buffer. Out: real size of the digest saved. + * \param key TSIG used for signing. + * \param tsig_rcode RCODE of the TSIG. + * \param request_time_signed Clients time signed. + * + * \retval KNOT_EOK if everything went OK. + * \retval TODO + * + * \todo This function should return TSIG errors by their codes which are + * positive values - this will be recognized by the caller. + */ +int knot_tsig_sign(uint8_t *msg, size_t *msg_len, size_t msg_max_len, + const uint8_t *request_mac, size_t request_mac_len, + uint8_t *digest, size_t *digest_len, + const knot_tsig_key_t *key, uint16_t tsig_rcode, + uint64_t request_time_signed); + +/*! + * \brief Generate TSIG signature of a 2nd or later message in a TCP session. + * + * This function generates TSIG digest of the given message prepended with the + * given Request MAC (if any) and appended with TSIG Variables. It also appends + * the resulting TSIG RR to the message wire format and accordingly adjusts + * the message size. + * + * \note This function does not save the new digest to the 'digest' parameter + * unless everything went OK. This allows sending the same buffer to + * the 'request_mac' and 'digest' parameters. + * + * \param msg Message to be signed. + * \param msg_len Size of the message in bytes. + * \param msg_max_len Maximum size of the message in bytes. + * \param prev_digest Previous digest sent by the server in the session. + * \param prev_digest_len Size of the previous digest in bytes. + * \param digest Buffer to save the digest in. + * \param digest_len In: size of the buffer. Out: real size of the digest saved. + * \param key TSIG key for signing. + * \param to_sign Data being signed. + * \param to_sign_len Size of the data being signed. + * + * \retval KNOT_EOK if successful. + * \retval TODO + * + * \todo This function should return TSIG errors by their codes which are + * positive values - this will be recognized by the caller. + */ +int knot_tsig_sign_next(uint8_t *msg, size_t *msg_len, size_t msg_max_len, + const uint8_t *prev_digest, size_t prev_digest_len, + uint8_t *digest, size_t *digest_len, + const knot_tsig_key_t *key, uint8_t *to_sign, + size_t to_sign_len); + +/*! + * \brief Checks incoming request. + * + * \param tsig_rr TSIG extracted from the packet. + * \param wire Wire format of the packet (including the TSIG RR). + * \param size Size of the wire format of packet in bytes. + * \param tsig_key TSIG key. + * + * \retval KNOT_EOK If the signature is valid. + * \retval TODO + * + * \todo This function should return TSIG errors by their codes which are + * positive values - this will be recognized by the caller. + */ +int knot_tsig_server_check(const knot_rrset_t *tsig_rr, + const uint8_t *wire, size_t size, + const knot_tsig_key_t *tsig_key); + +/*! + * \brief Checks incoming response. + * + * \param tsig_rr TSIG extracted from the packet. + * \param wire Wire format of the packet (including the TSIG RR). + * \param size Size of the wire format of packet in bytes. + * \param request_mac Request MAC. (may be NULL). + * \param request_mac_len Size of the request MAC in bytes. + * \param key TSIG key. + * \param prev_time_signed Time for TSIG period validity. + * + * \retval KNOT_EOK If the signature is valid. + * \retval TODO + * + * \todo This function should return TSIG errors by their codes which are + * positive values - this will be recognized by the caller. + */ +int knot_tsig_client_check(const knot_rrset_t *tsig_rr, + const uint8_t *wire, size_t size, + const uint8_t *request_mac, size_t request_mac_len, + const knot_tsig_key_t *key, + uint64_t prev_time_signed); + +/*! + * \brief Checks signature of 2nd or next packet in a TCP session. + * + * \param tsig_rr TSIG extracted from the packet. + * \param wire Wire format of the packet (including the TSIG RR). + * \param size Size of the wire format of packet in bytes. + * \param prev_digest Previous digest sent by the server in the session. + * \param prev_digest_len Size of the previous digest in bytes. + * \param key TSIG key. + * \param prev_time_signed Time for TSIG period validity. + * + * \retval KNOT_EOK If the signature is valid. + * \retval TODO + * + * \todo This function should return TSIG errors by their codes which are + * positive values - this will be recognized by the caller. + */ +int knot_tsig_client_check_next(const knot_rrset_t *tsig_rr, + const uint8_t *wire, size_t size, + const uint8_t *prev_digest, + size_t prev_digest_len, + const knot_tsig_key_t *key, + uint64_t prev_time_signed); + +/*! + * \todo Documentation! + */ +int knot_tsig_add(uint8_t *msg, size_t *msg_len, size_t msg_max_len, + uint16_t tsig_rcode, const knot_rrset_t *tsig_rr); + +/*! \brief Append TSIG RR to message. + * \todo Proper documentation. + */ +int knot_tsig_append(uint8_t *msg, size_t *msg_len, size_t msg_max_len, + const knot_rrset_t *tsig_rr); + +/*! \brief Return true if the TSIG RCODE allows signing the packet. + * \todo Proper documentation. + */ +static inline bool knot_tsig_can_sign(uint16_t tsig_rcode) { + return tsig_rcode == KNOT_RCODE_NOERROR || tsig_rcode == KNOT_RCODE_BADTIME; +} + +/*! @} */ diff --git a/src/libknot/tsig.c b/src/libknot/tsig.c new file mode 100644 index 0000000..465307b --- /dev/null +++ b/src/libknot/tsig.c @@ -0,0 +1,186 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <assert.h> +#include <string.h> + +#include "contrib/getline.h" +#include "contrib/string.h" +#include "libdnssec/error.h" +#include "libknot/attribute.h" +#include "libknot/errcode.h" +#include "libknot/tsig.h" + +_public_ +void knot_tsig_key_deinit(knot_tsig_key_t *key) +{ + if (!key) { + return; + } + + knot_dname_free(key->name, NULL); + + memset(key->secret.data, 0, key->secret.size); + dnssec_binary_free(&key->secret); + + memset(key, '\0', sizeof(*key)); +} + +_public_ +int knot_tsig_key_init(knot_tsig_key_t *key, const char *algorithm_name, + const char *name, const char *secret_b64) +{ + if (!name || !secret_b64 || !key) { + return KNOT_EINVAL; + } + + dnssec_tsig_algorithm_t algorithm = DNSSEC_TSIG_HMAC_SHA256; + if (algorithm_name != NULL) { + algorithm = dnssec_tsig_algorithm_from_name(algorithm_name); + if (algorithm == DNSSEC_TSIG_UNKNOWN) { + return KNOT_EMALF; + } + } + + knot_dname_t *dname = knot_dname_from_str_alloc(name); + if (!dname) { + return KNOT_ENOMEM; + } + knot_dname_to_lower(dname); + + dnssec_binary_t b64secret = { 0 }; + b64secret.data = (uint8_t *)secret_b64; + b64secret.size = strlen(secret_b64); + + dnssec_binary_t secret = { 0 }; + int result = dnssec_binary_from_base64(&b64secret, &secret); + if (result != KNOT_EOK) { + knot_dname_free(dname, NULL); + return result; + } + + key->name = dname; + key->algorithm = algorithm; + key->secret = secret; + + return KNOT_EOK; +} + +_public_ +int knot_tsig_key_init_str(knot_tsig_key_t *key, const char *params) +{ + if (!params) { + return KNOT_EINVAL; + } + + char *copy = strstrip(params); + if (!copy) { + return KNOT_ENOMEM; + } + + size_t copy_size = strlen(copy) + 1; + + // format [algorithm:]name:secret + + char *algorithm = NULL; + char *name = NULL; + char *secret = NULL; + + // find secret + + char *pos = strrchr(copy, ':'); + if (pos) { + *pos = '\0'; + secret = pos + 1; + } else { + memset(copy, 0, copy_size); + free(copy); + return KNOT_EMALF; + } + + // find name and optionally algorithm + + pos = strchr(copy, ':'); + if (pos) { + *pos = '\0'; + algorithm = copy; + name = pos + 1; + } else { + name = copy; + } + + int result = knot_tsig_key_init(key, algorithm, name, secret); + + memset(copy, 0, copy_size); + free(copy); + + return result; +} + +_public_ +int knot_tsig_key_init_file(knot_tsig_key_t *key, const char *filename) +{ + if (!filename) { + return KNOT_EINVAL; + } + + FILE *file = fopen(filename, "r"); + if (!file) { + return KNOT_EACCES; + } + + char *line = NULL; + size_t line_size = 0; + ssize_t read = knot_getline(&line, &line_size, file); + + fclose(file); + + if (read == -1) { + return KNOT_EMALF; + } + + int result = knot_tsig_key_init_str(key, line); + + memset(line, 0, line_size); + free(line); + + return result; +} + +_public_ +int knot_tsig_key_copy(knot_tsig_key_t *dst, const knot_tsig_key_t *src) +{ + if (!src || !dst) { + return KNOT_EINVAL; + } + + knot_tsig_key_t copy = { 0 }; + copy.algorithm = src->algorithm; + + copy.name = knot_dname_copy(src->name, NULL); + if (!copy.name) { + return KNOT_ENOMEM; + } + + if (dnssec_binary_dup(&src->secret, ©.secret) != DNSSEC_EOK) { + knot_tsig_key_deinit(©); + return KNOT_ENOMEM; + } + + *dst = copy; + + return KNOT_EOK; +} diff --git a/src/libknot/tsig.h b/src/libknot/tsig.h new file mode 100644 index 0000000..f3e4bba --- /dev/null +++ b/src/libknot/tsig.h @@ -0,0 +1,92 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +/*! + * \file + * + * \brief TSIG operations + * + * \addtogroup knot-tsig + * @{ + */ + +#pragma once + +#include "libknot/dname.h" +#include "libdnssec/tsig.h" + +/*! + * \brief TSIG key. + */ +struct knot_tsig_key { + dnssec_tsig_algorithm_t algorithm; + knot_dname_t *name; + dnssec_binary_t secret; +}; +typedef struct knot_tsig_key knot_tsig_key_t; + +/*! + * \brief Packet signing context. + */ +typedef struct knot_sign_context { + knot_tsig_key_t tsig_key; + uint8_t *tsig_buf; + uint8_t *tsig_digest; + size_t tsig_buflen; + size_t tsig_digestlen; + uint8_t tsig_runlen; + uint64_t tsig_time_signed; + size_t pkt_count; +} knot_sign_context_t; + +/*! + * \brief Initialize a new TSIG key from individual key parameters. + * + * \param[out] key Key to be initialized. + * \param[in] algorithm Algorithm name. NULL for default (hmac-md5). + * \param[in] name Key name (domain name in presentation format). + * \param[in] secret_b64 Secret encoded using Base 64. + * + * \return Error code, KNOT_EOK if successful. + */ +int knot_tsig_key_init(knot_tsig_key_t *key, const char *algorithm, + const char *name, const char *secret_b64); + +/*! + * \brief Create a new TSIG key from a string encoding all parameters. + * + * \param[out] key Key to be initialized. + * \param[in] params Parameters in a form \a [algorithm:]name:base64_secret + */ +int knot_tsig_key_init_str(knot_tsig_key_t *key, const char *params); + +/*! + * \brief Create a new TSIG key by reading the parameters from a file. + * + * The file content is parsed by \a tsig_key_create_str. + */ +int knot_tsig_key_init_file(knot_tsig_key_t *key, const char *filename); + +/*! + * \brief Deinitialize TSIG key. + */ +void knot_tsig_key_deinit(knot_tsig_key_t *key); + +/*! + * \brief Duplicate a TSIG key. + */ +int knot_tsig_key_copy(knot_tsig_key_t *dst, const knot_tsig_key_t *src); + +/*! @} */ diff --git a/src/libknot/version.h b/src/libknot/version.h new file mode 100644 index 0000000..df15570 --- /dev/null +++ b/src/libknot/version.h @@ -0,0 +1,25 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#define KNOT_VERSION_MAJOR 2 +#define KNOT_VERSION_MINOR 7 +#define KNOT_VERSION_PATCH 0x06 + +#define KNOT_VERSION_HEX ((KNOT_VERSION_MAJOR << 16) | \ + (KNOT_VERSION_MINOR << 8) | \ + (KNOT_VERSION_PATCH)) diff --git a/src/libknot/version.h.in b/src/libknot/version.h.in new file mode 100644 index 0000000..bbe8742 --- /dev/null +++ b/src/libknot/version.h.in @@ -0,0 +1,25 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#define KNOT_VERSION_MAJOR @KNOT_VERSION_MAJOR@ +#define KNOT_VERSION_MINOR @KNOT_VERSION_MINOR@ +#define KNOT_VERSION_PATCH 0x0@KNOT_VERSION_PATCH@ + +#define KNOT_VERSION_HEX ((KNOT_VERSION_MAJOR << 16) | \ + (KNOT_VERSION_MINOR << 8) | \ + (KNOT_VERSION_PATCH)) diff --git a/src/libknot/wire.h b/src/libknot/wire.h new file mode 100644 index 0000000..5f69d9f --- /dev/null +++ b/src/libknot/wire.h @@ -0,0 +1,153 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \file + * + * \brief Wire integer operations. + * + * \addtogroup wire + * @{ + */ + +#pragma once + +#include <assert.h> +#include <stdint.h> +#include <string.h> + +#include "libknot/endian.h" + +/*! + * \brief Reads 2 bytes from the wireformat data. + * + * \param pos Data to read the 2 bytes from. + * + * \return The 2 bytes read, in host byte order. + */ +inline static uint16_t knot_wire_read_u16(const uint8_t *pos) +{ + assert(pos); + uint16_t result; + memcpy(&result, pos, sizeof(result)); + return be16toh(result); +} + +/*! + * \brief Reads 4 bytes from the wireformat data. + * + * \param pos Data to read the 4 bytes from. + * + * \return The 4 bytes read, in host byte order. + */ +inline static uint32_t knot_wire_read_u32(const uint8_t *pos) +{ + assert(pos); + uint32_t result; + memcpy(&result, pos, sizeof(result)); + return be32toh(result); +} + +/*! + * \brief Reads 6 bytes from the wireformat data. + * + * \param pos Data to read the 6 bytes from. + * + * \return The 6 bytes read, in host byte order. + */ +inline static uint64_t knot_wire_read_u48(const uint8_t *pos) +{ + assert(pos); + uint64_t input = 0; + memcpy((uint8_t *)&input + 1, pos, 6); + return be64toh(input) >> 8; +} + +/*! + * \brief Read 8 bytes from the wireformat data. + * + * \param pos Data to read the 8 bytes from. + * + * \return The 8 bytes read, in host byte order. + */ +inline static uint64_t knot_wire_read_u64(const uint8_t *pos) +{ + assert(pos); + uint64_t result; + memcpy(&result, pos, sizeof(result)); + return be64toh(result); +} + +/*! + * \brief Writes 2 bytes in wireformat. + * + * The data are stored in network byte order (big endian). + * + * \param pos Position where to put the 2 bytes. + * \param data Data to put. + */ +inline static void knot_wire_write_u16(uint8_t *pos, uint16_t data) +{ + assert(pos); + uint16_t beval = htobe16(data); + memcpy(pos, &beval, sizeof(beval)); +} + +/*! + * \brief Writes 4 bytes in wireformat. + * + * The data are stored in network byte order (big endian). + * + * \param pos Position where to put the 4 bytes. + * \param data Data to put. + */ +inline static void knot_wire_write_u32(uint8_t *pos, uint32_t data) +{ + assert(pos); + uint32_t beval = htobe32(data); + memcpy(pos, &beval, sizeof(beval)); +} + +/*! + * \brief Writes 6 bytes in wireformat. + * + * The data are stored in network byte order (big endian). + * + * \param pos Position where to put the 4 bytes. + * \param data Data to put. + */ +inline static void knot_wire_write_u48(uint8_t *pos, uint64_t data) +{ + assert(pos); + uint64_t swapped = htobe64(data << 8); + memcpy(pos, (uint8_t *)&swapped + 1, 6); +} + +/*! + * \brief Writes 8 bytes in wireformat. + * + * The data are stored in network byte order (big endian). + * + * \param pos Position where to put the 8 bytes. + * \param data Data to put. + */ +inline static void knot_wire_write_u64(uint8_t *pos, uint64_t data) +{ + assert(pos); + uint64_t beval = htobe64(data); + memcpy(pos, &beval, sizeof(beval)); +} + +/*! @} */ diff --git a/src/libknot/yparser/yparser.c b/src/libknot/yparser/yparser.c new file mode 100644 index 0000000..91d472d --- /dev/null +++ b/src/libknot/yparser/yparser.c @@ -0,0 +1,172 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <unistd.h> + +#include "libknot/yparser/yparser.h" +#include "libknot/attribute.h" +#include "libknot/errcode.h" + +extern int _yp_start_state; +extern int _yp_parse(yp_parser_t *parser); + +_public_ +void yp_init( + yp_parser_t *parser) +{ + if (parser == NULL) { + return; + } + + memset(parser, 0, sizeof(*parser)); + + parser->cs = _yp_start_state; + parser->file.descriptor = -1; + parser->line_count = 1; +} + +_public_ +void yp_deinit( + yp_parser_t *parser) +{ + if (parser == NULL) { + return; + } + + if (parser->file.descriptor != -1) { + munmap((void *)parser->input.start, + parser->input.end - parser->input.start); + close(parser->file.descriptor); + free(parser->file.name); + } +} + +_public_ +int yp_set_input_string( + yp_parser_t *parser, + const char *input, + size_t size) +{ + if (parser == NULL || input == NULL) { + return KNOT_EINVAL; + } + + // Reinitialize the parser. + yp_deinit(parser); + yp_init(parser); + + // Set the parser input limits. + parser->input.start = input; + parser->input.current = input; + parser->input.end = input + size; + parser->input.eof = false; + + return KNOT_EOK; +} + +_public_ +int yp_set_input_file( + yp_parser_t *parser, + const char *file_name) +{ + if (parser == NULL || file_name == NULL) { + return KNOT_EINVAL; + } + + // Reinitialize the parser. + yp_deinit(parser); + yp_init(parser); + + // Try to open the file. + parser->file.descriptor = open(file_name, O_RDONLY); + if (parser->file.descriptor == -1) { + return knot_map_errno(); + } + + // Check for regular file input. + struct stat file_stat; + if (fstat(parser->file.descriptor, &file_stat) == -1) { + close(parser->file.descriptor); + return knot_map_errno(); + } else if (!S_ISREG(file_stat.st_mode)) { + close(parser->file.descriptor); + return KNOT_EFILE; + } + + char *start = NULL; + + // Check for empty file (cannot mmap). + if (file_stat.st_size > 0) { + // Map the file to the memory. + start = mmap(0, file_stat.st_size, PROT_READ, MAP_SHARED, + parser->file.descriptor, 0); + if (start == MAP_FAILED) { + close(parser->file.descriptor); + return KNOT_ENOMEM; + } + + // Try to set the mapped memory advise to sequential. + (void)madvise(start, file_stat.st_size, MADV_SEQUENTIAL); + + parser->input.eof = false; + } else { + parser->input.eof = true; + } + + parser->file.name = strdup(file_name); + + // Set the parser input limits. + parser->input.start = start; + parser->input.current = start; + parser->input.end = start + file_stat.st_size; + + return KNOT_EOK; +} + +_public_ +int yp_parse( + yp_parser_t *parser) +{ + if (parser == NULL) { + return KNOT_EINVAL; + } + + int ret = KNOT_EPARSEFAIL; + + // Run the parser until found new item, error or end of input. + do { + // Check for the end of the input. + if (parser->input.current == parser->input.end) { + if (parser->input.eof) { + // End of parsing. + return KNOT_EOF; + } else { + // Set the parser to final parsing. + parser->input.eof = true; + } + } + + // Parse the next item. + ret = _yp_parse(parser); + } while (ret == KNOT_EFEWDATA); + + return ret; +} diff --git a/src/libknot/yparser/yparser.h b/src/libknot/yparser/yparser.h new file mode 100644 index 0000000..b01870d --- /dev/null +++ b/src/libknot/yparser/yparser.h @@ -0,0 +1,148 @@ +/* Copyright (C) 2015 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +/*! + * \file + * + * \brief Simple parser (Yparser) of a YAML-inspired data format. + * + * \addtogroup yparser + * @{ + */ + +#pragma once + +#include <stdbool.h> +#include <stddef.h> + +/*! Maximal length of textual key value. */ +#define YP_MAX_TXT_KEY_LEN 128 +/*! Maximal length of textual data value. */ +#define YP_MAX_TXT_DATA_LEN 32768 + +/*! Parser events indicating type of lastly parsed item. */ +typedef enum { + YP_ENULL = 0, /*!< No valid data. */ + YP_EKEY0, /*!< First level item. */ + YP_EKEY1, /*!< Second level item. */ + YP_EID, /*!< Second level identifier. */ +} yp_event_t; + +/*! Context structure of yparser. */ +typedef struct { + /*! Current parser state (Ragel internals). */ + int cs; + /*! Indication if the current item was already processed. */ + bool processed; + /*! Current block indentation. */ + size_t indent; + /*! Last id dash position. */ + size_t id_pos; + + /*! Input parameters. */ + struct { + /*! Start of the block. */ + const char *start; + /*! Current parser position. */ + const char *current; + /*! End of the block. */ + const char *end; + /*! Indication for the final block parsing. */ + bool eof; + } input; + + /*! File input parameters. */ + struct { + /*! File name. */ + char *name; + /*! File descriptor. */ + int descriptor; + } file; + + /*! [out] Current line number (error location). */ + size_t line_count; + /*! [out] Current event. */ + yp_event_t event; + /*! [out] Parsed key (zero terminated string). */ + char key[YP_MAX_TXT_KEY_LEN]; + /*! [out] Key length. */ + size_t key_len; + /*! [out] Parsed data (zero terminated string). */ + char data[YP_MAX_TXT_DATA_LEN]; + /*! [out] Data length. */ + size_t data_len; +} yp_parser_t; + +/*! + * Initializes the parser. + * + * \param[in] parser Parser context. + */ +void yp_init( + yp_parser_t *parser +); + +/*! + * Deinitializes the parser. + * + * \param[in] parser Parser context. + */ +void yp_deinit( + yp_parser_t *parser +); + +/*! + * Sets the parser to parse given string. + * + * \param[in] parser Parser context. + * \param[in] input The string to parse. + * \param[in] size Length of the string. + * + * \return Error code, KNOT_EOK if success. + */ +int yp_set_input_string( + yp_parser_t *parser, + const char *input, + size_t size +); + +/*! + * Sets the parser to parse given file. + * + * \param[in] parser Parser context. + * \param[in] file_name The filename to parse. + * + * \return Error code, KNOT_EOK if success. + */ +int yp_set_input_file( + yp_parser_t *parser, + const char *file_name +); + +/*! + * Parses one item from the input. + * + * If the item has more values, this function returns for each value. The item + * can also have no value. + * + * \param[in] parser Parser context. + * + * \return Error code, KNOT_EOK if success, KNOT_EOF if end of data. + */ +int yp_parse( + yp_parser_t *parser +); + +/*! @} */ diff --git a/src/libknot/yparser/ypbody.c b/src/libknot/yparser/ypbody.c new file mode 100644 index 0000000..bfceaf0 --- /dev/null +++ b/src/libknot/yparser/ypbody.c @@ -0,0 +1,456 @@ + +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <string.h> + +#include "libknot/yparser/yparser.h" +#include "libknot/errcode.h" + + + + +// Include parser static data (Ragel internals). + +static const char _yparser_actions[] = { + 0, 1, 0, 1, 1, 1, 2, 1, + 3, 1, 4, 1, 6, 1, 8, 1, + 9, 1, 10, 1, 11, 1, 12, 1, + 15, 2, 1, 0, 2, 1, 2, 2, + 2, 0, 2, 3, 4, 2, 5, 4, + 2, 6, 0, 2, 7, 8, 2, 12, + 13, 2, 14, 12, 3, 1, 2, 0, + 3, 1, 7, 8, 3, 1, 12, 13, + 3, 1, 14, 12, 3, 2, 7, 8, + 3, 2, 12, 13, 3, 2, 14, 12, + 4, 1, 2, 7, 8, 4, 1, 2, + 12, 13, 4, 1, 2, 14, 12 +}; + +static const unsigned char _yparser_key_offsets[] = { + 0, 0, 13, 15, 16, 25, 36, 38, + 39, 49, 60, 71, 73, 76, 89, 93, + 96, 100, 102, 105, 115, 124, 127, 130, + 133, 137, 140, 143, 146, 157, 170, 183, + 196 +}; + +static const char _yparser_trans_keys[] = { + 10, 13, 32, 35, 45, 46, 92, 48, + 57, 65, 90, 97, 122, 10, 13, 32, + 32, 46, 92, 48, 57, 65, 90, 97, + 122, 32, 58, 92, 45, 46, 48, 57, + 65, 90, 97, 122, 32, 58, 32, 32, + 33, 34, 92, 36, 43, 45, 90, 94, + 126, 10, 13, 32, 33, 92, 36, 43, + 45, 90, 94, 126, 32, 58, 92, 45, + 46, 48, 57, 65, 90, 97, 122, 32, + 58, 10, 13, 32, 10, 13, 32, 34, + 35, 91, 92, 33, 43, 45, 90, 94, + 126, 34, 92, 32, 126, 10, 13, 32, + 10, 13, 32, 35, 10, 13, 34, 32, + 126, 32, 33, 34, 92, 36, 43, 45, + 90, 94, 126, 32, 33, 44, 92, 93, + 36, 90, 94, 126, 32, 44, 93, 10, + 13, 32, 34, 32, 126, 34, 92, 32, + 126, 32, 44, 93, 34, 32, 126, 34, + 32, 126, 32, 58, 92, 45, 46, 48, + 57, 65, 90, 97, 122, 10, 13, 32, + 35, 45, 46, 92, 48, 57, 65, 90, + 97, 122, 10, 13, 32, 35, 45, 46, + 92, 48, 57, 65, 90, 97, 122, 10, + 13, 32, 35, 45, 46, 92, 48, 57, + 65, 90, 97, 122, 10, 13, 32, 35, + 45, 46, 92, 48, 57, 65, 90, 97, + 122, 0 +}; + +static const char _yparser_single_lengths[] = { + 0, 7, 2, 1, 3, 3, 2, 1, + 4, 5, 3, 2, 3, 7, 2, 3, + 4, 2, 1, 4, 5, 3, 3, 1, + 2, 3, 1, 1, 3, 7, 7, 7, + 7 +}; + +static const char _yparser_range_lengths[] = { + 0, 3, 0, 0, 3, 4, 0, 0, + 3, 3, 4, 0, 0, 3, 1, 0, + 0, 0, 1, 3, 2, 0, 0, 1, + 1, 0, 1, 1, 4, 3, 3, 3, + 3 +}; + +static const unsigned char _yparser_index_offsets[] = { + 0, 0, 11, 14, 16, 23, 31, 34, + 36, 44, 53, 61, 64, 68, 79, 83, + 87, 92, 95, 98, 106, 114, 118, 122, + 125, 129, 133, 136, 139, 147, 158, 169, + 180 +}; + +static const char _yparser_indicies[] = { + 1, 2, 3, 4, 5, 6, 6, 6, + 6, 6, 0, 1, 2, 4, 7, 0, + 7, 8, 8, 8, 8, 8, 0, 9, + 11, 10, 10, 10, 10, 10, 0, 12, + 13, 0, 14, 0, 14, 15, 16, 17, + 15, 15, 15, 0, 18, 19, 20, 21, + 22, 21, 21, 21, 0, 23, 25, 24, + 24, 24, 24, 24, 0, 26, 27, 0, + 28, 29, 30, 0, 28, 29, 30, 16, + 31, 32, 17, 15, 15, 15, 0, 34, + 35, 33, 0, 18, 19, 20, 0, 28, + 29, 36, 31, 0, 28, 29, 31, 37, + 33, 0, 32, 38, 39, 40, 38, 38, + 38, 0, 41, 42, 43, 44, 45, 42, + 42, 0, 46, 32, 47, 0, 28, 29, + 36, 0, 48, 42, 0, 50, 51, 49, + 0, 41, 43, 45, 0, 52, 49, 0, + 53, 21, 0, 54, 56, 55, 55, 55, + 55, 55, 0, 1, 2, 3, 4, 5, + 57, 57, 57, 57, 57, 0, 58, 59, + 60, 61, 62, 63, 63, 63, 63, 63, + 0, 64, 65, 66, 67, 68, 69, 69, + 69, 69, 69, 0, 70, 71, 72, 73, + 74, 75, 75, 75, 75, 75, 0, 0 +}; + +static const char _yparser_trans_targs[] = { + 0, 30, 31, 1, 2, 3, 28, 4, + 5, 6, 5, 7, 6, 7, 8, 9, + 14, 27, 32, 29, 16, 9, 27, 11, + 10, 12, 11, 12, 32, 29, 13, 17, + 19, 14, 15, 18, 16, 14, 20, 24, + 23, 21, 20, 19, 23, 22, 21, 22, + 20, 24, 25, 26, 24, 9, 11, 28, + 12, 10, 30, 31, 1, 2, 3, 10, + 30, 31, 1, 2, 3, 10, 30, 31, + 1, 2, 3, 10 +}; + +static const char _yparser_trans_actions[] = { + 23, 1, 0, 46, 0, 49, 43, 21, + 43, 19, 13, 19, 0, 0, 0, 34, + 7, 34, 40, 11, 11, 9, 9, 15, + 13, 15, 0, 0, 1, 0, 0, 0, + 0, 9, 0, 9, 0, 37, 34, 7, + 34, 11, 9, 11, 9, 11, 0, 0, + 37, 9, 0, 9, 37, 37, 17, 13, + 17, 43, 52, 28, 85, 28, 90, 80, + 31, 5, 72, 5, 76, 68, 25, 3, + 60, 3, 64, 56 +}; + +static const char _yparser_eof_actions[] = { + 0, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 23, 23, 23, + 23, 23, 23, 23, 23, 0, 28, 5, + 3 +}; + + + + + +int _yp_start_state = 29; + +int _yp_parse( + yp_parser_t *parser) +{ + // Parser input limits (Ragel internals). + const char *p, *pe, *eof; + + // Current item indent. + size_t indent = 0; + // Current id dash position. + size_t id_pos = 0; + // Indicates if the current parsing step contains an item. + bool found = false; + + if (!parser->input.eof) { // Restore parser input limits. + p = parser->input.current; + pe = parser->input.end; + eof = NULL; + } else { // Set the last artificial block with just one new line char. + p = "\n"; + pe = p + 1; + eof = pe; + } + + // Include parser body. + + { + int _klen; + unsigned int _trans; + const char *_acts; + unsigned int _nacts; + const char *_keys; + + if ( p == pe ) + goto _test_eof; + if ( parser->cs == 0 ) + goto _out; +_resume: + _keys = _yparser_trans_keys + _yparser_key_offsets[ parser->cs]; + _trans = _yparser_index_offsets[ parser->cs]; + + _klen = _yparser_single_lengths[ parser->cs]; + if ( _klen > 0 ) { + const char *_lower = _keys; + const char *_mid; + const char *_upper = _keys + _klen - 1; + while (1) { + if ( _upper < _lower ) + break; + + _mid = _lower + ((_upper-_lower) >> 1); + if ( (*p) < *_mid ) + _upper = _mid - 1; + else if ( (*p) > *_mid ) + _lower = _mid + 1; + else { + _trans += (unsigned int)(_mid - _keys); + goto _match; + } + } + _keys += _klen; + _trans += _klen; + } + + _klen = _yparser_range_lengths[ parser->cs]; + if ( _klen > 0 ) { + const char *_lower = _keys; + const char *_mid; + const char *_upper = _keys + (_klen<<1) - 2; + while (1) { + if ( _upper < _lower ) + break; + + _mid = _lower + (((_upper-_lower) >> 1) & ~1); + if ( (*p) < _mid[0] ) + _upper = _mid - 2; + else if ( (*p) > _mid[1] ) + _lower = _mid + 2; + else { + _trans += (unsigned int)((_mid - _keys)>>1); + goto _match; + } + } + _trans += _klen; + } + +_match: + _trans = _yparser_indicies[_trans]; + parser->cs = _yparser_trans_targs[_trans]; + + if ( _yparser_trans_actions[_trans] == 0 ) + goto _again; + + _acts = _yparser_actions + _yparser_trans_actions[_trans]; + _nacts = (unsigned int) *_acts++; + while ( _nacts-- > 0 ) + { + switch ( *_acts++ ) + { + case 0: + { + // Return if key without value. + if (parser->event != YP_ENULL && !parser->processed) { + parser->processed = true; + found = true; + {p++; goto _out; } + } + } + break; + case 1: + { + parser->line_count++; + parser->event = YP_ENULL; + parser->processed = false; + } + break; + case 2: + { + indent = 0; + id_pos = 0; + } + break; + case 3: + { + parser->data_len = 0; + } + break; + case 4: + { + if (parser->data_len >= sizeof(parser->data) - 1) { + return KNOT_ESPACE; + } + parser->data[parser->data_len++] = (*p); + } + break; + case 5: + { + parser->data_len--; + } + break; + case 6: + { + // Return if a value parsed. + parser->data[parser->data_len] = '\0'; + parser->processed = true; + found = true; + {p++; goto _out; } + } + break; + case 7: + { + if (indent > 0 && parser->indent > 0 && + indent != parser->indent) { + return KNOT_YP_EINVAL_INDENT; + } + parser->processed = false; + parser->key_len = 0; + parser->data_len = 0; + parser->event = YP_ENULL; + } + break; + case 8: + { + if (parser->key_len >= sizeof(parser->key) - 1) { + return KNOT_ESPACE; + } + parser->key[parser->key_len++] = (*p); + } + break; + case 9: + { + parser->key[parser->key_len] = '\0'; + parser->indent = 0; + parser->id_pos = 0; + parser->event = YP_EKEY0; + } + break; + case 10: + { + parser->key[parser->key_len] = '\0'; + parser->indent = indent; + parser->event = YP_EKEY1; + } + break; + case 11: + { + parser->key[parser->key_len] = '\0'; + parser->indent = indent; + parser->id_pos = id_pos; + parser->event = YP_EID; + } + break; + case 12: + { + indent++; + } + break; + case 13: + { + id_pos++; + } + break; + case 14: + { + if (id_pos > 0 && parser->id_pos > 0 && + id_pos != parser->id_pos) { + return KNOT_YP_EINVAL_INDENT; + } + parser->indent = 0; + } + break; + case 15: + { + switch ((*p)) { + case '\t': + return KNOT_YP_ECHAR_TAB; + default: + return KNOT_EPARSEFAIL; + } + } + break; + } + } + +_again: + if ( parser->cs == 0 ) + goto _out; + if ( ++p != pe ) + goto _resume; + _test_eof: {} + if ( p == eof ) + { + const char *__acts = _yparser_actions + _yparser_eof_actions[ parser->cs]; + unsigned int __nacts = (unsigned int) *__acts++; + while ( __nacts-- > 0 ) { + switch ( *__acts++ ) { + case 1: + { + parser->line_count++; + parser->event = YP_ENULL; + parser->processed = false; + } + break; + case 2: + { + indent = 0; + id_pos = 0; + } + break; + case 15: + { + switch ((*p)) { + case '\t': + return KNOT_YP_ECHAR_TAB; + default: + return KNOT_EPARSEFAIL; + } + } + break; + } + } + } + + _out: {} + } + + + // Store the current parser position. + if (!parser->input.eof) { + parser->input.current = p; + } else { + parser->input.current = parser->input.end; + } + + // Check for general parser error. + if (parser->cs == 0) { + return KNOT_EPARSEFAIL; + } + + // Check if parsed an item. + if (found) { + return KNOT_EOK; + } else { + return KNOT_EFEWDATA; + } +} diff --git a/src/libknot/yparser/ypformat.c b/src/libknot/yparser/ypformat.c new file mode 100644 index 0000000..2648c75 --- /dev/null +++ b/src/libknot/yparser/ypformat.c @@ -0,0 +1,121 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stdio.h> + +#include "libknot/yparser/yptrafo.h" +#include "libknot/attribute.h" +#include "libknot/errcode.h" + +static int format_item( + const yp_item_t *item, + const uint8_t *data, + size_t data_len, + char *out, + size_t out_len, + yp_style_t style, + const char *prefix, + bool first_value, + bool last_value) +{ + if (item == NULL || out == NULL || prefix == NULL) { + return KNOT_EINVAL; + } + + // Format key part. + int ret = snprintf(out, out_len, "%s%s%s%s", + first_value ? prefix : "", + first_value ? item->name + 1 : "", + first_value ? ":" : "", + item->type == YP_TGRP ? + "\n" : (first_value && !last_value ? " [ " : " ")); + if (ret < 0 || ret >= out_len) { + return KNOT_ESPACE; + } + out += ret; + out_len -= ret; + + // Finish if group. + if (item->type == YP_TGRP) { + return KNOT_EOK; + } + + // Format data part. + size_t aux_len = out_len; + ret = yp_item_to_txt(item, data, data_len, out, &aux_len, style); + if (ret != KNOT_EOK) { + return ret; + } + out += aux_len; + out_len -= aux_len; + + // Format data end. + ret = snprintf(out, out_len, "%s%s", + last_value && !first_value ? " ]" : "", + last_value ? "\n" : ","); + if (ret < 0 || ret >= out_len) { + return KNOT_ESPACE; + } + + return KNOT_EOK; +} + +_public_ +int yp_format_key0( + const yp_item_t *item, + const uint8_t *data, + size_t data_len, + char *out, + size_t out_len, + yp_style_t style, + bool first_value, + bool last_value) +{ + return format_item(item, data, data_len, out, out_len, style, "", + first_value, last_value); +} + +_public_ +int yp_format_id( + const yp_item_t *item, + const uint8_t *data, + size_t data_len, + char *out, + size_t out_len, + yp_style_t style) +{ + if (data == NULL) { + return KNOT_EINVAL; + } + + return format_item(item, data, data_len, out, out_len, style, " - ", + true, true); +} + +_public_ +int yp_format_key1( + const yp_item_t *item, + const uint8_t *data, + size_t data_len, + char *out, + size_t out_len, + yp_style_t style, + bool first_value, + bool last_value) +{ + return format_item(item, data, data_len, out, out_len, style, " ", + first_value, last_value); +} diff --git a/src/libknot/yparser/ypformat.h b/src/libknot/yparser/ypformat.h new file mode 100644 index 0000000..605d509 --- /dev/null +++ b/src/libknot/yparser/ypformat.h @@ -0,0 +1,100 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +/*! + * \file + * + * \brief Tools for Yparser format creation. + * + * \addtogroup yparser + * @{ + */ + +#pragma once + +#include "libknot/yparser/ypschema.h" + +/*! + * Formats key0 item. + * + * \param[in] item Schema item to format. + * \param[in] data Data to format. + * \param[in] data_len Data length. + * \param[out] out Output buffer. + * \param[in, out] out_len Output buffer length, output length. + * \param[in] style Value style. + * \param[in] first_value First value indication (multivalued support). + * \param[in] last_value Last value indication (multivalued support). + * + * \return Error code, KNOT_EOK if success. + */ +int yp_format_key0( + const yp_item_t *item, + const uint8_t *data, + size_t data_len, + char *out, + size_t out_len, + yp_style_t style, + bool first_value, + bool last_value +); + +/*! + * Formats identifier item. + * + * \param[in] item Schema item to format. + * \param[in] data Data to format. + * \param[in] data_len Data length. + * \param[out] out Output buffer. + * \param[in, out] out_len Output buffer length, output length. + * \param[in] style Value style. + * + * \return Error code, KNOT_EOK if success. + */ +int yp_format_id( + const yp_item_t *item, + const uint8_t *data, + size_t data_len, + char *out, + size_t out_len, + yp_style_t style +); + +/*! + * Formats key1 item. + * + * \param[in] item Schema item to format. + * \param[in] data Data to format. + * \param[in] data_len Data length. + * \param[out] out Output buffer. + * \param[in, out] out_len Output buffer length, output length. + * \param[in] style Value style. + * \param[in] first_value First value indication (multivalued support). + * \param[in] last_value Last value indication (multivalued support). + * + * \return Error code, KNOT_EOK if success. + */ +int yp_format_key1( + const yp_item_t *item, + const uint8_t *data, + size_t data_len, + char *out, + size_t out_len, + yp_style_t style, + bool first_value, + bool last_value +); + +/*! @} */ diff --git a/src/libknot/yparser/ypschema.c b/src/libknot/yparser/ypschema.c new file mode 100644 index 0000000..7ffb7b7 --- /dev/null +++ b/src/libknot/yparser/ypschema.c @@ -0,0 +1,575 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +#include "libknot/yparser/ypschema.h" +#include "libknot/yparser/yptrafo.h" +#include "libknot/attribute.h" +#include "libknot/errcode.h" + +static size_t schema_count( + const yp_item_t *src) +{ + size_t count = 0; + for (const yp_item_t *item = src; item->name != NULL; item++) { + count++; + } + + return count; +} + +/*! Initializes the referenced item. */ +static int set_ref_item( + yp_item_t *dst, + const yp_item_t *schema) +{ + if (schema == NULL) { + return KNOT_EINVAL; + } + + // Get reference category. + const yp_name_t *ref_name = dst->var.r.ref_name; + const yp_item_t *ref = yp_schema_find(ref_name, NULL, schema); + if (ref == NULL) { + return KNOT_YP_EINVAL_ITEM; + } + + dst->var.r.ref = ref; + + return KNOT_EOK; +} + +/*! Copies the sub_items list and initializes pointer to the identifier item. */ +static int set_grp_item( + yp_item_t *dst, + const yp_item_t *src, + const yp_item_t *schema) +{ + // Count subitems. + size_t count = schema_count(src->var.g.sub_items); + + // Allocate space for subitems + terminal zero item. + size_t memsize = (count + 1) * sizeof(yp_item_t); + dst->sub_items = malloc(memsize); + if (dst->sub_items == NULL) { + return KNOT_ENOMEM; + } + memset(dst->sub_items, 0, memsize); + + // Copy subitems. + for (size_t i = 0; i < count; i++) { + // The first item is an identifier if multi group. + if (i == 0 && (dst->flags & YP_FMULTI)) { + dst->var.g.id = &dst->sub_items[0]; + } + + // Copy sub-item. + dst->sub_items[i] = src->var.g.sub_items[i]; + + // Initialize sub-item. + int ret = KNOT_EOK; + switch (dst->sub_items[i].type) { + case YP_TREF: + ret = set_ref_item(dst->sub_items + i, schema); + break; + case YP_TGRP: // Deeper hierarchy is not supported. + ret = KNOT_ENOTSUP; + break; + default: + break; + } + + // Set the parent item. + dst->sub_items[i].parent = dst; + + if (ret != KNOT_EOK) { + free(dst->sub_items); + dst->sub_items = NULL; + return ret; + } + } + + if (src->flags & YP_FALLOC) { + dst->var.g.sub_items = malloc(memsize); + if (dst->var.g.sub_items == NULL) { + free(dst->sub_items); + dst->sub_items = NULL; + return KNOT_ENOMEM; + } + memcpy((void *)dst->var.g.sub_items, src->var.g.sub_items, memsize); + } + + return KNOT_EOK; +} + +static int set_item( + yp_item_t *dst, + const yp_item_t *src, + const yp_item_t *schema) +{ + // Check maximal item name length. + if ((uint8_t)src->name[0] > YP_MAX_ITEM_NAME_LEN) { + return KNOT_ERANGE; + } + + // Copy the static data. + *dst = *src; + + // Copy item name into dynamic memory. + if (src->flags & YP_FALLOC) { + dst->name = malloc(src->name[0] + 2); + if (dst->name == NULL) { + return KNOT_ENOMEM; + } + memcpy((void *)dst->name, src->name, src->name[0] + 2); + } + + // Item type specific preparation. + switch (src->type) { + case YP_TREF: + return set_ref_item(dst, schema); + case YP_TGRP: + return set_grp_item(dst, src, schema); + default: + return KNOT_EOK; + } +} + +static void unset_item( + yp_item_t *item) +{ + if (item->flags & YP_FALLOC) { + free((void *)item->name); + if (item->flags & YP_FALLOC) { + free((void *)item->var.g.sub_items); + } + } + if (item->sub_items != NULL) { + free(item->sub_items); + } + + memset(item, 0, sizeof(yp_item_t)); +} + +static int schema_copy( + yp_item_t *dst, + const yp_item_t *src, + const yp_item_t *schema) +{ + // Copy the schema. + for (int i = 0; src[i].name != NULL; i++) { + int ret = set_item(dst + i, src + i, schema); + if (ret != KNOT_EOK) { + return ret; + } + } + + return KNOT_EOK; +} + +_public_ +int yp_schema_copy( + yp_item_t **dst, + const yp_item_t *src) +{ + if (dst == NULL || src == NULL) { + return KNOT_EINVAL; + } + + // Allocate space for new schema (+ terminal NULL item). + size_t size = (schema_count(src) + 1) * sizeof(yp_item_t); + *dst = malloc(size); + if (*dst == NULL) { + return KNOT_ENOMEM; + } + memset(*dst, 0, size); + + // Copy the schema. + int ret = schema_copy(*dst, src, *dst); + if (ret != KNOT_EOK) { + yp_schema_free(*dst); + return ret; + } + + return KNOT_EOK; +} + +_public_ +int yp_schema_merge( + yp_item_t **dst, + const yp_item_t *src1, + const yp_item_t *src2) +{ + if (dst == NULL || src1 == NULL || src2 == NULL) { + return KNOT_EINVAL; + } + + size_t count1 = schema_count(src1); + size_t count2 = schema_count(src2); + + // Allocate space for new schema (+ terminal NULL item). + size_t size = (count1 + count2 + 1) * sizeof(yp_item_t); + *dst = malloc(size); + if (*dst == NULL) { + return KNOT_ENOMEM; + } + memset(*dst, 0, size); + + // Copy the first schema. + int ret = schema_copy(*dst, src1, *dst); + if (ret != KNOT_EOK) { + yp_schema_free(*dst); + return ret; + } + + // Copy the second schema. + ret = schema_copy(*dst + count1, src2, *dst); + if (ret != KNOT_EOK) { + yp_schema_free(*dst); + return ret; + } + + return KNOT_EOK; +} + +_public_ +void yp_schema_purge_dynamic( + yp_item_t *schema) +{ + if (schema == NULL) { + return; + } + + for (yp_item_t *item = schema; item->name != NULL; item++) { + if (item->flags & YP_FALLOC) { + unset_item(item); + } + } +} + +_public_ +void yp_schema_free( + yp_item_t *schema) +{ + if (schema == NULL) { + return; + } + + for (yp_item_t *item = schema; item->name != NULL; item++) { + unset_item(item); + } + free(schema); +} + +/*! Search the schema for an item with the given name. */ +static const yp_item_t* find_item( + const char *name, + size_t name_len, + const yp_item_t *schema) +{ + if (name == NULL || schema == NULL) { + return NULL; + } + + for (const yp_item_t *item = schema; item->name != NULL; item++) { + if (item->name[0] != name_len) { + continue; + } + if (memcmp(item->name + 1, name, name_len) == 0) { + return item; + } + } + + return NULL; +} + +_public_ +const yp_item_t* yp_schema_find( + const yp_name_t *name, + const yp_name_t *parent_name, + const yp_item_t *schema) +{ + if (name == NULL || schema == NULL) { + return NULL; + } + + if (parent_name == NULL) { + return find_item(name + 1, name[0], schema); + } else { + const yp_item_t *parent = find_item(parent_name + 1, + parent_name[0], schema); + if (parent == NULL) { + return NULL; + } + return find_item(name + 1, name[0], parent->sub_items); + } +} + +_public_ +yp_check_ctx_t* yp_schema_check_init( + yp_item_t **schema) +{ + if (schema == NULL) { + return NULL; + } + + yp_check_ctx_t *ctx = malloc(sizeof(yp_check_ctx_t)); + if (ctx == NULL) { + return NULL; + } + memset(ctx, 0, sizeof(yp_check_ctx_t)); + + ctx->schema = schema; + + return ctx; +} + +static void reset_ctx( + yp_check_ctx_t *ctx, + size_t index) +{ + assert(index < YP_MAX_NODE_DEPTH); + + yp_node_t *node = &ctx->nodes[index]; + + node->parent = (index > 0) ? &ctx->nodes[index - 1] : NULL; + node->item = NULL; + node->id_len = 0; + node->data_len = 0; + + ctx->current = index; +} + +static int check_item( + const char *key, + size_t key_len, + const char *data, + size_t data_len, + yp_check_ctx_t *ctx, + bool allow_key1_without_id) +{ + yp_node_t *node = &ctx->nodes[ctx->current]; + yp_node_t *parent = node->parent; + bool is_id = false; + + if (parent != NULL) { + // Check for invalid indentation. + if (parent->item == NULL) { + return KNOT_YP_EINVAL_INDENT; + } + + // Check if valid group parent. + if (parent->item->type != YP_TGRP) { + return KNOT_YP_EINVAL_ITEM; + } + + // Check if valid subitem. + node->item = find_item(key, key_len, parent->item->sub_items); + } else { + node->item = find_item(key, key_len, *ctx->schema); + } + if (node->item == NULL) { + return KNOT_YP_EINVAL_ITEM; + } + + // Check if the parent requires id specification. + if (parent != NULL && parent->item->var.g.id != NULL) { + // Check if id. + if (node->item == parent->item->var.g.id) { + is_id = true; + // Move current to the parent. + --(ctx->current); + // Check for missing id. + } else if (parent->id_len == 0 && !allow_key1_without_id) { + return KNOT_YP_ENOID; + } + } + + // Return if no data provided. + if (data == NULL) { + return KNOT_EOK; + } + + // Group cannot have data. + if (data_len != 0 && node->item->type == YP_TGRP) { + return KNOT_YP_ENOTSUP_DATA; + } + + // Convert item data to binary format. + const yp_item_t *item = (node->item->type != YP_TREF) ? + node->item : node->item->var.r.ref->var.g.id; + if (is_id) { + // Textual id must not be empty. + if (data_len == 0) { + return KNOT_YP_ENODATA; + } + + parent->id_len = sizeof(((yp_node_t *)NULL)->id); + int ret = yp_item_to_bin(item, data, data_len, parent->id, + &parent->id_len); + + // Binary id must not be empty. + if (ret == KNOT_EOK && parent->id_len == 0) { + return KNOT_YP_EINVAL_DATA; + } + + return ret; + } else { + node->data_len = sizeof(((yp_node_t *)NULL)->data); + int ret = yp_item_to_bin(item, data, data_len, node->data, + &node->data_len); + return ret; + } +} + +_public_ +int yp_schema_check_parser( + yp_check_ctx_t *ctx, + const yp_parser_t *parser) +{ + if (ctx == NULL || parser == NULL) { + return KNOT_EINVAL; + } + + int ret; + + switch (parser->event) { + case YP_EKEY0: + reset_ctx(ctx, 0); + ret = check_item(parser->key, parser->key_len, parser->data, + parser->data_len, ctx, false); + break; + case YP_EKEY1: + reset_ctx(ctx, 1); + ret = check_item(parser->key, parser->key_len, parser->data, + parser->data_len, ctx, false); + if (ret != KNOT_EOK) { + break; + } + + // Check for KEY1 event with id item. + if (ctx->current != 1) { + return KNOT_YP_ENOTSUP_ID; + } + + break; + case YP_EID: + reset_ctx(ctx, 1); + ret = check_item(parser->key, parser->key_len, parser->data, + parser->data_len, ctx, false); + if (ret != KNOT_EOK) { + break; + } + + // Check for ID event with nonid item. + if (ctx->current != 0) { + return KNOT_YP_EINVAL_ID; + } + + break; + default: + ret = KNOT_EPARSEFAIL; + break; + } + + return ret; +} + +_public_ +int yp_schema_check_str( + yp_check_ctx_t *ctx, + const char *key0, + const char *key1, + const char *id, + const char *data) +{ + if (ctx == NULL) { + return KNOT_EINVAL; + } + + size_t key0_len = (key0 != NULL) ? strlen(key0) : 0; + size_t key1_len = (key1 != NULL) ? strlen(key1) : 0; + size_t id_len = (id != NULL) ? strlen(id) : 0; + size_t data_len = (data != NULL) ? strlen(data) : 0; + + // Key0 must always be non-empty. + if (key0_len == 0) { + return KNOT_YP_EINVAL_ITEM; + } + + // Process key0. + reset_ctx(ctx, 0); + if (key1_len == 0) { + int ret = check_item(key0, key0_len, data, data_len, ctx, false); + if (ret != KNOT_EOK) { + return ret; + } + } else { + int ret = check_item(key0, key0_len, NULL, 0, ctx, false); + if (ret != KNOT_EOK) { + return ret; + } + } + + // Process id. + if (id_len != 0) { + if (ctx->nodes[0].item->type != YP_TGRP || + ctx->nodes[0].item->var.g.id == NULL) { + return KNOT_YP_ENOTSUP_ID; + } + const yp_name_t *name = ctx->nodes[0].item->var.g.id->name; + + reset_ctx(ctx, 1); + int ret = check_item(name + 1, name[0], id, id_len, ctx, true); + if (ret != KNOT_EOK) { + return ret; + } + + // Check for non-id item (should not happen). + assert(ctx->current == 0); + + // Check for group id with data. + if (key1_len == 0 && data != NULL) { + return KNOT_YP_ENOTSUP_DATA; + } + } + + // Process key1. + if (key1_len != 0) { + reset_ctx(ctx, 1); + int ret = check_item(key1, key1_len, data, data_len, ctx, true); + if (ret != KNOT_EOK) { + return ret; + } + + // Check for id in key1 with extra data. + if (ctx->current != 1 && id_len != 0 && data != NULL) { + return KNOT_YP_ENOTSUP_DATA; + } + } + + return KNOT_EOK; +} + +_public_ +void yp_schema_check_deinit( + yp_check_ctx_t* ctx) +{ + free(ctx); +} diff --git a/src/libknot/yparser/ypschema.h b/src/libknot/yparser/ypschema.h new file mode 100644 index 0000000..972a781 --- /dev/null +++ b/src/libknot/yparser/ypschema.h @@ -0,0 +1,347 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +/*! + * \file + * + * \brief Schema layer for Yparser. + * + * \addtogroup yparser + * @{ + */ + +#pragma once + +#include <stdint.h> +#include <stddef.h> + +#include "libknot/yparser/yparser.h" +#include "libknot/lookup.h" + +struct wire_ctx; + +/*! Maximal length of item name. */ +#define YP_MAX_ITEM_NAME_LEN 64 +/*! Maximal length of binary identifier name (maximal dname length). */ +#define YP_MAX_ID_LEN 255 +/*! Maximal length of binary data (rough limit). */ +#define YP_MAX_DATA_LEN 32768 +/*! Integer item nil definition. */ +#define YP_NIL INT64_MIN +/*! Maximal number of miscellaneous callbacks/pointers. */ +#define YP_MAX_MISC_COUNT 4 +/*! Maximal node stack depth. */ +#define YP_MAX_NODE_DEPTH 2 + +#define YP_TXT_BIN_PARAMS struct wire_ctx *in, struct wire_ctx *out, const uint8_t *stop +#define YP_BIN_TXT_PARAMS struct wire_ctx *in, struct wire_ctx *out + +/*! Helper macros for item variables definition. */ +#define YP_VNONE .var.i = { 0 } +#define YP_VINT .var.i +#define YP_VBOOL .var.b +#define YP_VOPT .var.o +#define YP_VSTR .var.s +#define YP_VADDR .var.a +#define YP_VNET .var.d +#define YP_VDNAME .var.d +#define YP_VHEX .var.d +#define YP_VB64 .var.d +#define YP_VDATA .var.d +#define YP_VREF .var.r +#define YP_VGRP .var.g + +/*! Schema item name is a char string with a leading byte (string length). */ +typedef char yp_name_t; + +/*! Schema item type. */ +typedef enum { + YP_TNONE = 0, /*!< Unspecified. */ + YP_TINT, /*!< Integer. */ + YP_TBOOL, /*!< Boolean. */ + YP_TOPT, /*!< Option from the list. */ + YP_TSTR, /*!< String. */ + YP_THEX, /*!< String or hexadecimal string if "0x" prefix. */ + YP_TADDR, /*!< Address (address[\@port] or UNIX socket path). */ + YP_TNET, /*!< Network address range (address[/mask] or address-address). */ + YP_TDNAME, /*!< Domain name. */ + YP_TB64, /*!< Base64 encoded string. */ + YP_TDATA, /*!< Customized data. */ + YP_TREF, /*!< Reference to another item. */ + YP_TGRP, /*!< Group of sub-items. */ +} yp_type_t; + +/*! Schema item flags. */ +typedef enum { + YP_FNONE = 0, /*!< Unspecified. */ + YP_FMULTI = 1 << 0, /*!< Multivalued item. */ + YP_FALLOC = 1 << 1, /*!< Allocated item. */ + YP_FUSR1 = 1 << 5, /*!< User-defined flag1. */ + YP_FUSR2 = 1 << 6, /*!< User-defined flag2. */ + YP_FUSR3 = 1 << 7, /*!< User-defined flag3. */ + YP_FUSR4 = 1 << 8, /*!< User-defined flag4. */ + YP_FUSR5 = 1 << 9, /*!< User-defined flag5. */ + YP_FUSR6 = 1 << 10, /*!< User-defined flag6. */ + YP_FUSR7 = 1 << 11, /*!< User-defined flag7. */ + YP_FUSR8 = 1 << 12, /*!< User-defined flag8. */ + YP_FUSR9 = 1 << 13, /*!< User-defined flag9. */ + YP_FUSR10 = 1 << 14, /*!< User-defined flag10. */ + YP_FUSR11 = 1 << 15, /*!< User-defined flag11. */ + YP_FUSR12 = 1 << 16, /*!< User-defined flag12. */ + YP_FUSR13 = 1 << 17, /*!< User-defined flag13. */ + YP_FUSR14 = 1 << 18, /*!< User-defined flag14. */ + YP_FUSR15 = 1 << 19, /*!< User-defined flag15. */ + YP_FUSR16 = 1 << 20, /*!< User-defined flag16. */ +} yp_flag_t; + +/*! Schema item style. */ +typedef enum { + YP_SNONE = 0, /*!< Unspecified. */ + YP_SSIZE = 1 << 0, /*!< Size unit (B, K, M, G) (in, out). */ + YP_STIME = 1 << 1, /*!< Time unit (s, m, h, d) (in, out). */ + YP_SUNIT = YP_SSIZE | YP_STIME, /*!< Unit (in, out). */ + YP_SNOQUOTE = 1 << 2 /*!< Unquoted value (out). */ +} yp_style_t; + +typedef struct yp_item yp_item_t; + +/*! Schema item variables (type dependent). */ +typedef union { + /*! Integer variables. */ + struct { + /*! Minimal value. */ + int64_t min; + /*! Maximal value. */ + int64_t max; + /*! Default value. */ + int64_t dflt; + /*! Possible unit type. */ + yp_style_t unit; + } i; + /*! Boolen variables. */ + struct { + /*! Default value. */ + bool dflt; + } b; + /*! Option variables. */ + struct { + /*! List of options (maximal value is 255). */ + struct knot_lookup const *opts; + /*! Default value. */ + unsigned dflt; + } o; + /*! String variables. */ + struct { + /*! Default value. */ + char const *dflt; + } s; + /*! Address variables. */ + struct { + /*! Default port. */ + uint16_t dflt_port; + /*! Default socket. */ + char const *dflt_socket; + } a; + /*! Customized data variables. */ + struct { + /*! Length of default data. */ + size_t dflt_len; + /*! Default data. */ + uint8_t const *dflt; + /*! Text to binary transformation function. */ + int (*to_bin)(YP_TXT_BIN_PARAMS); + /*! Binary to text transformatio function. */ + int (*to_txt)(YP_BIN_TXT_PARAMS); + } d; + /*! Reference variables. */ + struct { + /*! Referenced group name. */ + yp_name_t const *ref_name; + /*! Referenced item (dynamic value). */ + yp_item_t const *ref; + } r; + /*! Group variables. */ + struct { + /*! List of sub-items. */ + yp_item_t const *sub_items; + /*! ID item of sub-items (dynamic value). */ + yp_item_t const *id; + } g; +} yp_var_t; + +/*! Schema item specification. */ +struct yp_item { + /*! Item name. */ + const yp_name_t *name; + /*! Item type. */ + yp_type_t type; + /*! Item parameters. */ + yp_var_t var; + /*! Item flags. */ + yp_flag_t flags; + /*! Arbitrary data/callbacks. */ + const void *misc[YP_MAX_MISC_COUNT]; + /*! Parent item. */ + yp_item_t *parent; + /*! Item group subitems (name=NULL terminated array). */ + yp_item_t *sub_items; +}; + +typedef struct yp_node yp_node_t; +struct yp_node { + /*! Parent node. */ + yp_node_t *parent; + /*! Node item descriptor. */ + const yp_item_t *item; + /*! Current binary id length. */ + size_t id_len; + /*! Current binary id. */ + uint8_t id[YP_MAX_ID_LEN]; + /*! Current item data length. */ + size_t data_len; + /*! Current item data. */ + uint8_t data[YP_MAX_DATA_LEN]; +}; + +/*! Context parameters for check operations. */ +typedef struct { + /*! Used schema. */ + yp_item_t **schema; + /*! Index of the current node. */ + size_t current; + /*! Node stack. */ + yp_node_t nodes[YP_MAX_NODE_DEPTH]; +} yp_check_ctx_t; + +/*! + * Copies the schema and reinitializes dynamic parameters. + * + * \param[out] dst New copy of the schema. + * \param[in] src Source schema. + * + * \return Error code, KNOT_EOK if success. + */ +int yp_schema_copy( + yp_item_t **dst, + const yp_item_t *src +); + +/*! + * Merges two schemas. + * + * \param[out] dst Merged schema. + * \param[in] src1 Source schema1. + * \param[in] src2 Source schema2. + * + * \return Error code, KNOT_EOK if success. + */ +int yp_schema_merge( + yp_item_t **dst, + const yp_item_t *src1, + const yp_item_t *src2 +); + +/*! + * Purges dynamic items from the schema. + * + * \param[in] schema Schema to purge. + */ +void yp_schema_purge_dynamic( + yp_item_t *schema +); + +/*! + * Deallocates the schema. + * + * \param[in] schema A schema returned by #yp_schema_copy(). + */ +void yp_schema_free( + yp_item_t *schema +); + +/*! + * Tries to find given parent_name/name in the schema. + * + * \param[in] name Name of the item. + * \param[in] parent_name Name of the parent item (NULL if no parent). + * \param[in] schema Schema. + * + * \return Item, NULL if not found or error. + */ +const yp_item_t* yp_schema_find( + const yp_name_t *name, + const yp_name_t *parent_name, + const yp_item_t *schema +); + +/*! + * Prepares a context for item check against the schema. + * + * \param[in] schema Schema. + * + * \return Context, NULL if error. + */ +yp_check_ctx_t* yp_schema_check_init( + yp_item_t **schema +); + +/*! + * Checks the current parser output against the schema. + * + * If the item is correct, context also contains binary value of the item. + * + * \param[in,out] ctx Check context. + * \param[in] parser Parser context. + * + * \return Error code, KNOT_EOK if success. + */ +int yp_schema_check_parser( + yp_check_ctx_t *ctx, + const yp_parser_t *parser +); + +/*! + * Checks the string data against the schema. + * + * Description: key0[id].key1 data + * + * If the item is correct, context also contains binary value of the item. + * + * \param[in,out] ctx Check context. + * \param[in] key0 Key0 item name. + * \param[in] key1 Key1 item name. + * \param[in] id Item identifier. + * \param[in] data Item data (NULL means no data provided). + * + * \return Error code, KNOT_EOK if success. + */ +int yp_schema_check_str( + yp_check_ctx_t *ctx, + const char *key0, + const char *key1, + const char *id, + const char *data +); + +/*! + * Deallocates the context. + * + * \param[in,out] ctx Check context. + */ +void yp_schema_check_deinit( + yp_check_ctx_t *ctx +); + +/*! @} */ diff --git a/src/libknot/yparser/yptrafo.c b/src/libknot/yparser/yptrafo.c new file mode 100644 index 0000000..581aa32 --- /dev/null +++ b/src/libknot/yparser/yptrafo.c @@ -0,0 +1,1094 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <arpa/inet.h> +#include <inttypes.h> +#include <limits.h> +#include <stdlib.h> +#include <stdio.h> + +#include "libknot/yparser/yptrafo.h" +#include "libknot/attribute.h" +#include "libknot/consts.h" +#include "libknot/dname.h" +#include "contrib/base64.h" +#include "contrib/ctype.h" +#include "contrib/sockaddr.h" +#include "contrib/wire_ctx.h" + +enum { + UNIT_BYTE = 'B', + UNIT_KILO = 'K', + UNIT_MEGA = 'M', + UNIT_GIGA = 'G', + UNIT_SEC = 's', + UNIT_MIN = 'm', + UNIT_HOUR = 'h', + UNIT_DAY = 'd' +}; + +enum { + MULTI_BYTE = 1, + MULTI_KILO = 1024, + MULTI_MEGA = 1024 * 1024, + MULTI_GIGA = 1024 * 1024 * 1024, + MULTI_SEC = 1, + MULTI_MIN = 60, + MULTI_HOUR = 3600, + MULTI_DAY = 24 * 3600 +}; + +static wire_ctx_t copy_in( + wire_ctx_t *in, + size_t in_len, + char *buf, + size_t buf_len) +{ + wire_ctx_t ctx = wire_ctx_init((uint8_t *)buf, buf_len); + wire_ctx_write(&ctx, in->position, in_len); + wire_ctx_skip(in, in_len); + // Write the terminator. + wire_ctx_write_u8(&ctx, '\0'); + wire_ctx_skip(&ctx, -1); + return ctx; +} + +_public_ +int yp_str_to_bin( + YP_TXT_BIN_PARAMS) +{ + YP_CHECK_PARAMS_BIN; + + wire_ctx_write(out, in->position, YP_LEN); + wire_ctx_skip(in, YP_LEN); + // Write string terminator. + wire_ctx_write_u8(out, '\0'); + + YP_CHECK_RET; +} + +_public_ +int yp_str_to_txt( + YP_BIN_TXT_PARAMS) +{ + YP_CHECK_PARAMS_TXT; + + size_t len = strlen((char *)in->position) + 1; + + wire_ctx_write(out, in->position, len); + wire_ctx_skip(in, len); + // Set the terminator as a current position. + wire_ctx_skip(out, -1); + + YP_CHECK_RET; +} + +_public_ +int yp_bool_to_bin( + YP_TXT_BIN_PARAMS) +{ + YP_CHECK_PARAMS_BIN; + + if (strncasecmp((char *)in->position, "on", YP_LEN) == 0 || + strncasecmp((char *)in->position, "true", YP_LEN) == 0) { + wire_ctx_write_u8(out, 1); + } else if (strncasecmp((char *)in->position, "off", YP_LEN) == 0 || + strncasecmp((char *)in->position, "false", YP_LEN) == 0) { + wire_ctx_write_u8(out, 0); + } else { + return KNOT_EINVAL; + } + + wire_ctx_skip(in, YP_LEN); + + YP_CHECK_RET; +} + +_public_ +int yp_bool_to_txt( + YP_BIN_TXT_PARAMS) +{ + YP_CHECK_PARAMS_TXT; + + const char *value; + + switch (wire_ctx_read_u8(in)) { + case 0: + value = "off"; + break; + case 1: + value = "on"; + break; + default: + return KNOT_EINVAL; + } + + int ret = snprintf((char *)out->position, wire_ctx_available(out), "%s", + value); + if (ret <= 0 || ret >= wire_ctx_available(out)) { + return KNOT_ESPACE; + } + wire_ctx_skip(out, ret); + + YP_CHECK_RET; +} + +static int remove_unit( + int64_t *number, + char unit, + yp_style_t style) +{ + int64_t multiplier = 1; + + // Get the multiplier for the unit. + if (style & YP_SSIZE) { + switch (unit) { + case UNIT_BYTE: + multiplier = MULTI_BYTE; + break; + case UNIT_KILO: + multiplier = MULTI_KILO; + break; + case UNIT_MEGA: + multiplier = MULTI_MEGA; + break; + case UNIT_GIGA: + multiplier = MULTI_GIGA; + break; + default: + return KNOT_EINVAL; + } + } else if (style & YP_STIME) { + switch (unit) { + case UNIT_SEC: + multiplier = MULTI_SEC; + break; + case UNIT_MIN: + multiplier = MULTI_MIN; + break; + case UNIT_HOUR: + multiplier = MULTI_HOUR; + break; + case UNIT_DAY: + multiplier = MULTI_DAY; + break; + default: + return KNOT_EINVAL; + } + } else { + return KNOT_EINVAL; + } + + // Check for possible number overflow. + if (INT64_MAX / multiplier < (*number >= 0 ? *number : -*number)) { + return KNOT_ERANGE; + } + + *number *= multiplier; + + return KNOT_EOK; +} + +_public_ +int yp_int_to_bin( + YP_TXT_BIN_PARAMS, + int64_t min, + int64_t max, + yp_style_t style) +{ + YP_CHECK_PARAMS_BIN; + + // Copy input string to the buffer to limit strtoll overread. + char buf[32]; + wire_ctx_t buf_ctx = copy_in(in, YP_LEN, buf, sizeof(buf)); + if (buf_ctx.error != KNOT_EOK) { + return buf_ctx.error; + } + + // Parse the number. + char *end; + errno = 0; + int64_t number = strtoll(buf, &end, 10); + + // Check for number overflow. + if (errno == ERANGE && (number == LLONG_MAX || number == LLONG_MIN)) { + return KNOT_ERANGE; + } + // Check if the whole string is invalid. + if ((errno != 0 && number == 0) || end == buf) { + return KNOT_EINVAL; + } + // Check the rest of the string for a unit. + if (*end != '\0') { + // Check just for one-char rest. + if (*(end + 1) != '\0') { + return KNOT_EINVAL; + } + + // Try to apply a unit on the number. + int ret = remove_unit(&number, *end, style); + if (ret != KNOT_EOK) { + return ret; + } + } + + // Check for the result number overflow. + if (number < min || number > max) { + return KNOT_ERANGE; + } + + // Write the result. + wire_ctx_write_u64(out, number); + + YP_CHECK_RET; +} + +static void add_unit( + int64_t *number, + char *unit, + yp_style_t style) +{ + int64_t multiplier = 1; + char basic_unit = '\0'; + char new_unit = '\0'; + + // Get the multiplier for the unit. + if (style & YP_SSIZE) { + basic_unit = UNIT_BYTE; + + if (*number < MULTI_KILO) { + multiplier = MULTI_BYTE; + new_unit = UNIT_BYTE; + } else if (*number < MULTI_MEGA) { + multiplier = MULTI_KILO; + new_unit = UNIT_KILO; + } else if (*number < MULTI_GIGA) { + multiplier = MULTI_MEGA; + new_unit = UNIT_MEGA; + } else { + multiplier = MULTI_GIGA; + new_unit = UNIT_GIGA; + } + } else if (style & YP_STIME) { + basic_unit = UNIT_SEC; + + if (*number < MULTI_MIN) { + multiplier = MULTI_SEC; + new_unit = UNIT_SEC; + } else if (*number < MULTI_HOUR) { + multiplier = MULTI_MIN; + new_unit = UNIT_MIN; + } else if (*number < MULTI_DAY) { + multiplier = MULTI_HOUR; + new_unit = UNIT_HOUR; + } else { + multiplier = MULTI_DAY; + new_unit = UNIT_DAY; + } + } + + // Check for unit application without any remainder. + if ((*number % multiplier) == 0) { + *number /= multiplier; + *unit = new_unit; + } else { + *unit = basic_unit; + } +} + +_public_ +int yp_int_to_txt( + YP_BIN_TXT_PARAMS, + yp_style_t style) +{ + YP_CHECK_PARAMS_TXT; + + char unit[2] = { '\0' }; + int64_t number = wire_ctx_read_u64(in); + add_unit(&number, unit, style); + + int ret = snprintf((char *)out->position, wire_ctx_available(out), + "%"PRId64"%s", number, unit); + if (ret <= 0 || ret >= wire_ctx_available(out)) { + return KNOT_ESPACE; + } + wire_ctx_skip(out, ret); + + YP_CHECK_RET; +} + +static uint8_t sock_type_guess( + const uint8_t *str, + size_t len) +{ + size_t dots = 0; + size_t semicolons = 0; + size_t digits = 0; + + // Analyze the string. + for (size_t i = 0; i < len; i++) { + if (str[i] == '.') dots++; + else if (str[i] == ':') semicolons++; + else if (is_digit(str[i])) digits++; + } + + // Guess socket type. + if (semicolons >= 1) { + return 6; + } else if (semicolons == 0 && dots == 3 && digits >= 3) { + return 4; + } else { + return 0; + } +} + +_public_ +int yp_addr_noport_to_bin( + YP_TXT_BIN_PARAMS, + bool allow_unix) +{ + YP_CHECK_PARAMS_BIN; + + struct in_addr addr4; + struct in6_addr addr6; + + uint8_t type = sock_type_guess(in->position, YP_LEN); + + // Copy address to the buffer to limit inet_pton overread. + char buf[INET6_ADDRSTRLEN]; + if (type == 4 || type == 6) { + wire_ctx_t buf_ctx = copy_in(in, YP_LEN, buf, sizeof(buf)); + if (buf_ctx.error != KNOT_EOK) { + return buf_ctx.error; + } + } + + // Write address type. + wire_ctx_write_u8(out, type); + + // Write address as such. + if (type == 4 && inet_pton(AF_INET, buf, &addr4) == 1) { + wire_ctx_write(out, (uint8_t *)&(addr4.s_addr), + sizeof(addr4.s_addr)); + } else if (type == 6 && inet_pton(AF_INET6, buf, &addr6) == 1) { + wire_ctx_write(out, (uint8_t *)&(addr6.s6_addr), + sizeof(addr6.s6_addr)); + } else if (type == 0 && allow_unix) { + int ret = yp_str_to_bin(in, out, stop); + if (ret != KNOT_EOK) { + return ret; + } + } else { + return KNOT_EINVAL; + } + + YP_CHECK_RET; +} + +_public_ +int yp_addr_noport_to_txt( + YP_BIN_TXT_PARAMS) +{ + YP_CHECK_PARAMS_TXT; + + struct in_addr addr4; + struct in6_addr addr6; + + int ret; + + switch (wire_ctx_read_u8(in)) { + case 0: + ret = yp_str_to_txt(in, out); + if (ret != KNOT_EOK) { + return ret; + } + break; + case 4: + wire_ctx_read(in, &(addr4.s_addr), sizeof(addr4.s_addr)); + if (inet_ntop(AF_INET, &addr4, (char *)out->position, + wire_ctx_available(out)) == NULL) { + return KNOT_EINVAL; + } + wire_ctx_skip(out, strlen((char *)out->position)); + break; + case 6: + wire_ctx_read(in, &(addr6.s6_addr), sizeof(addr6.s6_addr)); + if (inet_ntop(AF_INET6, &addr6, (char *)out->position, + wire_ctx_available(out)) == NULL) { + return KNOT_EINVAL; + } + wire_ctx_skip(out, strlen((char *)out->position)); + break; + default: + return KNOT_EINVAL; + } + + YP_CHECK_RET; +} + +_public_ +int yp_addr_to_bin( + YP_TXT_BIN_PARAMS) +{ + YP_CHECK_PARAMS_BIN; + + // Check for address@port separator. + const uint8_t *pos = (uint8_t *)strrchr((char *)in->position, '@'); + // Ignore out-of-bounds result. + if (pos >= stop) { + pos = NULL; + } + + // Store address type position. + uint8_t *type = out->position; + + // Write address (UNIX socket can't have a port). + int ret = yp_addr_noport_to_bin(in, out, pos, pos == NULL); + if (ret != KNOT_EOK) { + return ret; + } + + if (pos != NULL) { + // Skip the separator. + wire_ctx_skip(in, sizeof(uint8_t)); + + // Write port. + ret = yp_int_to_bin(in, out, stop, 0, UINT16_MAX, YP_SNONE); + if (ret != KNOT_EOK) { + return ret; + } + } else if (*type == 4 || *type == 6) { + wire_ctx_write_u64(out, (uint64_t)-1); + } + + YP_CHECK_RET; +} + +_public_ +int yp_addr_to_txt( + YP_BIN_TXT_PARAMS) +{ + YP_CHECK_PARAMS_TXT; + + // Store address type position. + uint8_t *type = in->position; + + // Write address. + int ret = yp_addr_noport_to_txt(in, out); + if (ret != KNOT_EOK) { + return ret; + } + + // Write port. + if (*type == 4 || *type == 6) { + int64_t port = wire_ctx_read_u64(in); + + if (port >= 0) { + // Write separator. + wire_ctx_write_u8(out, '@'); + + // Write port. + wire_ctx_skip(in, -sizeof(uint64_t)); + int ret = yp_int_to_txt(in, out, YP_SNONE); + if (ret != KNOT_EOK) { + return ret; + } + } + } + + YP_CHECK_RET; +} + +_public_ +int yp_addr_range_to_bin( + YP_TXT_BIN_PARAMS) +{ + YP_CHECK_PARAMS_BIN; + + // Format: 0 - single address, 1 - address prefix, 2 - address range. + uint8_t format = 0; + + // Check for the "addr/mask" format. + const uint8_t *pos = (uint8_t *)strchr((char *)in->position, '/'); + if (pos >= stop) { + pos = NULL; + } + + if (pos != NULL) { + format = 1; + } else { + // Check for the "addr1-addr2" format. + pos = (uint8_t *)strchr((char *)in->position, '-'); + if (pos >= stop) { + pos = NULL; + } + if (pos != NULL) { + format = 2; + } + } + + // Store address1 type position. + uint8_t *type1 = out->position; + + // Write the first address. + int ret = yp_addr_noport_to_bin(in, out, pos, false); + if (ret != KNOT_EOK) { + return ret; + } + + wire_ctx_write_u8(out, format); + + switch (format) { + case 1: + // Skip the separator. + wire_ctx_skip(in, sizeof(uint8_t)); + + // Write the prefix length. + ret = yp_int_to_bin(in, out, stop, 0, (*type1 == 4) ? 32 : 128, + YP_SNONE); + if (ret != KNOT_EOK) { + return ret; + } + break; + case 2: + // Skip the separator. + wire_ctx_skip(in, sizeof(uint8_t)); + + // Store address2 type position. + uint8_t *type2 = out->position; + + // Write the second address. + ret = yp_addr_noport_to_bin(in, out, stop, false); + if (ret != KNOT_EOK) { + return ret; + } + + // Check for address mismatch. + if (*type1 != *type2) { + return KNOT_EINVAL; + } + break; + default: + break; + } + + YP_CHECK_RET; +} + +_public_ +int yp_addr_range_to_txt( + YP_BIN_TXT_PARAMS) +{ + YP_CHECK_PARAMS_TXT; + + // Write the first address. + int ret = yp_addr_noport_to_txt(in, out); + if (ret != KNOT_EOK) { + return ret; + } + + uint8_t format = wire_ctx_read_u8(in); + + switch (format) { + case 1: + // Write the separator. + wire_ctx_write_u8(out, '/'); + + // Write the prefix length. + ret = yp_int_to_txt(in, out, YP_SNONE); + if (ret != KNOT_EOK) { + return ret; + } + break; + case 2: + // Write the separator. + wire_ctx_write_u8(out, '-'); + + // Write the second address. + ret = yp_addr_noport_to_txt(in, out); + if (ret != KNOT_EOK) { + return ret; + } + break; + default: + break; + } + + YP_CHECK_RET; +} + +_public_ +int yp_option_to_bin( + YP_TXT_BIN_PARAMS, + const knot_lookup_t *opts) +{ + YP_CHECK_PARAMS_BIN; + + while (opts->name != NULL) { + if (YP_LEN == strlen(opts->name) && + strncasecmp((char *)in->position, opts->name, YP_LEN) == 0) { + wire_ctx_write_u8(out, opts->id); + wire_ctx_skip(in, YP_LEN); + YP_CHECK_RET; + } + opts++; + } + + return KNOT_EINVAL; +} + +_public_ +int yp_option_to_txt( + YP_BIN_TXT_PARAMS, + const knot_lookup_t *opts) +{ + uint8_t id = wire_ctx_read_u8(in); + + while (opts->name != NULL) { + if (id == opts->id) { + int ret = snprintf((char *)out->position, + wire_ctx_available(out), "%s", + opts->name); + if (ret <= 0 || ret >= wire_ctx_available(out)) { + return KNOT_ESPACE; + } + wire_ctx_skip(out, ret); + YP_CHECK_RET; + } + opts++; + } + + return KNOT_EINVAL; +} + +_public_ +int yp_dname_to_bin( + YP_TXT_BIN_PARAMS) +{ + YP_CHECK_PARAMS_BIN; + + // Copy dname string to the buffer to limit dname_from_str overread. + char buf[KNOT_DNAME_TXT_MAXLEN + 1]; + wire_ctx_t buf_ctx = copy_in(in, YP_LEN, buf, sizeof(buf)); + if (buf_ctx.error != KNOT_EOK) { + return buf_ctx.error; + } + + // Convert the dname. + knot_dname_t *dname = knot_dname_from_str(out->position, buf, + wire_ctx_available(out)); + if (dname == NULL) { + return KNOT_EINVAL; + } + + // Check the result and count the length. + int ret = knot_dname_wire_check(out->position, + out->position + wire_ctx_available(out), + NULL); + if (ret <= 0) { + return KNOT_EINVAL; + } + + // Convert the result to lower case. + knot_dname_to_lower(out->position); + + wire_ctx_skip(out, ret); + + YP_CHECK_RET; +} + +_public_ +int yp_dname_to_txt( + YP_BIN_TXT_PARAMS) +{ + YP_CHECK_PARAMS_TXT; + + char *name = knot_dname_to_str((char *)out->position, in->position, + wire_ctx_available(out)); + if (name == NULL) { + return KNOT_EINVAL; + } + + wire_ctx_skip(out, strlen((char *)out->position)); + + YP_CHECK_RET; +} + +static int hex_to_num(char hex) { + if (hex >= '0' && hex <= '9') return hex - '0'; + if (hex >= 'a' && hex <= 'f') return hex - 'a' + 10; + if (hex >= 'A' && hex <= 'F') return hex - 'A' + 10; + return -1; +} + +_public_ +int yp_hex_to_bin( + YP_TXT_BIN_PARAMS) +{ + YP_CHECK_PARAMS_BIN; + + // Check for hex notation (leading "0x"). + if (wire_ctx_available(in) >= 2 && + in->position[0] == '0' && in->position[1] == 'x') { + wire_ctx_skip(in, 2); + + if (YP_LEN % 2 != 0) { + return KNOT_EINVAL; + } + + // Write data length. + wire_ctx_write_u16(out, YP_LEN / 2); + + // Decode hex string. + while (YP_LEN > 0) { + uint8_t buf[2] = { 0 }; + wire_ctx_read(in, buf, sizeof(buf)); + + if (!is_xdigit(buf[0]) || + !is_xdigit(buf[1])) { + return KNOT_EINVAL; + } + + wire_ctx_write_u8(out, 16 * hex_to_num(buf[0]) + + hex_to_num(buf[1])); + } + } else { + // Write data length. + wire_ctx_write_u16(out, YP_LEN); + + // Write textual string (without terminator). + wire_ctx_write(out, in->position, YP_LEN); + wire_ctx_skip(in, YP_LEN); + } + + YP_CHECK_RET; +} + +_public_ +int yp_hex_to_txt( + YP_BIN_TXT_PARAMS) +{ + YP_CHECK_PARAMS_TXT; + + size_t len = wire_ctx_read_u16(in); + + bool printable = true; + + // Check for printable string. + for (size_t i = 0; i < len; i++) { + if (!is_print(in->position[i])) { + printable = false; + break; + } + } + + if (printable) { + wire_ctx_write(out, in->position, len); + wire_ctx_skip(in, len); + } else { + const char *prefix = "0x"; + const char *hex = "0123456789ABCDEF"; + + // Write hex prefix. + wire_ctx_write(out, (uint8_t *)prefix, strlen(prefix)); + + // Encode data to hex. + for (size_t i = 0; i < len; i++) { + uint8_t bin = wire_ctx_read_u8(in); + wire_ctx_write_u8(out, hex[bin / 16]); + wire_ctx_write_u8(out, hex[bin % 16]); + } + } + + // Write the terminator. + wire_ctx_write_u8(out, '\0'); + wire_ctx_skip(out, -1); + + YP_CHECK_RET; +} + +_public_ +int yp_base64_to_bin( + YP_TXT_BIN_PARAMS) +{ + YP_CHECK_PARAMS_BIN; + + // Reserve some space for data length. + wire_ctx_skip(out, sizeof(uint16_t)); + + int ret = base64_decode(in->position, YP_LEN, out->position, + wire_ctx_available(out)); + if (ret < 0) { + return ret; + } + wire_ctx_skip(in, YP_LEN); + + // Write the data length. + wire_ctx_skip(out, -sizeof(uint16_t)); + wire_ctx_write_u16(out, ret); + wire_ctx_skip(out, ret); + + YP_CHECK_RET; +} + +_public_ +int yp_base64_to_txt( + YP_BIN_TXT_PARAMS) +{ + YP_CHECK_PARAMS_TXT; + + // Read the data length. + uint16_t len = wire_ctx_read_u16(in); + + int ret = base64_encode(in->position, len, out->position, + wire_ctx_available(out)); + if (ret < 0) { + return ret; + } + wire_ctx_skip(out, ret); + + // Write the terminator. + wire_ctx_write_u8(out, '\0'); + wire_ctx_skip(out, -1); + + YP_CHECK_RET; +} + +_public_ +int yp_item_to_bin( + const yp_item_t *item, + const char *txt, + size_t txt_len, + uint8_t *bin, + size_t *bin_len) +{ + if (item == NULL || txt == NULL || bin == NULL || bin_len == NULL) { + return KNOT_EINVAL; + } + + wire_ctx_t in = wire_ctx_init_const((const uint8_t *)txt, txt_len); + wire_ctx_t out = wire_ctx_init(bin, *bin_len); + + int ret; + size_t ref_len; + + switch (item->type) { + case YP_TINT: + ret = yp_int_to_bin(&in, &out, NULL, item->var.i.min, + item->var.i.max, item->var.i.unit); + break; + case YP_TBOOL: + ret = yp_bool_to_bin(&in, &out, NULL); + break; + case YP_TOPT: + ret = yp_option_to_bin(&in, &out, NULL, item->var.o.opts); + break; + case YP_TSTR: + ret = yp_str_to_bin(&in, &out, NULL); + break; + case YP_TADDR: + ret = yp_addr_to_bin(&in, &out, NULL); + break; + case YP_TNET: + ret = yp_addr_range_to_bin(&in, &out, NULL); + break; + case YP_TDNAME: + ret = yp_dname_to_bin(&in, &out, NULL); + break; + case YP_THEX: + ret = yp_hex_to_bin(&in, &out, NULL); + break; + case YP_TB64: + ret = yp_base64_to_bin(&in, &out, NULL); + break; + case YP_TDATA: + ret = item->var.d.to_bin(&in, &out, NULL); + break; + case YP_TREF: + ref_len = wire_ctx_available(&out); + ret = yp_item_to_bin(item->var.r.ref->var.g.id, + (char *)in.position, wire_ctx_available(&in), + out.position, &ref_len); + wire_ctx_skip(&out, ref_len); + break; + default: + ret = KNOT_EOK; + } + + if (ret != KNOT_EOK) { + return ret; + } else if (in.error != KNOT_EOK) { + return in.error; + } else if (out.error != KNOT_EOK) { + return out.error; + } + + *bin_len = wire_ctx_offset(&out); + + return KNOT_EOK; +} + +_public_ +int yp_item_to_txt( + const yp_item_t *item, + const uint8_t *bin, + size_t bin_len, + char *txt, + size_t *txt_len, + yp_style_t style) +{ + if (item == NULL || bin == NULL || txt == NULL || txt_len == NULL) { + return KNOT_EINVAL; + } + + wire_ctx_t in = wire_ctx_init_const(bin, bin_len); + wire_ctx_t out = wire_ctx_init((uint8_t *)txt, *txt_len); + + // Write leading quote. + if (!(style & YP_SNOQUOTE)) { + wire_ctx_write_u8(&out, '"'); + } + + int ret; + size_t ref_len; + + switch (item->type) { + case YP_TINT: + ret = yp_int_to_txt(&in, &out, item->var.i.unit & style); + break; + case YP_TBOOL: + ret = yp_bool_to_txt(&in, &out); + break; + case YP_TOPT: + ret = yp_option_to_txt(&in, &out, item->var.o.opts); + break; + case YP_TSTR: + ret = yp_str_to_txt(&in, &out); + break; + case YP_TADDR: + ret = yp_addr_to_txt(&in, &out); + break; + case YP_TNET: + ret = yp_addr_range_to_txt(&in, &out); + break; + case YP_TDNAME: + ret = yp_dname_to_txt(&in, &out); + break; + case YP_THEX: + ret = yp_hex_to_txt(&in, &out); + break; + case YP_TB64: + ret = yp_base64_to_txt(&in, &out); + break; + case YP_TDATA: + ret = item->var.d.to_txt(&in, &out); + break; + case YP_TREF: + ref_len = wire_ctx_available(&out); + ret = yp_item_to_txt(item->var.r.ref->var.g.id, + in.position, wire_ctx_available(&in), + (char *)out.position, + &ref_len, style | YP_SNOQUOTE); + wire_ctx_skip(&out, ref_len); + break; + default: + ret = KNOT_EOK; + } + + // Write trailing quote. + if (!(style & YP_SNOQUOTE)) { + wire_ctx_write_u8(&out, '"'); + } + + // Write string terminator. + wire_ctx_write_u8(&out, '\0'); + wire_ctx_skip(&out, -1); + + if (ret != KNOT_EOK) { + return ret; + } else if (in.error != KNOT_EOK) { + return in.error; + } else if (out.error != KNOT_EOK) { + return out.error; + } + + *txt_len = wire_ctx_offset(&out); + + return KNOT_EOK; +} + +_public_ +struct sockaddr_storage yp_addr_noport( + const uint8_t *data) +{ + struct sockaddr_storage ss = { AF_UNSPEC }; + + // Read address type. + uint8_t type = *data; + data += sizeof(type); + + // Set address. + switch (type) { + case 0: + sockaddr_set(&ss, AF_UNIX, (char *)data, 0); + break; + case 4: + sockaddr_set_raw(&ss, AF_INET, data, + sizeof(((struct in_addr *)NULL)->s_addr)); + break; + case 6: + sockaddr_set_raw(&ss, AF_INET6, data, + sizeof(((struct in6_addr *)NULL)->s6_addr)); + break; + } + + return ss; +} + +_public_ +struct sockaddr_storage yp_addr( + const uint8_t *data, + bool *no_port) +{ + struct sockaddr_storage ss = yp_addr_noport(data); + + size_t addr_len; + + // Get binary address length. + switch (ss.ss_family) { + case AF_INET: + addr_len = sizeof(((struct in_addr *)NULL)->s_addr); + break; + case AF_INET6: + addr_len = sizeof(((struct in6_addr *)NULL)->s6_addr); + break; + default: + addr_len = 0; + *no_port = true; + } + + if (addr_len > 0) { + int64_t port = knot_wire_read_u64(data + sizeof(uint8_t) + addr_len); + if (port >= 0) { + sockaddr_port_set((struct sockaddr *)&ss, port); + *no_port = false; + } else { + *no_port = true; + } + } + + return ss; +} diff --git a/src/libknot/yparser/yptrafo.h b/src/libknot/yparser/yptrafo.h new file mode 100644 index 0000000..59fd4c8 --- /dev/null +++ b/src/libknot/yparser/yptrafo.h @@ -0,0 +1,310 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +/*! + * \file + * + * \brief Value transformations for Yparser. + * + * \addtogroup yparser + * @{ + */ + +#pragma once + +#include "libknot/yparser/ypschema.h" +#include "libknot/lookup.h" +#include "libknot/wire.h" + +/*! + * Transforms textual item value to binary form. + * + * \param[in] item Schema item to transform. + * \param[in] txt Value to transform. + * \param[in] txt_len Value length. + * \param[out] bin Output buffer. + * \param[in, out] bin_len Output buffer length, output length. + * + * \return Error code, KNOT_EOK if success. + */ +int yp_item_to_bin( + const yp_item_t *item, + const char *txt, + size_t txt_len, + uint8_t *bin, + size_t *bin_len +); + +/*! + * Transforms binary item value to textual form. + * + * \param[in] item Schema item to transform. + * \param[in] bin Value to transform. + * \param[in] bin_len Value length. + * \param[out] txt Output buffer. + * \param[in, out] txt_len Output buffer length, output length. + * \param[in] style Value style. + * + * \return Error code, KNOT_EOK if success. + */ +int yp_item_to_txt( + const yp_item_t *item, + const uint8_t *bin, + size_t bin_len, + char *txt, + size_t *txt_len, + yp_style_t style +); + +/*! + * Converts binary value to string pointer. + * + * \param[in] data Binary value to transform. + * + * \return String pointer. + */ +inline static const char* yp_str( + const uint8_t *data) +{ + return (const char *)data; +} + +/*! + * Converts binary value to boolean value. + * + * \param[in] data Binary value to transform. + * + * \return Boolean value. + */ +inline static bool yp_bool( + const uint8_t *data) +{ + return (data[0] == 0) ? false : true; +} + +/*! + * Converts binary value to integer value. + * + * \param[in] data Binary value to transform. + * + * \return Integer value. + */ +inline static int64_t yp_int( + const uint8_t *data) +{ + return (int64_t)knot_wire_read_u64(data); +} + +/*! + * Converts binary value to address value. + * + * \param[in] data Binary value to transform. + * + * \return Address value. + */ +struct sockaddr_storage yp_addr_noport( + const uint8_t *data +); + +/*! + * Converts binary value to address value with an optional port. + * + * \param[in] data Binary value to transform. + * \param[out] no_port No port indicator. + * + * \return Address value. + */ +struct sockaddr_storage yp_addr( + const uint8_t *data, + bool *no_port +); + +/*! + * Converts binary value to option value. + * + * \param[in] data Binary value to transform. + * + * \return Unsigned value. + */ +inline static unsigned yp_opt( + const uint8_t *data) +{ + return (unsigned)data[0]; +} + +/*! + * Converts binary value to dname pointer. + * + * \param[in] data Binary value to transform. + * + * \return Dname pointer. + */ +inline static const uint8_t* yp_dname( + const uint8_t *data) +{ + return data; +} + +/*! + * Converts binary value to data pointer. + * + * Applies to all data types with 2-byte length prefix (YP_THEX, YP_TB64). + * + * \param[in] data Binary value to transform. + * + * \return Data pointer. + */ +inline static const uint8_t* yp_bin( + const uint8_t *data) +{ + return data + 2; +} + +/*! + * Gets binary value length. + * + * Applies to all data types with 2-byte length prefix (YP_THEX, YP_TB64). + * + * \param[in] data Binary value to transform. + * + * \return Data length. + */ +inline static size_t yp_bin_len( + const uint8_t *data) +{ + return knot_wire_read_u16(data); +} + +/*! + * \brief Helper macros for conversion functions. + */ + +#define YP_CHECK_CTX \ + if (in->error != KNOT_EOK) { \ + return in->error; \ + } else if (out->error != KNOT_EOK) { \ + return out->error; \ + } \ + +#define YP_CHECK_STOP \ + if (stop != NULL) { \ + assert(stop <= in->position + wire_ctx_available(in)); \ + } else { \ + stop = in->position + wire_ctx_available(in); \ + } + +#define YP_LEN (stop - in->position) + +#define YP_CHECK_PARAMS_BIN \ + YP_CHECK_CTX YP_CHECK_STOP + +#define YP_CHECK_PARAMS_TXT \ + YP_CHECK_CTX + +#define YP_CHECK_RET \ + YP_CHECK_CTX return KNOT_EOK; + +/*! + * \brief Conversion functions for basic types. + */ + +int yp_str_to_bin( + YP_TXT_BIN_PARAMS +); + +int yp_str_to_txt( + YP_BIN_TXT_PARAMS +); + +int yp_bool_to_bin( + YP_TXT_BIN_PARAMS +); + +int yp_bool_to_txt( + YP_BIN_TXT_PARAMS +); + +int yp_int_to_bin( + YP_TXT_BIN_PARAMS, + int64_t min, + int64_t max, + yp_style_t style +); + +int yp_int_to_txt( + YP_BIN_TXT_PARAMS, + yp_style_t style +); + +int yp_addr_noport_to_bin( + YP_TXT_BIN_PARAMS, + bool allow_unix +); + +int yp_addr_noport_to_txt( + YP_BIN_TXT_PARAMS +); + +int yp_addr_to_bin( + YP_TXT_BIN_PARAMS +); + +int yp_addr_to_txt( + YP_BIN_TXT_PARAMS +); + +int yp_addr_range_to_bin( + YP_TXT_BIN_PARAMS +); + +int yp_addr_range_to_txt( + YP_BIN_TXT_PARAMS +); + +int yp_option_to_bin( + YP_TXT_BIN_PARAMS, + const struct knot_lookup *opts +); + +int yp_option_to_txt( + YP_BIN_TXT_PARAMS, + const struct knot_lookup *opts +); + +int yp_dname_to_bin( + YP_TXT_BIN_PARAMS +); + +int yp_dname_to_txt( + YP_BIN_TXT_PARAMS +); + +int yp_hex_to_bin( + YP_TXT_BIN_PARAMS +); + +int yp_hex_to_txt( + YP_BIN_TXT_PARAMS +); + +int yp_base64_to_bin( + YP_TXT_BIN_PARAMS +); + +int yp_base64_to_txt( + YP_BIN_TXT_PARAMS +); + +/*! @} */ diff --git a/src/libzscanner.pc.in b/src/libzscanner.pc.in new file mode 100644 index 0000000..35a8dab --- /dev/null +++ b/src/libzscanner.pc.in @@ -0,0 +1,13 @@ +prefix=@prefix@ +exec_prefix=@prefix@ +libdir=@libdir@ +includedir=@includedir@ +soname=@libzscanner_SONAME@ + +Name: libzscanner +Description: Knot DNS Zone Parsing library +URL: https://www.knot-dns.cz +Version: @PACKAGE_VERSION@ +Libs: -L${libdir} -lzscanner +Libs.private: -lm +Cflags: -I${includedir} diff --git a/src/libzscanner/Makefile.inc b/src/libzscanner/Makefile.inc new file mode 100644 index 0000000..1a03536 --- /dev/null +++ b/src/libzscanner/Makefile.inc @@ -0,0 +1,39 @@ +lib_LTLIBRARIES += libzscanner.la +pkgconfig_DATA += libzscanner.pc + +libzscanner_la_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAG_VISIBILITY) +libzscanner_la_LDFLAGS = $(AM_LDFLAGS) $(libzscanner_VERSION_INFO) $(LDFLAG_EXCLUDE_LIBS) +libzscanner_la_LIBADD = $(math_LIBS) + +EXTRA_DIST += \ + libzscanner/scanner.rl \ + libzscanner/scanner_body.rl \ + libzscanner/scanner.c.g2 \ + libzscanner/scanner.c.t0 +include_libzscannerdir = $(includedir)/libzscanner + +include_libzscanner_HEADERS = \ + libzscanner/error.h \ + libzscanner/scanner.h \ + libzscanner/version.h + +libzscanner_la_SOURCES = \ + libzscanner/error.c \ + libzscanner/functions.h \ + libzscanner/functions.c \ + $(include_libzscanner_HEADERS) + +BUILT_SOURCES += libzscanner/scanner.c +CLEANFILES += libzscanner/scanner.c + +nodist_libzscanner_la_SOURCES = \ + libzscanner/scanner.c + +if FAST_PARSER +libzscanner/scanner.c: libzscanner/scanner.c.g2 + @cp $(srcdir)/$@.g2 $@ + @echo "NOTE: Compilation of scanner.c can take several minutes!" +else +libzscanner/scanner.c: libzscanner/scanner.c.t0 + @cp $(srcdir)/$@.t0 $@ +endif diff --git a/src/libzscanner/error.c b/src/libzscanner/error.c new file mode 100644 index 0000000..da487fb --- /dev/null +++ b/src/libzscanner/error.c @@ -0,0 +1,188 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdlib.h> + +#include "libzscanner/error.h" + +typedef struct { + int code; + const char *text; + const char *code_name; +} err_table_t; + +#define ERR_ITEM(code, text) { code, text, #code } + +static const err_table_t err_msgs[] = { + ERR_ITEM( ZS_OK, + "ok" ), + ERR_ITEM( ZS_EINVAL, + "invalid parameter" ), + ERR_ITEM( ZS_ENOMEM, + "not enough memory" ), + ERR_ITEM( ZS_FILE_OPEN, + "file open error" ), + ERR_ITEM( ZS_FILE_INVALID, + "invalid file" ), + ERR_ITEM( ZS_DOS_NEWLINE, + "unsupported CRLF newline, remove CR bytes" ), + ERR_ITEM( ZS_UNCOVERED_STATE, + "general scanner error" ), + ERR_ITEM( ZS_UNCLOSED_MULTILINE, + "unclosed last multiline block" ), + ERR_ITEM( ZS_LEFT_PARENTHESIS, + "too many left parentheses" ), + ERR_ITEM( ZS_RIGHT_PARENTHESIS, + "too many right parentheses" ), + ERR_ITEM( ZS_UNSUPPORTED_TYPE, + "unsupported record type" ), + ERR_ITEM( ZS_BAD_PREVIOUS_OWNER, + "previous owner is invalid" ), + ERR_ITEM( ZS_BAD_DNAME_CHAR, + "invalid domain name character" ), + ERR_ITEM( ZS_BAD_OWNER, + "owner is invalid" ), + ERR_ITEM( ZS_LABEL_OVERFLOW, + "maximal domain name label length has exceeded" ), + ERR_ITEM( ZS_DNAME_OVERFLOW, + "maximal domain name length has exceeded" ), + ERR_ITEM( ZS_BAD_NUMBER, + "invalid number" ), + ERR_ITEM( ZS_NUMBER64_OVERFLOW, + "number is too big" ), + ERR_ITEM( ZS_NUMBER32_OVERFLOW, + "number is bigger than 32 bits" ), + ERR_ITEM( ZS_NUMBER16_OVERFLOW, + "number is bigger than 16 bits" ), + ERR_ITEM( ZS_NUMBER8_OVERFLOW, + "number is bigger than 8 bits" ), + ERR_ITEM( ZS_FLOAT_OVERFLOW, + "float number overflow" ), + ERR_ITEM( ZS_RDATA_OVERFLOW, + "maximal record data length has exceeded" ), + ERR_ITEM( ZS_ITEM_OVERFLOW, + "maximal item length has exceeded" ), + ERR_ITEM( ZS_BAD_ADDRESS_CHAR, + "invalid address character" ), + ERR_ITEM( ZS_BAD_IPV4, + "invalid IPv4 address" ), + ERR_ITEM( ZS_BAD_IPV6, + "invalid IPv6 address" ), + ERR_ITEM( ZS_BAD_GATEWAY, + "invalid gateway" ), + ERR_ITEM( ZS_BAD_GATEWAY_KEY, + "invalid gateway key" ), + ERR_ITEM( ZS_BAD_APL, + "invalid address prefix list" ), + ERR_ITEM( ZS_BAD_RDATA, + "invalid record data" ), + ERR_ITEM( ZS_BAD_HEX_RDATA, + "invalid record data in hex format" ), + ERR_ITEM( ZS_BAD_HEX_CHAR, + "invalid hexadecimal character" ), + ERR_ITEM( ZS_BAD_BASE64_CHAR, + "invalid Base64 character" ), + ERR_ITEM( ZS_BAD_BASE32HEX_CHAR, + "invalid Base32hex character" ), + ERR_ITEM( ZS_BAD_REST, + "unexpected data" ), + ERR_ITEM( ZS_BAD_TIMESTAMP_CHAR, + "invalid timestamp character" ), + ERR_ITEM( ZS_BAD_TIMESTAMP_LENGTH, + "invalid timestamp length" ), + ERR_ITEM( ZS_BAD_TIMESTAMP, + "invalid timestamp" ), + ERR_ITEM( ZS_BAD_DATE, + "invalid date" ), + ERR_ITEM( ZS_BAD_TIME, + "invalid time" ), + ERR_ITEM( ZS_BAD_TIME_UNIT, + "invalid time unit" ), + ERR_ITEM( ZS_BAD_BITMAP, + "invalid bitmap" ), + ERR_ITEM( ZS_TEXT_OVERFLOW, + "text is too long" ), + ERR_ITEM( ZS_BAD_TEXT_CHAR, + "invalid text character" ), + ERR_ITEM( ZS_BAD_TEXT, + "invalid text string" ), + ERR_ITEM( ZS_BAD_DIRECTIVE, + "invalid directive" ), + ERR_ITEM( ZS_BAD_TTL, + "invalid zone TTL" ), + ERR_ITEM( ZS_BAD_ORIGIN, + "invalid FQDN zone origin" ), + ERR_ITEM( ZS_BAD_INCLUDE_FILENAME, + "invalid filename in include directive" ), + ERR_ITEM( ZS_BAD_INCLUDE_ORIGIN, + "invalid origin in include directive" ), + ERR_ITEM( ZS_UNPROCESSED_INCLUDE, + "include file processing error" ), + ERR_ITEM( ZS_UNOPENED_INCLUDE, + "include file opening error" ), + ERR_ITEM( ZS_BAD_RDATA_LENGTH, + "the rdata length statement is incorrect" ), + ERR_ITEM( ZS_CANNOT_TEXT_DATA, + "unable to process text form for this type" ), + ERR_ITEM( ZS_BAD_LOC_DATA, + "invalid zone location data" ), + ERR_ITEM( ZS_UNKNOWN_BLOCK, + "unknown rdata block" ), + ERR_ITEM( ZS_BAD_ALGORITHM, + "invalid algorithm" ), + ERR_ITEM( ZS_BAD_CERT_TYPE, + "invalid certificate type" ), + ERR_ITEM( ZS_BAD_EUI_LENGTH, + "invalid EUI length" ), + ERR_ITEM( ZS_BAD_L64_LENGTH, + "invalid 64-bit locator" ), + ERR_ITEM( ZS_BAD_CHAR_COLON, + "missing colon character" ), + ERR_ITEM( ZS_BAD_CHAR_DASH, + "missing dash character" ), + + ERR_ITEM( 0, NULL ) // Terminator +}; + +__attribute__((visibility("default"))) +const char* zs_strerror(const int code) +{ + const err_table_t *err = err_msgs; + + while (err->text != NULL) { + if (err->code == code) { + return err->text; + } + err++; + } + + return NULL; +} + +__attribute__((visibility("default"))) +const char* zs_errorname(const int code) +{ + const err_table_t *err = err_msgs; + + while (err->text != NULL) { + if (err->code == code) { + return err->code_name; + } + err++; + } + + return NULL; +} diff --git a/src/libzscanner/error.h b/src/libzscanner/error.h new file mode 100644 index 0000000..c547127 --- /dev/null +++ b/src/libzscanner/error.h @@ -0,0 +1,111 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \file + * + * \brief Error codes and handling. + * + * \addtogroup zone_scanner + * @{ + */ + +#pragma once + +enum err_codes { + ZS_OK = 0, + ZS_EINVAL = -1000, + ZS_ENOMEM, + ZS_FILE_OPEN, + ZS_FILE_INVALID, + ZS_DOS_NEWLINE, + ZS_UNCOVERED_STATE, + ZS_UNCLOSED_MULTILINE, + ZS_LEFT_PARENTHESIS, + ZS_RIGHT_PARENTHESIS, + ZS_UNSUPPORTED_TYPE, + ZS_BAD_PREVIOUS_OWNER, + ZS_BAD_DNAME_CHAR, + ZS_BAD_OWNER, + ZS_LABEL_OVERFLOW, + ZS_DNAME_OVERFLOW, + ZS_BAD_NUMBER, + ZS_NUMBER64_OVERFLOW, + ZS_NUMBER32_OVERFLOW, + ZS_NUMBER16_OVERFLOW, + ZS_NUMBER8_OVERFLOW, + ZS_FLOAT_OVERFLOW, + ZS_RDATA_OVERFLOW, + ZS_ITEM_OVERFLOW, + ZS_BAD_ADDRESS_CHAR, + ZS_BAD_IPV4, + ZS_BAD_IPV6, + ZS_BAD_GATEWAY, + ZS_BAD_GATEWAY_KEY, + ZS_BAD_APL, + ZS_BAD_RDATA, + ZS_BAD_HEX_RDATA, + ZS_BAD_HEX_CHAR, + ZS_BAD_BASE64_CHAR, + ZS_BAD_BASE32HEX_CHAR, + ZS_BAD_REST, + ZS_BAD_TIMESTAMP_CHAR, + ZS_BAD_TIMESTAMP_LENGTH, + ZS_BAD_TIMESTAMP, + ZS_BAD_DATE, + ZS_BAD_TIME, + ZS_BAD_TIME_UNIT, + ZS_BAD_BITMAP, + ZS_TEXT_OVERFLOW, + ZS_BAD_TEXT_CHAR, + ZS_BAD_TEXT, + ZS_BAD_DIRECTIVE, + ZS_BAD_TTL, + ZS_BAD_ORIGIN, + ZS_BAD_INCLUDE_FILENAME, + ZS_BAD_INCLUDE_ORIGIN, + ZS_UNPROCESSED_INCLUDE, + ZS_UNOPENED_INCLUDE, + ZS_BAD_RDATA_LENGTH, + ZS_CANNOT_TEXT_DATA, + ZS_BAD_LOC_DATA, + ZS_UNKNOWN_BLOCK, + ZS_BAD_ALGORITHM, + ZS_BAD_CERT_TYPE, + ZS_BAD_EUI_LENGTH, + ZS_BAD_L64_LENGTH, + ZS_BAD_CHAR_COLON, + ZS_BAD_CHAR_DASH +}; + +/*! + * \brief Returns error message for the given error code. + * + * \param code Error code. + * + * \return String containing the error message. + */ +const char* zs_strerror(const int code); + +/*! + * \brief Returns error code name of the given error code. + * + * \param code Error code. + * + * \return String containing the error code name. + */ +const char* zs_errorname(const int code); + +/*! @} */ diff --git a/src/libzscanner/functions.c b/src/libzscanner/functions.c new file mode 100644 index 0000000..d9a1477 --- /dev/null +++ b/src/libzscanner/functions.c @@ -0,0 +1,821 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stdint.h> +#include <stdlib.h> + +#include "libzscanner/error.h" +#include "libzscanner/functions.h" + +const uint8_t digit_to_num[] = { + ['0'] = 0, ['1'] = 1, ['2'] = 2, ['3'] = 3, ['4'] = 4, + ['5'] = 5, ['6'] = 6, ['7'] = 7, ['8'] = 8, ['9'] = 9, +}; + +/* + * Hex transformation: + * 1 2 + * 12345678 12345678 + * in: AAAA BBBB + * out: AAAABBBB + */ + +const uint8_t first_hex_to_num[] = { + ['0'] = ( 0 * 16), ['6'] = ( 6 * 16), ['C'] = (12 * 16), ['b'] = (11 * 16), + ['1'] = ( 1 * 16), ['7'] = ( 7 * 16), ['D'] = (13 * 16), ['c'] = (12 * 16), + ['2'] = ( 2 * 16), ['8'] = ( 8 * 16), ['E'] = (14 * 16), ['d'] = (13 * 16), + ['3'] = ( 3 * 16), ['9'] = ( 9 * 16), ['F'] = (15 * 16), ['e'] = (14 * 16), + ['4'] = ( 4 * 16), ['A'] = (10 * 16), ['a'] = (10 * 16), ['f'] = (15 * 16), + ['5'] = ( 5 * 16), ['B'] = (11 * 16), +}; + +const uint8_t second_hex_to_num[] = { + ['0'] = 0, ['4'] = 4, ['8'] = 8, ['C'] = 12, ['a'] = 10, ['d'] = 13, + ['1'] = 1, ['5'] = 5, ['9'] = 9, ['D'] = 13, ['b'] = 11, ['e'] = 14, + ['2'] = 2, ['6'] = 6, ['A'] = 10, ['E'] = 14, ['c'] = 12, ['f'] = 15, + ['3'] = 3, ['7'] = 7, ['B'] = 11, ['F'] = 15, +}; + +/* + * Base64 transformation: + * 1 2 3 4 + * 12345678 12345678 12345678 12345678 + * in: 00AAAAAA 00BBBBBB 00CCCCCC 00DDDDDD + * out: AAAAAABB BBBBCCCC CCDDDDDD + */ + +// 0x3F = 00111111 +const uint8_t first_base64_to_num[] = { + ['A'] = (( 0 & 0x3F) << 2), ['g'] = ((32 & 0x3F) << 2), + ['B'] = (( 1 & 0x3F) << 2), ['h'] = ((33 & 0x3F) << 2), + ['C'] = (( 2 & 0x3F) << 2), ['i'] = ((34 & 0x3F) << 2), + ['D'] = (( 3 & 0x3F) << 2), ['j'] = ((35 & 0x3F) << 2), + ['E'] = (( 4 & 0x3F) << 2), ['k'] = ((36 & 0x3F) << 2), + ['F'] = (( 5 & 0x3F) << 2), ['l'] = ((37 & 0x3F) << 2), + ['G'] = (( 6 & 0x3F) << 2), ['m'] = ((38 & 0x3F) << 2), + ['H'] = (( 7 & 0x3F) << 2), ['n'] = ((39 & 0x3F) << 2), + ['I'] = (( 8 & 0x3F) << 2), ['o'] = ((40 & 0x3F) << 2), + ['J'] = (( 9 & 0x3F) << 2), ['p'] = ((41 & 0x3F) << 2), + ['K'] = ((10 & 0x3F) << 2), ['q'] = ((42 & 0x3F) << 2), + ['L'] = ((11 & 0x3F) << 2), ['r'] = ((43 & 0x3F) << 2), + ['M'] = ((12 & 0x3F) << 2), ['s'] = ((44 & 0x3F) << 2), + ['N'] = ((13 & 0x3F) << 2), ['t'] = ((45 & 0x3F) << 2), + ['O'] = ((14 & 0x3F) << 2), ['u'] = ((46 & 0x3F) << 2), + ['P'] = ((15 & 0x3F) << 2), ['v'] = ((47 & 0x3F) << 2), + ['Q'] = ((16 & 0x3F) << 2), ['w'] = ((48 & 0x3F) << 2), + ['R'] = ((17 & 0x3F) << 2), ['x'] = ((49 & 0x3F) << 2), + ['S'] = ((18 & 0x3F) << 2), ['y'] = ((50 & 0x3F) << 2), + ['T'] = ((19 & 0x3F) << 2), ['z'] = ((51 & 0x3F) << 2), + ['U'] = ((20 & 0x3F) << 2), ['0'] = ((52 & 0x3F) << 2), + ['V'] = ((21 & 0x3F) << 2), ['1'] = ((53 & 0x3F) << 2), + ['W'] = ((22 & 0x3F) << 2), ['2'] = ((54 & 0x3F) << 2), + ['X'] = ((23 & 0x3F) << 2), ['3'] = ((55 & 0x3F) << 2), + ['Y'] = ((24 & 0x3F) << 2), ['4'] = ((56 & 0x3F) << 2), + ['Z'] = ((25 & 0x3F) << 2), ['5'] = ((57 & 0x3F) << 2), + ['a'] = ((26 & 0x3F) << 2), ['6'] = ((58 & 0x3F) << 2), + ['b'] = ((27 & 0x3F) << 2), ['7'] = ((59 & 0x3F) << 2), + ['c'] = ((28 & 0x3F) << 2), ['8'] = ((60 & 0x3F) << 2), + ['d'] = ((29 & 0x3F) << 2), ['9'] = ((61 & 0x3F) << 2), + ['e'] = ((30 & 0x3F) << 2), ['+'] = ((62 & 0x3F) << 2), + ['f'] = ((31 & 0x3F) << 2), ['/'] = ((63 & 0x3F) << 2), +}; + +// 0x30 = 00110000 +const uint8_t second_left_base64_to_num[] = { + ['A'] = (( 0 & 0x30) >> 4), ['g'] = ((32 & 0x30) >> 4), + ['B'] = (( 1 & 0x30) >> 4), ['h'] = ((33 & 0x30) >> 4), + ['C'] = (( 2 & 0x30) >> 4), ['i'] = ((34 & 0x30) >> 4), + ['D'] = (( 3 & 0x30) >> 4), ['j'] = ((35 & 0x30) >> 4), + ['E'] = (( 4 & 0x30) >> 4), ['k'] = ((36 & 0x30) >> 4), + ['F'] = (( 5 & 0x30) >> 4), ['l'] = ((37 & 0x30) >> 4), + ['G'] = (( 6 & 0x30) >> 4), ['m'] = ((38 & 0x30) >> 4), + ['H'] = (( 7 & 0x30) >> 4), ['n'] = ((39 & 0x30) >> 4), + ['I'] = (( 8 & 0x30) >> 4), ['o'] = ((40 & 0x30) >> 4), + ['J'] = (( 9 & 0x30) >> 4), ['p'] = ((41 & 0x30) >> 4), + ['K'] = ((10 & 0x30) >> 4), ['q'] = ((42 & 0x30) >> 4), + ['L'] = ((11 & 0x30) >> 4), ['r'] = ((43 & 0x30) >> 4), + ['M'] = ((12 & 0x30) >> 4), ['s'] = ((44 & 0x30) >> 4), + ['N'] = ((13 & 0x30) >> 4), ['t'] = ((45 & 0x30) >> 4), + ['O'] = ((14 & 0x30) >> 4), ['u'] = ((46 & 0x30) >> 4), + ['P'] = ((15 & 0x30) >> 4), ['v'] = ((47 & 0x30) >> 4), + ['Q'] = ((16 & 0x30) >> 4), ['w'] = ((48 & 0x30) >> 4), + ['R'] = ((17 & 0x30) >> 4), ['x'] = ((49 & 0x30) >> 4), + ['S'] = ((18 & 0x30) >> 4), ['y'] = ((50 & 0x30) >> 4), + ['T'] = ((19 & 0x30) >> 4), ['z'] = ((51 & 0x30) >> 4), + ['U'] = ((20 & 0x30) >> 4), ['0'] = ((52 & 0x30) >> 4), + ['V'] = ((21 & 0x30) >> 4), ['1'] = ((53 & 0x30) >> 4), + ['W'] = ((22 & 0x30) >> 4), ['2'] = ((54 & 0x30) >> 4), + ['X'] = ((23 & 0x30) >> 4), ['3'] = ((55 & 0x30) >> 4), + ['Y'] = ((24 & 0x30) >> 4), ['4'] = ((56 & 0x30) >> 4), + ['Z'] = ((25 & 0x30) >> 4), ['5'] = ((57 & 0x30) >> 4), + ['a'] = ((26 & 0x30) >> 4), ['6'] = ((58 & 0x30) >> 4), + ['b'] = ((27 & 0x30) >> 4), ['7'] = ((59 & 0x30) >> 4), + ['c'] = ((28 & 0x30) >> 4), ['8'] = ((60 & 0x30) >> 4), + ['d'] = ((29 & 0x30) >> 4), ['9'] = ((61 & 0x30) >> 4), + ['e'] = ((30 & 0x30) >> 4), ['+'] = ((62 & 0x30) >> 4), + ['f'] = ((31 & 0x30) >> 4), ['/'] = ((63 & 0x30) >> 4), +}; + +// 0x0F = 00001111 +const uint8_t second_right_base64_to_num[] = { + ['A'] = (( 0 & 0x0F) << 4), ['g'] = ((32 & 0x0F) << 4), + ['B'] = (( 1 & 0x0F) << 4), ['h'] = ((33 & 0x0F) << 4), + ['C'] = (( 2 & 0x0F) << 4), ['i'] = ((34 & 0x0F) << 4), + ['D'] = (( 3 & 0x0F) << 4), ['j'] = ((35 & 0x0F) << 4), + ['E'] = (( 4 & 0x0F) << 4), ['k'] = ((36 & 0x0F) << 4), + ['F'] = (( 5 & 0x0F) << 4), ['l'] = ((37 & 0x0F) << 4), + ['G'] = (( 6 & 0x0F) << 4), ['m'] = ((38 & 0x0F) << 4), + ['H'] = (( 7 & 0x0F) << 4), ['n'] = ((39 & 0x0F) << 4), + ['I'] = (( 8 & 0x0F) << 4), ['o'] = ((40 & 0x0F) << 4), + ['J'] = (( 9 & 0x0F) << 4), ['p'] = ((41 & 0x0F) << 4), + ['K'] = ((10 & 0x0F) << 4), ['q'] = ((42 & 0x0F) << 4), + ['L'] = ((11 & 0x0F) << 4), ['r'] = ((43 & 0x0F) << 4), + ['M'] = ((12 & 0x0F) << 4), ['s'] = ((44 & 0x0F) << 4), + ['N'] = ((13 & 0x0F) << 4), ['t'] = ((45 & 0x0F) << 4), + ['O'] = ((14 & 0x0F) << 4), ['u'] = ((46 & 0x0F) << 4), + ['P'] = ((15 & 0x0F) << 4), ['v'] = ((47 & 0x0F) << 4), + ['Q'] = ((16 & 0x0F) << 4), ['w'] = ((48 & 0x0F) << 4), + ['R'] = ((17 & 0x0F) << 4), ['x'] = ((49 & 0x0F) << 4), + ['S'] = ((18 & 0x0F) << 4), ['y'] = ((50 & 0x0F) << 4), + ['T'] = ((19 & 0x0F) << 4), ['z'] = ((51 & 0x0F) << 4), + ['U'] = ((20 & 0x0F) << 4), ['0'] = ((52 & 0x0F) << 4), + ['V'] = ((21 & 0x0F) << 4), ['1'] = ((53 & 0x0F) << 4), + ['W'] = ((22 & 0x0F) << 4), ['2'] = ((54 & 0x0F) << 4), + ['X'] = ((23 & 0x0F) << 4), ['3'] = ((55 & 0x0F) << 4), + ['Y'] = ((24 & 0x0F) << 4), ['4'] = ((56 & 0x0F) << 4), + ['Z'] = ((25 & 0x0F) << 4), ['5'] = ((57 & 0x0F) << 4), + ['a'] = ((26 & 0x0F) << 4), ['6'] = ((58 & 0x0F) << 4), + ['b'] = ((27 & 0x0F) << 4), ['7'] = ((59 & 0x0F) << 4), + ['c'] = ((28 & 0x0F) << 4), ['8'] = ((60 & 0x0F) << 4), + ['d'] = ((29 & 0x0F) << 4), ['9'] = ((61 & 0x0F) << 4), + ['e'] = ((30 & 0x0F) << 4), ['+'] = ((62 & 0x0F) << 4), + ['f'] = ((31 & 0x0F) << 4), ['/'] = ((63 & 0x0F) << 4), +}; + +// 0x3C = 00111100 +const uint8_t third_left_base64_to_num[] = { + ['A'] = (( 0 & 0x3C) >> 2), ['g'] = ((32 & 0x3C) >> 2), + ['B'] = (( 1 & 0x3C) >> 2), ['h'] = ((33 & 0x3C) >> 2), + ['C'] = (( 2 & 0x3C) >> 2), ['i'] = ((34 & 0x3C) >> 2), + ['D'] = (( 3 & 0x3C) >> 2), ['j'] = ((35 & 0x3C) >> 2), + ['E'] = (( 4 & 0x3C) >> 2), ['k'] = ((36 & 0x3C) >> 2), + ['F'] = (( 5 & 0x3C) >> 2), ['l'] = ((37 & 0x3C) >> 2), + ['G'] = (( 6 & 0x3C) >> 2), ['m'] = ((38 & 0x3C) >> 2), + ['H'] = (( 7 & 0x3C) >> 2), ['n'] = ((39 & 0x3C) >> 2), + ['I'] = (( 8 & 0x3C) >> 2), ['o'] = ((40 & 0x3C) >> 2), + ['J'] = (( 9 & 0x3C) >> 2), ['p'] = ((41 & 0x3C) >> 2), + ['K'] = ((10 & 0x3C) >> 2), ['q'] = ((42 & 0x3C) >> 2), + ['L'] = ((11 & 0x3C) >> 2), ['r'] = ((43 & 0x3C) >> 2), + ['M'] = ((12 & 0x3C) >> 2), ['s'] = ((44 & 0x3C) >> 2), + ['N'] = ((13 & 0x3C) >> 2), ['t'] = ((45 & 0x3C) >> 2), + ['O'] = ((14 & 0x3C) >> 2), ['u'] = ((46 & 0x3C) >> 2), + ['P'] = ((15 & 0x3C) >> 2), ['v'] = ((47 & 0x3C) >> 2), + ['Q'] = ((16 & 0x3C) >> 2), ['w'] = ((48 & 0x3C) >> 2), + ['R'] = ((17 & 0x3C) >> 2), ['x'] = ((49 & 0x3C) >> 2), + ['S'] = ((18 & 0x3C) >> 2), ['y'] = ((50 & 0x3C) >> 2), + ['T'] = ((19 & 0x3C) >> 2), ['z'] = ((51 & 0x3C) >> 2), + ['U'] = ((20 & 0x3C) >> 2), ['0'] = ((52 & 0x3C) >> 2), + ['V'] = ((21 & 0x3C) >> 2), ['1'] = ((53 & 0x3C) >> 2), + ['W'] = ((22 & 0x3C) >> 2), ['2'] = ((54 & 0x3C) >> 2), + ['X'] = ((23 & 0x3C) >> 2), ['3'] = ((55 & 0x3C) >> 2), + ['Y'] = ((24 & 0x3C) >> 2), ['4'] = ((56 & 0x3C) >> 2), + ['Z'] = ((25 & 0x3C) >> 2), ['5'] = ((57 & 0x3C) >> 2), + ['a'] = ((26 & 0x3C) >> 2), ['6'] = ((58 & 0x3C) >> 2), + ['b'] = ((27 & 0x3C) >> 2), ['7'] = ((59 & 0x3C) >> 2), + ['c'] = ((28 & 0x3C) >> 2), ['8'] = ((60 & 0x3C) >> 2), + ['d'] = ((29 & 0x3C) >> 2), ['9'] = ((61 & 0x3C) >> 2), + ['e'] = ((30 & 0x3C) >> 2), ['+'] = ((62 & 0x3C) >> 2), + ['f'] = ((31 & 0x3C) >> 2), ['/'] = ((63 & 0x3C) >> 2), +}; + +// 0x03 = 00000011 +const uint8_t third_right_base64_to_num[] = { + ['A'] = (( 0 & 0x03) << 6), ['g'] = ((32 & 0x03) << 6), + ['B'] = (( 1 & 0x03) << 6), ['h'] = ((33 & 0x03) << 6), + ['C'] = (( 2 & 0x03) << 6), ['i'] = ((34 & 0x03) << 6), + ['D'] = (( 3 & 0x03) << 6), ['j'] = ((35 & 0x03) << 6), + ['E'] = (( 4 & 0x03) << 6), ['k'] = ((36 & 0x03) << 6), + ['F'] = (( 5 & 0x03) << 6), ['l'] = ((37 & 0x03) << 6), + ['G'] = (( 6 & 0x03) << 6), ['m'] = ((38 & 0x03) << 6), + ['H'] = (( 7 & 0x03) << 6), ['n'] = ((39 & 0x03) << 6), + ['I'] = (( 8 & 0x03) << 6), ['o'] = ((40 & 0x03) << 6), + ['J'] = (( 9 & 0x03) << 6), ['p'] = ((41 & 0x03) << 6), + ['K'] = ((10 & 0x03) << 6), ['q'] = ((42 & 0x03) << 6), + ['L'] = ((11 & 0x03) << 6), ['r'] = ((43 & 0x03) << 6), + ['M'] = ((12 & 0x03) << 6), ['s'] = ((44 & 0x03) << 6), + ['N'] = ((13 & 0x03) << 6), ['t'] = ((45 & 0x03) << 6), + ['O'] = ((14 & 0x03) << 6), ['u'] = ((46 & 0x03) << 6), + ['P'] = ((15 & 0x03) << 6), ['v'] = ((47 & 0x03) << 6), + ['Q'] = ((16 & 0x03) << 6), ['w'] = ((48 & 0x03) << 6), + ['R'] = ((17 & 0x03) << 6), ['x'] = ((49 & 0x03) << 6), + ['S'] = ((18 & 0x03) << 6), ['y'] = ((50 & 0x03) << 6), + ['T'] = ((19 & 0x03) << 6), ['z'] = ((51 & 0x03) << 6), + ['U'] = ((20 & 0x03) << 6), ['0'] = ((52 & 0x03) << 6), + ['V'] = ((21 & 0x03) << 6), ['1'] = ((53 & 0x03) << 6), + ['W'] = ((22 & 0x03) << 6), ['2'] = ((54 & 0x03) << 6), + ['X'] = ((23 & 0x03) << 6), ['3'] = ((55 & 0x03) << 6), + ['Y'] = ((24 & 0x03) << 6), ['4'] = ((56 & 0x03) << 6), + ['Z'] = ((25 & 0x03) << 6), ['5'] = ((57 & 0x03) << 6), + ['a'] = ((26 & 0x03) << 6), ['6'] = ((58 & 0x03) << 6), + ['b'] = ((27 & 0x03) << 6), ['7'] = ((59 & 0x03) << 6), + ['c'] = ((28 & 0x03) << 6), ['8'] = ((60 & 0x03) << 6), + ['d'] = ((29 & 0x03) << 6), ['9'] = ((61 & 0x03) << 6), + ['e'] = ((30 & 0x03) << 6), ['+'] = ((62 & 0x03) << 6), + ['f'] = ((31 & 0x03) << 6), ['/'] = ((63 & 0x03) << 6), +}; + +// 0x3F = 00111111 +const uint8_t fourth_base64_to_num[] = { + ['A'] = (( 0 & 0x3F) << 0), ['g'] = ((32 & 0x3F) << 0), + ['B'] = (( 1 & 0x3F) << 0), ['h'] = ((33 & 0x3F) << 0), + ['C'] = (( 2 & 0x3F) << 0), ['i'] = ((34 & 0x3F) << 0), + ['D'] = (( 3 & 0x3F) << 0), ['j'] = ((35 & 0x3F) << 0), + ['E'] = (( 4 & 0x3F) << 0), ['k'] = ((36 & 0x3F) << 0), + ['F'] = (( 5 & 0x3F) << 0), ['l'] = ((37 & 0x3F) << 0), + ['G'] = (( 6 & 0x3F) << 0), ['m'] = ((38 & 0x3F) << 0), + ['H'] = (( 7 & 0x3F) << 0), ['n'] = ((39 & 0x3F) << 0), + ['I'] = (( 8 & 0x3F) << 0), ['o'] = ((40 & 0x3F) << 0), + ['J'] = (( 9 & 0x3F) << 0), ['p'] = ((41 & 0x3F) << 0), + ['K'] = ((10 & 0x3F) << 0), ['q'] = ((42 & 0x3F) << 0), + ['L'] = ((11 & 0x3F) << 0), ['r'] = ((43 & 0x3F) << 0), + ['M'] = ((12 & 0x3F) << 0), ['s'] = ((44 & 0x3F) << 0), + ['N'] = ((13 & 0x3F) << 0), ['t'] = ((45 & 0x3F) << 0), + ['O'] = ((14 & 0x3F) << 0), ['u'] = ((46 & 0x3F) << 0), + ['P'] = ((15 & 0x3F) << 0), ['v'] = ((47 & 0x3F) << 0), + ['Q'] = ((16 & 0x3F) << 0), ['w'] = ((48 & 0x3F) << 0), + ['R'] = ((17 & 0x3F) << 0), ['x'] = ((49 & 0x3F) << 0), + ['S'] = ((18 & 0x3F) << 0), ['y'] = ((50 & 0x3F) << 0), + ['T'] = ((19 & 0x3F) << 0), ['z'] = ((51 & 0x3F) << 0), + ['U'] = ((20 & 0x3F) << 0), ['0'] = ((52 & 0x3F) << 0), + ['V'] = ((21 & 0x3F) << 0), ['1'] = ((53 & 0x3F) << 0), + ['W'] = ((22 & 0x3F) << 0), ['2'] = ((54 & 0x3F) << 0), + ['X'] = ((23 & 0x3F) << 0), ['3'] = ((55 & 0x3F) << 0), + ['Y'] = ((24 & 0x3F) << 0), ['4'] = ((56 & 0x3F) << 0), + ['Z'] = ((25 & 0x3F) << 0), ['5'] = ((57 & 0x3F) << 0), + ['a'] = ((26 & 0x3F) << 0), ['6'] = ((58 & 0x3F) << 0), + ['b'] = ((27 & 0x3F) << 0), ['7'] = ((59 & 0x3F) << 0), + ['c'] = ((28 & 0x3F) << 0), ['8'] = ((60 & 0x3F) << 0), + ['d'] = ((29 & 0x3F) << 0), ['9'] = ((61 & 0x3F) << 0), + ['e'] = ((30 & 0x3F) << 0), ['+'] = ((62 & 0x3F) << 0), + ['f'] = ((31 & 0x3F) << 0), ['/'] = ((63 & 0x3F) << 0), +}; + +/* + * Base32hex transformation (with lower-case): + * 1 2 3 4 5 6 7 8 + * 12345678 12345678 12345678 12345678 12345678 12345678 12345678 12345678 + * in: 000AAAAA 000BBBBB 000CCCCC 000DDDDD 000EEEEE 000FFFFF 000GGGGG 000HHHHH + * out AAAAABBB BBCCCCCD DDDDEEEE EFFFFFGG GGGHHHHH + */ + +// 0x1F = 00011111 +const uint8_t first_base32hex_to_num[] = { + ['0'] = (( 0 & 0x1F) << 3), ['R'] = ((27 & 0x1F) << 3), + ['1'] = (( 1 & 0x1F) << 3), ['S'] = ((28 & 0x1F) << 3), + ['2'] = (( 2 & 0x1F) << 3), ['T'] = ((29 & 0x1F) << 3), + ['3'] = (( 3 & 0x1F) << 3), ['U'] = ((30 & 0x1F) << 3), + ['4'] = (( 4 & 0x1F) << 3), ['V'] = ((31 & 0x1F) << 3), + ['5'] = (( 5 & 0x1F) << 3), ['a'] = ((10 & 0x1F) << 3), + ['6'] = (( 6 & 0x1F) << 3), ['b'] = ((11 & 0x1F) << 3), + ['7'] = (( 7 & 0x1F) << 3), ['c'] = ((12 & 0x1F) << 3), + ['8'] = (( 8 & 0x1F) << 3), ['d'] = ((13 & 0x1F) << 3), + ['9'] = (( 9 & 0x1F) << 3), ['e'] = ((14 & 0x1F) << 3), + ['A'] = ((10 & 0x1F) << 3), ['f'] = ((15 & 0x1F) << 3), + ['B'] = ((11 & 0x1F) << 3), ['g'] = ((16 & 0x1F) << 3), + ['C'] = ((12 & 0x1F) << 3), ['h'] = ((17 & 0x1F) << 3), + ['D'] = ((13 & 0x1F) << 3), ['i'] = ((18 & 0x1F) << 3), + ['E'] = ((14 & 0x1F) << 3), ['j'] = ((19 & 0x1F) << 3), + ['F'] = ((15 & 0x1F) << 3), ['k'] = ((20 & 0x1F) << 3), + ['G'] = ((16 & 0x1F) << 3), ['l'] = ((21 & 0x1F) << 3), + ['H'] = ((17 & 0x1F) << 3), ['m'] = ((22 & 0x1F) << 3), + ['I'] = ((18 & 0x1F) << 3), ['n'] = ((23 & 0x1F) << 3), + ['J'] = ((19 & 0x1F) << 3), ['o'] = ((24 & 0x1F) << 3), + ['K'] = ((20 & 0x1F) << 3), ['p'] = ((25 & 0x1F) << 3), + ['L'] = ((21 & 0x1F) << 3), ['q'] = ((26 & 0x1F) << 3), + ['M'] = ((22 & 0x1F) << 3), ['r'] = ((27 & 0x1F) << 3), + ['N'] = ((23 & 0x1F) << 3), ['s'] = ((28 & 0x1F) << 3), + ['O'] = ((24 & 0x1F) << 3), ['t'] = ((29 & 0x1F) << 3), + ['P'] = ((25 & 0x1F) << 3), ['u'] = ((30 & 0x1F) << 3), + ['Q'] = ((26 & 0x1F) << 3), ['v'] = ((31 & 0x1F) << 3), +}; + +// 0x1C = 00011100 +const uint8_t second_left_base32hex_to_num[] = { + ['0'] = (( 0 & 0x1C) >> 2), ['R'] = ((27 & 0x1C) >> 2), + ['1'] = (( 1 & 0x1C) >> 2), ['S'] = ((28 & 0x1C) >> 2), + ['2'] = (( 2 & 0x1C) >> 2), ['T'] = ((29 & 0x1C) >> 2), + ['3'] = (( 3 & 0x1C) >> 2), ['U'] = ((30 & 0x1C) >> 2), + ['4'] = (( 4 & 0x1C) >> 2), ['V'] = ((31 & 0x1C) >> 2), + ['5'] = (( 5 & 0x1C) >> 2), ['a'] = ((10 & 0x1C) >> 2), + ['6'] = (( 6 & 0x1C) >> 2), ['b'] = ((11 & 0x1C) >> 2), + ['7'] = (( 7 & 0x1C) >> 2), ['c'] = ((12 & 0x1C) >> 2), + ['8'] = (( 8 & 0x1C) >> 2), ['d'] = ((13 & 0x1C) >> 2), + ['9'] = (( 9 & 0x1C) >> 2), ['e'] = ((14 & 0x1C) >> 2), + ['A'] = ((10 & 0x1C) >> 2), ['f'] = ((15 & 0x1C) >> 2), + ['B'] = ((11 & 0x1C) >> 2), ['g'] = ((16 & 0x1C) >> 2), + ['C'] = ((12 & 0x1C) >> 2), ['h'] = ((17 & 0x1C) >> 2), + ['D'] = ((13 & 0x1C) >> 2), ['i'] = ((18 & 0x1C) >> 2), + ['E'] = ((14 & 0x1C) >> 2), ['j'] = ((19 & 0x1C) >> 2), + ['F'] = ((15 & 0x1C) >> 2), ['k'] = ((20 & 0x1C) >> 2), + ['G'] = ((16 & 0x1C) >> 2), ['l'] = ((21 & 0x1C) >> 2), + ['H'] = ((17 & 0x1C) >> 2), ['m'] = ((22 & 0x1C) >> 2), + ['I'] = ((18 & 0x1C) >> 2), ['n'] = ((23 & 0x1C) >> 2), + ['J'] = ((19 & 0x1C) >> 2), ['o'] = ((24 & 0x1C) >> 2), + ['K'] = ((20 & 0x1C) >> 2), ['p'] = ((25 & 0x1C) >> 2), + ['L'] = ((21 & 0x1C) >> 2), ['q'] = ((26 & 0x1C) >> 2), + ['M'] = ((22 & 0x1C) >> 2), ['r'] = ((27 & 0x1C) >> 2), + ['N'] = ((23 & 0x1C) >> 2), ['s'] = ((28 & 0x1C) >> 2), + ['O'] = ((24 & 0x1C) >> 2), ['t'] = ((29 & 0x1C) >> 2), + ['P'] = ((25 & 0x1C) >> 2), ['u'] = ((30 & 0x1C) >> 2), + ['Q'] = ((26 & 0x1C) >> 2), ['v'] = ((31 & 0x1C) >> 2), +}; + +// 0x03 = 00000011 +const uint8_t second_right_base32hex_to_num[] = { + ['0'] = (( 0 & 0x03) << 6), ['R'] = ((27 & 0x03) << 6), + ['1'] = (( 1 & 0x03) << 6), ['S'] = ((28 & 0x03) << 6), + ['2'] = (( 2 & 0x03) << 6), ['T'] = ((29 & 0x03) << 6), + ['3'] = (( 3 & 0x03) << 6), ['U'] = ((30 & 0x03) << 6), + ['4'] = (( 4 & 0x03) << 6), ['V'] = ((31 & 0x03) << 6), + ['5'] = (( 5 & 0x03) << 6), ['a'] = ((10 & 0x03) << 6), + ['6'] = (( 6 & 0x03) << 6), ['b'] = ((11 & 0x03) << 6), + ['7'] = (( 7 & 0x03) << 6), ['c'] = ((12 & 0x03) << 6), + ['8'] = (( 8 & 0x03) << 6), ['d'] = ((13 & 0x03) << 6), + ['9'] = (( 9 & 0x03) << 6), ['e'] = ((14 & 0x03) << 6), + ['A'] = ((10 & 0x03) << 6), ['f'] = ((15 & 0x03) << 6), + ['B'] = ((11 & 0x03) << 6), ['g'] = ((16 & 0x03) << 6), + ['C'] = ((12 & 0x03) << 6), ['h'] = ((17 & 0x03) << 6), + ['D'] = ((13 & 0x03) << 6), ['i'] = ((18 & 0x03) << 6), + ['E'] = ((14 & 0x03) << 6), ['j'] = ((19 & 0x03) << 6), + ['F'] = ((15 & 0x03) << 6), ['k'] = ((20 & 0x03) << 6), + ['G'] = ((16 & 0x03) << 6), ['l'] = ((21 & 0x03) << 6), + ['H'] = ((17 & 0x03) << 6), ['m'] = ((22 & 0x03) << 6), + ['I'] = ((18 & 0x03) << 6), ['n'] = ((23 & 0x03) << 6), + ['J'] = ((19 & 0x03) << 6), ['o'] = ((24 & 0x03) << 6), + ['K'] = ((20 & 0x03) << 6), ['p'] = ((25 & 0x03) << 6), + ['L'] = ((21 & 0x03) << 6), ['q'] = ((26 & 0x03) << 6), + ['M'] = ((22 & 0x03) << 6), ['r'] = ((27 & 0x03) << 6), + ['N'] = ((23 & 0x03) << 6), ['s'] = ((28 & 0x03) << 6), + ['O'] = ((24 & 0x03) << 6), ['t'] = ((29 & 0x03) << 6), + ['P'] = ((25 & 0x03) << 6), ['u'] = ((30 & 0x03) << 6), + ['Q'] = ((26 & 0x03) << 6), ['v'] = ((31 & 0x03) << 6), +}; + +// 0x1F = 00011111 +const uint8_t third_base32hex_to_num[] = { + ['0'] = (( 0 & 0x1F) << 1), ['R'] = ((27 & 0x1F) << 1), + ['1'] = (( 1 & 0x1F) << 1), ['S'] = ((28 & 0x1F) << 1), + ['2'] = (( 2 & 0x1F) << 1), ['T'] = ((29 & 0x1F) << 1), + ['3'] = (( 3 & 0x1F) << 1), ['U'] = ((30 & 0x1F) << 1), + ['4'] = (( 4 & 0x1F) << 1), ['V'] = ((31 & 0x1F) << 1), + ['5'] = (( 5 & 0x1F) << 1), ['a'] = ((10 & 0x1F) << 1), + ['6'] = (( 6 & 0x1F) << 1), ['b'] = ((11 & 0x1F) << 1), + ['7'] = (( 7 & 0x1F) << 1), ['c'] = ((12 & 0x1F) << 1), + ['8'] = (( 8 & 0x1F) << 1), ['d'] = ((13 & 0x1F) << 1), + ['9'] = (( 9 & 0x1F) << 1), ['e'] = ((14 & 0x1F) << 1), + ['A'] = ((10 & 0x1F) << 1), ['f'] = ((15 & 0x1F) << 1), + ['B'] = ((11 & 0x1F) << 1), ['g'] = ((16 & 0x1F) << 1), + ['C'] = ((12 & 0x1F) << 1), ['h'] = ((17 & 0x1F) << 1), + ['D'] = ((13 & 0x1F) << 1), ['i'] = ((18 & 0x1F) << 1), + ['E'] = ((14 & 0x1F) << 1), ['j'] = ((19 & 0x1F) << 1), + ['F'] = ((15 & 0x1F) << 1), ['k'] = ((20 & 0x1F) << 1), + ['G'] = ((16 & 0x1F) << 1), ['l'] = ((21 & 0x1F) << 1), + ['H'] = ((17 & 0x1F) << 1), ['m'] = ((22 & 0x1F) << 1), + ['I'] = ((18 & 0x1F) << 1), ['n'] = ((23 & 0x1F) << 1), + ['J'] = ((19 & 0x1F) << 1), ['o'] = ((24 & 0x1F) << 1), + ['K'] = ((20 & 0x1F) << 1), ['p'] = ((25 & 0x1F) << 1), + ['L'] = ((21 & 0x1F) << 1), ['q'] = ((26 & 0x1F) << 1), + ['M'] = ((22 & 0x1F) << 1), ['r'] = ((27 & 0x1F) << 1), + ['N'] = ((23 & 0x1F) << 1), ['s'] = ((28 & 0x1F) << 1), + ['O'] = ((24 & 0x1F) << 1), ['t'] = ((29 & 0x1F) << 1), + ['P'] = ((25 & 0x1F) << 1), ['u'] = ((30 & 0x1F) << 1), + ['Q'] = ((26 & 0x1F) << 1), ['v'] = ((31 & 0x1F) << 1), +}; + +// 0x10 = 00010000 +const uint8_t fourth_left_base32hex_to_num[] = { + ['0'] = (( 0 & 0x10) >> 4), ['R'] = ((27 & 0x10) >> 4), + ['1'] = (( 1 & 0x10) >> 4), ['S'] = ((28 & 0x10) >> 4), + ['2'] = (( 2 & 0x10) >> 4), ['T'] = ((29 & 0x10) >> 4), + ['3'] = (( 3 & 0x10) >> 4), ['U'] = ((30 & 0x10) >> 4), + ['4'] = (( 4 & 0x10) >> 4), ['V'] = ((31 & 0x10) >> 4), + ['5'] = (( 5 & 0x10) >> 4), ['a'] = ((10 & 0x10) >> 4), + ['6'] = (( 6 & 0x10) >> 4), ['b'] = ((11 & 0x10) >> 4), + ['7'] = (( 7 & 0x10) >> 4), ['c'] = ((12 & 0x10) >> 4), + ['8'] = (( 8 & 0x10) >> 4), ['d'] = ((13 & 0x10) >> 4), + ['9'] = (( 9 & 0x10) >> 4), ['e'] = ((14 & 0x10) >> 4), + ['A'] = ((10 & 0x10) >> 4), ['f'] = ((15 & 0x10) >> 4), + ['B'] = ((11 & 0x10) >> 4), ['g'] = ((16 & 0x10) >> 4), + ['C'] = ((12 & 0x10) >> 4), ['h'] = ((17 & 0x10) >> 4), + ['D'] = ((13 & 0x10) >> 4), ['i'] = ((18 & 0x10) >> 4), + ['E'] = ((14 & 0x10) >> 4), ['j'] = ((19 & 0x10) >> 4), + ['F'] = ((15 & 0x10) >> 4), ['k'] = ((20 & 0x10) >> 4), + ['G'] = ((16 & 0x10) >> 4), ['l'] = ((21 & 0x10) >> 4), + ['H'] = ((17 & 0x10) >> 4), ['m'] = ((22 & 0x10) >> 4), + ['I'] = ((18 & 0x10) >> 4), ['n'] = ((23 & 0x10) >> 4), + ['J'] = ((19 & 0x10) >> 4), ['o'] = ((24 & 0x10) >> 4), + ['K'] = ((20 & 0x10) >> 4), ['p'] = ((25 & 0x10) >> 4), + ['L'] = ((21 & 0x10) >> 4), ['q'] = ((26 & 0x10) >> 4), + ['M'] = ((22 & 0x10) >> 4), ['r'] = ((27 & 0x10) >> 4), + ['N'] = ((23 & 0x10) >> 4), ['s'] = ((28 & 0x10) >> 4), + ['O'] = ((24 & 0x10) >> 4), ['t'] = ((29 & 0x10) >> 4), + ['P'] = ((25 & 0x10) >> 4), ['u'] = ((30 & 0x10) >> 4), + ['Q'] = ((26 & 0x10) >> 4), ['v'] = ((31 & 0x10) >> 4), +}; + +// 0x0F = 00001111 +const uint8_t fourth_right_base32hex_to_num[] = { + ['0'] = (( 0 & 0x0F) << 4), ['R'] = ((27 & 0x0F) << 4), + ['1'] = (( 1 & 0x0F) << 4), ['S'] = ((28 & 0x0F) << 4), + ['2'] = (( 2 & 0x0F) << 4), ['T'] = ((29 & 0x0F) << 4), + ['3'] = (( 3 & 0x0F) << 4), ['U'] = ((30 & 0x0F) << 4), + ['4'] = (( 4 & 0x0F) << 4), ['V'] = ((31 & 0x0F) << 4), + ['5'] = (( 5 & 0x0F) << 4), ['a'] = ((10 & 0x0F) << 4), + ['6'] = (( 6 & 0x0F) << 4), ['b'] = ((11 & 0x0F) << 4), + ['7'] = (( 7 & 0x0F) << 4), ['c'] = ((12 & 0x0F) << 4), + ['8'] = (( 8 & 0x0F) << 4), ['d'] = ((13 & 0x0F) << 4), + ['9'] = (( 9 & 0x0F) << 4), ['e'] = ((14 & 0x0F) << 4), + ['A'] = ((10 & 0x0F) << 4), ['f'] = ((15 & 0x0F) << 4), + ['B'] = ((11 & 0x0F) << 4), ['g'] = ((16 & 0x0F) << 4), + ['C'] = ((12 & 0x0F) << 4), ['h'] = ((17 & 0x0F) << 4), + ['D'] = ((13 & 0x0F) << 4), ['i'] = ((18 & 0x0F) << 4), + ['E'] = ((14 & 0x0F) << 4), ['j'] = ((19 & 0x0F) << 4), + ['F'] = ((15 & 0x0F) << 4), ['k'] = ((20 & 0x0F) << 4), + ['G'] = ((16 & 0x0F) << 4), ['l'] = ((21 & 0x0F) << 4), + ['H'] = ((17 & 0x0F) << 4), ['m'] = ((22 & 0x0F) << 4), + ['I'] = ((18 & 0x0F) << 4), ['n'] = ((23 & 0x0F) << 4), + ['J'] = ((19 & 0x0F) << 4), ['o'] = ((24 & 0x0F) << 4), + ['K'] = ((20 & 0x0F) << 4), ['p'] = ((25 & 0x0F) << 4), + ['L'] = ((21 & 0x0F) << 4), ['q'] = ((26 & 0x0F) << 4), + ['M'] = ((22 & 0x0F) << 4), ['r'] = ((27 & 0x0F) << 4), + ['N'] = ((23 & 0x0F) << 4), ['s'] = ((28 & 0x0F) << 4), + ['O'] = ((24 & 0x0F) << 4), ['t'] = ((29 & 0x0F) << 4), + ['P'] = ((25 & 0x0F) << 4), ['u'] = ((30 & 0x0F) << 4), + ['Q'] = ((26 & 0x0F) << 4), ['v'] = ((31 & 0x0F) << 4), +}; + +// 0x1E = 00011110 +const uint8_t fifth_left_base32hex_to_num[] = { + ['0'] = (( 0 & 0x1E) >> 1), ['R'] = ((27 & 0x1E) >> 1), + ['1'] = (( 1 & 0x1E) >> 1), ['S'] = ((28 & 0x1E) >> 1), + ['2'] = (( 2 & 0x1E) >> 1), ['T'] = ((29 & 0x1E) >> 1), + ['3'] = (( 3 & 0x1E) >> 1), ['U'] = ((30 & 0x1E) >> 1), + ['4'] = (( 4 & 0x1E) >> 1), ['V'] = ((31 & 0x1E) >> 1), + ['5'] = (( 5 & 0x1E) >> 1), ['a'] = ((10 & 0x1E) >> 1), + ['6'] = (( 6 & 0x1E) >> 1), ['b'] = ((11 & 0x1E) >> 1), + ['7'] = (( 7 & 0x1E) >> 1), ['c'] = ((12 & 0x1E) >> 1), + ['8'] = (( 8 & 0x1E) >> 1), ['d'] = ((13 & 0x1E) >> 1), + ['9'] = (( 9 & 0x1E) >> 1), ['e'] = ((14 & 0x1E) >> 1), + ['A'] = ((10 & 0x1E) >> 1), ['f'] = ((15 & 0x1E) >> 1), + ['B'] = ((11 & 0x1E) >> 1), ['g'] = ((16 & 0x1E) >> 1), + ['C'] = ((12 & 0x1E) >> 1), ['h'] = ((17 & 0x1E) >> 1), + ['D'] = ((13 & 0x1E) >> 1), ['i'] = ((18 & 0x1E) >> 1), + ['E'] = ((14 & 0x1E) >> 1), ['j'] = ((19 & 0x1E) >> 1), + ['F'] = ((15 & 0x1E) >> 1), ['k'] = ((20 & 0x1E) >> 1), + ['G'] = ((16 & 0x1E) >> 1), ['l'] = ((21 & 0x1E) >> 1), + ['H'] = ((17 & 0x1E) >> 1), ['m'] = ((22 & 0x1E) >> 1), + ['I'] = ((18 & 0x1E) >> 1), ['n'] = ((23 & 0x1E) >> 1), + ['J'] = ((19 & 0x1E) >> 1), ['o'] = ((24 & 0x1E) >> 1), + ['K'] = ((20 & 0x1E) >> 1), ['p'] = ((25 & 0x1E) >> 1), + ['L'] = ((21 & 0x1E) >> 1), ['q'] = ((26 & 0x1E) >> 1), + ['M'] = ((22 & 0x1E) >> 1), ['r'] = ((27 & 0x1E) >> 1), + ['N'] = ((23 & 0x1E) >> 1), ['s'] = ((28 & 0x1E) >> 1), + ['O'] = ((24 & 0x1E) >> 1), ['t'] = ((29 & 0x1E) >> 1), + ['P'] = ((25 & 0x1E) >> 1), ['u'] = ((30 & 0x1E) >> 1), + ['Q'] = ((26 & 0x1E) >> 1), ['v'] = ((31 & 0x1E) >> 1), +}; + +// 0x01 = 00000001 +const uint8_t fifth_right_base32hex_to_num[] = { + ['0'] = (( 0 & 0x01) << 7), ['R'] = ((27 & 0x01) << 7), + ['1'] = (( 1 & 0x01) << 7), ['S'] = ((28 & 0x01) << 7), + ['2'] = (( 2 & 0x01) << 7), ['T'] = ((29 & 0x01) << 7), + ['3'] = (( 3 & 0x01) << 7), ['U'] = ((30 & 0x01) << 7), + ['4'] = (( 4 & 0x01) << 7), ['V'] = ((31 & 0x01) << 7), + ['5'] = (( 5 & 0x01) << 7), ['a'] = ((10 & 0x01) << 7), + ['6'] = (( 6 & 0x01) << 7), ['b'] = ((11 & 0x01) << 7), + ['7'] = (( 7 & 0x01) << 7), ['c'] = ((12 & 0x01) << 7), + ['8'] = (( 8 & 0x01) << 7), ['d'] = ((13 & 0x01) << 7), + ['9'] = (( 9 & 0x01) << 7), ['e'] = ((14 & 0x01) << 7), + ['A'] = ((10 & 0x01) << 7), ['f'] = ((15 & 0x01) << 7), + ['B'] = ((11 & 0x01) << 7), ['g'] = ((16 & 0x01) << 7), + ['C'] = ((12 & 0x01) << 7), ['h'] = ((17 & 0x01) << 7), + ['D'] = ((13 & 0x01) << 7), ['i'] = ((18 & 0x01) << 7), + ['E'] = ((14 & 0x01) << 7), ['j'] = ((19 & 0x01) << 7), + ['F'] = ((15 & 0x01) << 7), ['k'] = ((20 & 0x01) << 7), + ['G'] = ((16 & 0x01) << 7), ['l'] = ((21 & 0x01) << 7), + ['H'] = ((17 & 0x01) << 7), ['m'] = ((22 & 0x01) << 7), + ['I'] = ((18 & 0x01) << 7), ['n'] = ((23 & 0x01) << 7), + ['J'] = ((19 & 0x01) << 7), ['o'] = ((24 & 0x01) << 7), + ['K'] = ((20 & 0x01) << 7), ['p'] = ((25 & 0x01) << 7), + ['L'] = ((21 & 0x01) << 7), ['q'] = ((26 & 0x01) << 7), + ['M'] = ((22 & 0x01) << 7), ['r'] = ((27 & 0x01) << 7), + ['N'] = ((23 & 0x01) << 7), ['s'] = ((28 & 0x01) << 7), + ['O'] = ((24 & 0x01) << 7), ['t'] = ((29 & 0x01) << 7), + ['P'] = ((25 & 0x01) << 7), ['u'] = ((30 & 0x01) << 7), + ['Q'] = ((26 & 0x01) << 7), ['v'] = ((31 & 0x01) << 7), +}; + +// 0x1F = 00011111 +const uint8_t sixth_base32hex_to_num[] = { + ['0'] = (( 0 & 0x1F) << 2), ['R'] = ((27 & 0x1F) << 2), + ['1'] = (( 1 & 0x1F) << 2), ['S'] = ((28 & 0x1F) << 2), + ['2'] = (( 2 & 0x1F) << 2), ['T'] = ((29 & 0x1F) << 2), + ['3'] = (( 3 & 0x1F) << 2), ['U'] = ((30 & 0x1F) << 2), + ['4'] = (( 4 & 0x1F) << 2), ['V'] = ((31 & 0x1F) << 2), + ['5'] = (( 5 & 0x1F) << 2), ['a'] = ((10 & 0x1F) << 2), + ['6'] = (( 6 & 0x1F) << 2), ['b'] = ((11 & 0x1F) << 2), + ['7'] = (( 7 & 0x1F) << 2), ['c'] = ((12 & 0x1F) << 2), + ['8'] = (( 8 & 0x1F) << 2), ['d'] = ((13 & 0x1F) << 2), + ['9'] = (( 9 & 0x1F) << 2), ['e'] = ((14 & 0x1F) << 2), + ['A'] = ((10 & 0x1F) << 2), ['f'] = ((15 & 0x1F) << 2), + ['B'] = ((11 & 0x1F) << 2), ['g'] = ((16 & 0x1F) << 2), + ['C'] = ((12 & 0x1F) << 2), ['h'] = ((17 & 0x1F) << 2), + ['D'] = ((13 & 0x1F) << 2), ['i'] = ((18 & 0x1F) << 2), + ['E'] = ((14 & 0x1F) << 2), ['j'] = ((19 & 0x1F) << 2), + ['F'] = ((15 & 0x1F) << 2), ['k'] = ((20 & 0x1F) << 2), + ['G'] = ((16 & 0x1F) << 2), ['l'] = ((21 & 0x1F) << 2), + ['H'] = ((17 & 0x1F) << 2), ['m'] = ((22 & 0x1F) << 2), + ['I'] = ((18 & 0x1F) << 2), ['n'] = ((23 & 0x1F) << 2), + ['J'] = ((19 & 0x1F) << 2), ['o'] = ((24 & 0x1F) << 2), + ['K'] = ((20 & 0x1F) << 2), ['p'] = ((25 & 0x1F) << 2), + ['L'] = ((21 & 0x1F) << 2), ['q'] = ((26 & 0x1F) << 2), + ['M'] = ((22 & 0x1F) << 2), ['r'] = ((27 & 0x1F) << 2), + ['N'] = ((23 & 0x1F) << 2), ['s'] = ((28 & 0x1F) << 2), + ['O'] = ((24 & 0x1F) << 2), ['t'] = ((29 & 0x1F) << 2), + ['P'] = ((25 & 0x1F) << 2), ['u'] = ((30 & 0x1F) << 2), + ['Q'] = ((26 & 0x1F) << 2), ['v'] = ((31 & 0x1F) << 2), +}; + +// 0x18 = 00011000 +const uint8_t seventh_left_base32hex_to_num[] = { + ['0'] = (( 0 & 0x18) >> 3), ['R'] = ((27 & 0x18) >> 3), + ['1'] = (( 1 & 0x18) >> 3), ['S'] = ((28 & 0x18) >> 3), + ['2'] = (( 2 & 0x18) >> 3), ['T'] = ((29 & 0x18) >> 3), + ['3'] = (( 3 & 0x18) >> 3), ['U'] = ((30 & 0x18) >> 3), + ['4'] = (( 4 & 0x18) >> 3), ['V'] = ((31 & 0x18) >> 3), + ['5'] = (( 5 & 0x18) >> 3), ['a'] = ((10 & 0x18) >> 3), + ['6'] = (( 6 & 0x18) >> 3), ['b'] = ((11 & 0x18) >> 3), + ['7'] = (( 7 & 0x18) >> 3), ['c'] = ((12 & 0x18) >> 3), + ['8'] = (( 8 & 0x18) >> 3), ['d'] = ((13 & 0x18) >> 3), + ['9'] = (( 9 & 0x18) >> 3), ['e'] = ((14 & 0x18) >> 3), + ['A'] = ((10 & 0x18) >> 3), ['f'] = ((15 & 0x18) >> 3), + ['B'] = ((11 & 0x18) >> 3), ['g'] = ((16 & 0x18) >> 3), + ['C'] = ((12 & 0x18) >> 3), ['h'] = ((17 & 0x18) >> 3), + ['D'] = ((13 & 0x18) >> 3), ['i'] = ((18 & 0x18) >> 3), + ['E'] = ((14 & 0x18) >> 3), ['j'] = ((19 & 0x18) >> 3), + ['F'] = ((15 & 0x18) >> 3), ['k'] = ((20 & 0x18) >> 3), + ['G'] = ((16 & 0x18) >> 3), ['l'] = ((21 & 0x18) >> 3), + ['H'] = ((17 & 0x18) >> 3), ['m'] = ((22 & 0x18) >> 3), + ['I'] = ((18 & 0x18) >> 3), ['n'] = ((23 & 0x18) >> 3), + ['J'] = ((19 & 0x18) >> 3), ['o'] = ((24 & 0x18) >> 3), + ['K'] = ((20 & 0x18) >> 3), ['p'] = ((25 & 0x18) >> 3), + ['L'] = ((21 & 0x18) >> 3), ['q'] = ((26 & 0x18) >> 3), + ['M'] = ((22 & 0x18) >> 3), ['r'] = ((27 & 0x18) >> 3), + ['N'] = ((23 & 0x18) >> 3), ['s'] = ((28 & 0x18) >> 3), + ['O'] = ((24 & 0x18) >> 3), ['t'] = ((29 & 0x18) >> 3), + ['P'] = ((25 & 0x18) >> 3), ['u'] = ((30 & 0x18) >> 3), + ['Q'] = ((26 & 0x18) >> 3), ['v'] = ((31 & 0x18) >> 3), +}; + +// 0x07 = 00000111 +const uint8_t seventh_right_base32hex_to_num[] = { + ['0'] = (( 0 & 0x07) << 5), ['R'] = ((27 & 0x07) << 5), + ['1'] = (( 1 & 0x07) << 5), ['S'] = ((28 & 0x07) << 5), + ['2'] = (( 2 & 0x07) << 5), ['T'] = ((29 & 0x07) << 5), + ['3'] = (( 3 & 0x07) << 5), ['U'] = ((30 & 0x07) << 5), + ['4'] = (( 4 & 0x07) << 5), ['V'] = ((31 & 0x07) << 5), + ['5'] = (( 5 & 0x07) << 5), ['a'] = ((10 & 0x07) << 5), + ['6'] = (( 6 & 0x07) << 5), ['b'] = ((11 & 0x07) << 5), + ['7'] = (( 7 & 0x07) << 5), ['c'] = ((12 & 0x07) << 5), + ['8'] = (( 8 & 0x07) << 5), ['d'] = ((13 & 0x07) << 5), + ['9'] = (( 9 & 0x07) << 5), ['e'] = ((14 & 0x07) << 5), + ['A'] = ((10 & 0x07) << 5), ['f'] = ((15 & 0x07) << 5), + ['B'] = ((11 & 0x07) << 5), ['g'] = ((16 & 0x07) << 5), + ['C'] = ((12 & 0x07) << 5), ['h'] = ((17 & 0x07) << 5), + ['D'] = ((13 & 0x07) << 5), ['i'] = ((18 & 0x07) << 5), + ['E'] = ((14 & 0x07) << 5), ['j'] = ((19 & 0x07) << 5), + ['F'] = ((15 & 0x07) << 5), ['k'] = ((20 & 0x07) << 5), + ['G'] = ((16 & 0x07) << 5), ['l'] = ((21 & 0x07) << 5), + ['H'] = ((17 & 0x07) << 5), ['m'] = ((22 & 0x07) << 5), + ['I'] = ((18 & 0x07) << 5), ['n'] = ((23 & 0x07) << 5), + ['J'] = ((19 & 0x07) << 5), ['o'] = ((24 & 0x07) << 5), + ['K'] = ((20 & 0x07) << 5), ['p'] = ((25 & 0x07) << 5), + ['L'] = ((21 & 0x07) << 5), ['q'] = ((26 & 0x07) << 5), + ['M'] = ((22 & 0x07) << 5), ['r'] = ((27 & 0x07) << 5), + ['N'] = ((23 & 0x07) << 5), ['s'] = ((28 & 0x07) << 5), + ['O'] = ((24 & 0x07) << 5), ['t'] = ((29 & 0x07) << 5), + ['P'] = ((25 & 0x07) << 5), ['u'] = ((30 & 0x07) << 5), + ['Q'] = ((26 & 0x07) << 5), ['v'] = ((31 & 0x07) << 5), +}; + +// 0x1F = 00011111 +const uint8_t eighth_base32hex_to_num[] = { + ['0'] = (( 0 & 0x1F) << 0), ['R'] = ((27 & 0x1F) << 0), + ['1'] = (( 1 & 0x1F) << 0), ['S'] = ((28 & 0x1F) << 0), + ['2'] = (( 2 & 0x1F) << 0), ['T'] = ((29 & 0x1F) << 0), + ['3'] = (( 3 & 0x1F) << 0), ['U'] = ((30 & 0x1F) << 0), + ['4'] = (( 4 & 0x1F) << 0), ['V'] = ((31 & 0x1F) << 0), + ['5'] = (( 5 & 0x1F) << 0), ['a'] = ((10 & 0x1F) << 0), + ['6'] = (( 6 & 0x1F) << 0), ['b'] = ((11 & 0x1F) << 0), + ['7'] = (( 7 & 0x1F) << 0), ['c'] = ((12 & 0x1F) << 0), + ['8'] = (( 8 & 0x1F) << 0), ['d'] = ((13 & 0x1F) << 0), + ['9'] = (( 9 & 0x1F) << 0), ['e'] = ((14 & 0x1F) << 0), + ['A'] = ((10 & 0x1F) << 0), ['f'] = ((15 & 0x1F) << 0), + ['B'] = ((11 & 0x1F) << 0), ['g'] = ((16 & 0x1F) << 0), + ['C'] = ((12 & 0x1F) << 0), ['h'] = ((17 & 0x1F) << 0), + ['D'] = ((13 & 0x1F) << 0), ['i'] = ((18 & 0x1F) << 0), + ['E'] = ((14 & 0x1F) << 0), ['j'] = ((19 & 0x1F) << 0), + ['F'] = ((15 & 0x1F) << 0), ['k'] = ((20 & 0x1F) << 0), + ['G'] = ((16 & 0x1F) << 0), ['l'] = ((21 & 0x1F) << 0), + ['H'] = ((17 & 0x1F) << 0), ['m'] = ((22 & 0x1F) << 0), + ['I'] = ((18 & 0x1F) << 0), ['n'] = ((23 & 0x1F) << 0), + ['J'] = ((19 & 0x1F) << 0), ['o'] = ((24 & 0x1F) << 0), + ['K'] = ((20 & 0x1F) << 0), ['p'] = ((25 & 0x1F) << 0), + ['L'] = ((21 & 0x1F) << 0), ['q'] = ((26 & 0x1F) << 0), + ['M'] = ((22 & 0x1F) << 0), ['r'] = ((27 & 0x1F) << 0), + ['N'] = ((23 & 0x1F) << 0), ['s'] = ((28 & 0x1F) << 0), + ['O'] = ((24 & 0x1F) << 0), ['t'] = ((29 & 0x1F) << 0), + ['P'] = ((25 & 0x1F) << 0), ['u'] = ((30 & 0x1F) << 0), + ['Q'] = ((26 & 0x1F) << 0), ['v'] = ((31 & 0x1F) << 0), +}; + +// Without leap day 29. 2. +static const uint8_t days_in_months[] = { + [ 1] = 31, [ 2] = 28, [ 3] = 31, [ 4] = 30, [ 5] = 31, [ 6] = 30, + [ 7] = 31, [ 8] = 31, [ 9] = 30, [10] = 31, [11] = 30, [12] = 31, +}; + +// Without leap day 29. 2. +static const uint16_t days_across_months[] = { + [ 1] = 0, [ 2] = 31, [ 3] = 59, [ 4] = 90, [ 5] = 120, [ 6] = 151, + [ 7] = 181, [ 8] = 212, [ 9] = 243, [10] = 273, [11] = 304, [12] = 334, +}; + +// 0 ~ 1970 ... 135 ~ 2105 +static const uint8_t is_leap_year[] = { + [ 1] = 0, [ 2] = 1, [ 3] = 0, [ 4] = 0, [ 5] = 0, + [ 6] = 1, [ 7] = 0, [ 8] = 0, [ 9] = 0, [ 10] = 1, + [ 11] = 0, [ 12] = 0, [ 13] = 0, [ 14] = 1, [ 15] = 0, + [ 16] = 0, [ 17] = 0, [ 18] = 1, [ 19] = 0, [ 20] = 0, + [ 21] = 0, [ 22] = 1, [ 23] = 0, [ 24] = 0, [ 25] = 0, + [ 26] = 1, [ 27] = 0, [ 28] = 0, [ 29] = 0, [ 30] = 1, + [ 31] = 0, [ 32] = 0, [ 33] = 0, [ 34] = 1, [ 35] = 0, + [ 36] = 0, [ 37] = 0, [ 38] = 1, [ 39] = 0, [ 40] = 0, + [ 41] = 0, [ 42] = 1, [ 43] = 0, [ 44] = 0, [ 45] = 0, + [ 46] = 1, [ 47] = 0, [ 48] = 0, [ 49] = 0, [ 50] = 1, + [ 51] = 0, [ 52] = 0, [ 53] = 0, [ 54] = 1, [ 55] = 0, + [ 56] = 0, [ 57] = 0, [ 58] = 1, [ 59] = 0, [ 60] = 0, + [ 61] = 0, [ 62] = 1, [ 63] = 0, [ 64] = 0, [ 65] = 0, + [ 66] = 1, [ 67] = 0, [ 68] = 0, [ 69] = 0, [ 70] = 1, + [ 71] = 0, [ 72] = 0, [ 73] = 0, [ 74] = 1, [ 75] = 0, + [ 76] = 0, [ 77] = 0, [ 78] = 1, [ 79] = 0, [ 80] = 0, + [ 81] = 0, [ 82] = 1, [ 83] = 0, [ 84] = 0, [ 85] = 0, + [ 86] = 1, [ 87] = 0, [ 88] = 0, [ 89] = 0, [ 90] = 1, + [ 91] = 0, [ 92] = 0, [ 93] = 0, [ 94] = 1, [ 95] = 0, + [ 96] = 0, [ 97] = 0, [ 98] = 1, [ 99] = 0, [100] = 0, + [101] = 0, [102] = 1, [103] = 0, [104] = 0, [105] = 0, + [106] = 1, [107] = 0, [108] = 0, [109] = 0, [110] = 1, + [111] = 0, [112] = 0, [113] = 0, [114] = 1, [115] = 0, + [116] = 0, [117] = 0, [118] = 1, [119] = 0, [120] = 0, + [121] = 0, [122] = 1, [123] = 0, [124] = 0, [125] = 0, + [126] = 1, [127] = 0, [128] = 0, [129] = 0, [130] = 0, + [131] = 0, [132] = 0, [133] = 0, [134] = 1, [135] = 0, +}; + +// 0 ~ 1970 ... 135 ~ 2105 +static const uint16_t days_across_years[] = { + [ 1] = 365, [ 2] = 730, [ 3] = 1096, [ 4] = 1461, [ 5] = 1826, + [ 6] = 2191, [ 7] = 2557, [ 8] = 2922, [ 9] = 3287, [ 10] = 3652, + [ 11] = 4018, [ 12] = 4383, [ 13] = 4748, [ 14] = 5113, [ 15] = 5479, + [ 16] = 5844, [ 17] = 6209, [ 18] = 6574, [ 19] = 6940, [ 20] = 7305, + [ 21] = 7670, [ 22] = 8035, [ 23] = 8401, [ 24] = 8766, [ 25] = 9131, + [ 26] = 9496, [ 27] = 9862, [ 28] = 10227, [ 29] = 10592, [ 30] = 10957, + [ 31] = 11323, [ 32] = 11688, [ 33] = 12053, [ 34] = 12418, [ 35] = 12784, + [ 36] = 13149, [ 37] = 13514, [ 38] = 13879, [ 39] = 14245, [ 40] = 14610, + [ 41] = 14975, [ 42] = 15340, [ 43] = 15706, [ 44] = 16071, [ 45] = 16436, + [ 46] = 16801, [ 47] = 17167, [ 48] = 17532, [ 49] = 17897, [ 50] = 18262, + [ 51] = 18628, [ 52] = 18993, [ 53] = 19358, [ 54] = 19723, [ 55] = 20089, + [ 56] = 20454, [ 57] = 20819, [ 58] = 21184, [ 59] = 21550, [ 60] = 21915, + [ 61] = 22280, [ 62] = 22645, [ 63] = 23011, [ 64] = 23376, [ 65] = 23741, + [ 66] = 24106, [ 67] = 24472, [ 68] = 24837, [ 69] = 25202, [ 70] = 25567, + [ 71] = 25933, [ 72] = 26298, [ 73] = 26663, [ 74] = 27028, [ 75] = 27394, + [ 76] = 27759, [ 77] = 28124, [ 78] = 28489, [ 79] = 28855, [ 80] = 29220, + [ 81] = 29585, [ 82] = 29950, [ 83] = 30316, [ 84] = 30681, [ 85] = 31046, + [ 86] = 31411, [ 87] = 31777, [ 88] = 32142, [ 89] = 32507, [ 90] = 32872, + [ 91] = 33238, [ 92] = 33603, [ 93] = 33968, [ 94] = 34333, [ 95] = 34699, + [ 96] = 35064, [ 97] = 35429, [ 98] = 35794, [ 99] = 36160, [100] = 36525, + [101] = 36890, [102] = 37255, [103] = 37621, [104] = 37986, [105] = 38351, + [106] = 38716, [107] = 39082, [108] = 39447, [109] = 39812, [110] = 40177, + [111] = 40543, [112] = 40908, [113] = 41273, [114] = 41638, [115] = 42004, + [116] = 42369, [117] = 42734, [118] = 43099, [119] = 43465, [120] = 43830, + [121] = 44195, [122] = 44560, [123] = 44926, [124] = 45291, [125] = 45656, + [126] = 46021, [127] = 46387, [128] = 46752, [129] = 47117, [130] = 47482, + [131] = 47847, [132] = 48212, [133] = 48577, [134] = 48942, [135] = 49308, +}; + +int date_to_timestamp(uint8_t *buff, uint32_t *timestamp) +{ + uint32_t year, month, day, hour, minute, second; + uint32_t leap_day = 0; + + year = 1000 * (buff[ 0] - '0') + 100 * (buff[ 1] - '0') + + 10 * (buff[ 2] - '0') + (buff[ 3] - '0'); + month = 10 * (buff[ 4] - '0') + (buff[ 5] - '0'); + day = 10 * (buff[ 6] - '0') + (buff[ 7] - '0'); + hour = 10 * (buff[ 8] - '0') + (buff[ 9] - '0'); + minute = 10 * (buff[10] - '0') + (buff[11] - '0'); + second = 10 * (buff[12] - '0') + (buff[13] - '0'); + + if (year < 1970 || year > 2105 || month < 1 || month > 12 || day < 1) { + return ZS_BAD_DATE; + } else { + year -= 1970; + } + + if (is_leap_year[year]) { + if (month > 2) { + leap_day = 1; // Add one day in case of leap year. + } else if (month == 2 && + day > (uint32_t)(days_in_months[month] + 1)) { + return ZS_BAD_DATE; + } + } else if (day > days_in_months[month]){ + return ZS_BAD_DATE; + } + + if (hour > 23 || minute > 59 || second > 59) { + return ZS_BAD_TIME; + } + + *timestamp = hour * 3600 + minute * 60 + second + + (days_across_years[year] + + days_across_months[month] + + day - 1 + leap_day) * 86400; + + return ZS_OK; +} + +void wire_dname_to_str(const uint8_t *data, + const uint32_t data_len, + char *text) +{ + uint32_t i = 0, text_len = 0; + + if (data == NULL || data_len == 0 || text == NULL) { + return; + } + + uint8_t label_len = data[0]; + + // Loop over data characters. + for (i = 1; i < data_len; i++) { + // Replace label length with dot. + if (label_len == 0) { + label_len = data[i]; + text[text_len++] = '.'; + continue; + } + + // Just in case use \123 notation. + text[text_len++] = '\\'; + text[text_len++] = (data[i] / 100) + '0'; + text[text_len++] = (data[i] / 10) % 10 + '0'; + text[text_len++] = (data[i] ) % 10 + '0'; + + label_len--; + } + + // Add trailing dot for root domain. + if (data_len == 1 && label_len == 0) { + text[text_len++] = '.'; + } + + // Ending text string. + text[text_len] = 0; +} + +uint8_t loc64to8(uint64_t number) +{ + uint8_t exponent = 0; + + while (number > 9) { + number /= 10; + exponent++; + } + // First 4 bits are mantisa, second 4 bits are exponent. + return ((uint8_t)number << 4) + (exponent & 15); +} diff --git a/src/libzscanner/functions.h b/src/libzscanner/functions.h new file mode 100644 index 0000000..ba1bc16 --- /dev/null +++ b/src/libzscanner/functions.h @@ -0,0 +1,110 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \file + * + * \brief Zone scanner auxiliary functions. + * + * \addtogroup zone_scanner + * @{ + */ + +#pragma once + +#include <stdint.h> + +/*! \brief Transforms digit char to number. */ +extern const uint8_t digit_to_num[]; + +/*! \brief Transforms first hex char to the part of the total number. */ +extern const uint8_t first_hex_to_num[]; +/*! \brief Transforms second hex char to the part of the total number. */ +extern const uint8_t second_hex_to_num[]; + +/*! \brief Transforms first Base64 char. */ +extern const uint8_t first_base64_to_num[]; +/*! \brief Transforms left part of the second Base64 char. */ +extern const uint8_t second_left_base64_to_num[]; +/*! \brief Transforms left part of the second Base64 char. */ +extern const uint8_t second_right_base64_to_num[]; +/*! \brief Transforms left part of the third Base64 char. */ +extern const uint8_t third_left_base64_to_num[]; +/*! \brief Transforms left part of the third Base64 char. */ +extern const uint8_t third_right_base64_to_num[]; +/*! \brief Transforms fourth Base64 char. */ +extern const uint8_t fourth_base64_to_num[]; + +/*! \brief Transforms first Base32hex char. */ +extern const uint8_t first_base32hex_to_num[]; +/*! \brief Transforms left part of the second Base32hex char. */ +extern const uint8_t second_left_base32hex_to_num[]; +/*! \brief Transforms right part of the second Base32hex char. */ +extern const uint8_t second_right_base32hex_to_num[]; +/*! \brief Transforms third Base32hex char. */ +extern const uint8_t third_base32hex_to_num[]; +/*! \brief Transforms left part of the fourth Base32hex char. */ +extern const uint8_t fourth_left_base32hex_to_num[]; +/*! \brief Transforms right part of the fourth Base32hex char. */ +extern const uint8_t fourth_right_base32hex_to_num[]; +/*! \brief Transforms left part of the fifth Base32hex char. */ +extern const uint8_t fifth_left_base32hex_to_num[]; +/*! \brief Transforms right part of the fifth Base32hex char. */ +extern const uint8_t fifth_right_base32hex_to_num[]; +/*! \brief Transforms sixth Base32hex char. */ +extern const uint8_t sixth_base32hex_to_num[]; +/*! \brief Transforms left part of the seventh Base32hex char. */ +extern const uint8_t seventh_left_base32hex_to_num[]; +/*! \brief Transforms right part of the seventh Base32hex char. */ +extern const uint8_t seventh_right_base32hex_to_num[]; +/*! \brief Transforms eighth Base32hex char. */ +extern const uint8_t eighth_base32hex_to_num[]; + +/*! + * \brief Converts YYYYMMDDHHMMSS time string to unsigned 32-bit timestamp. + * + * \param buff Buffer containing time string. + * \param timestamp Computed timestamp. + * + * \retval KNOT_EOK if success. + * \retval error_code if error. + */ +int date_to_timestamp(uint8_t *buff, uint32_t *timestamp); + +/*! + * \brief Converts wire-format dname to text dname. + * + * \param data Buffer containg wire-format dname. + * \param data_len Length of the buffer. + * \param text Text output. + */ +void wire_dname_to_str(const uint8_t *data, + const uint32_t data_len, + char *text); + +/*! + * \brief Converts unsigned integer to mantisa*10^(exponent). + * + * Given number is encoded as two 4-bit numbers. First part is mantisa [0-9], + * second part is decimal exponent [0-15]. Result is concatenation of these + * two blocks. + * + * \param number Number to convert. + * + * \retval number encoded number. + */ +uint8_t loc64to8(uint64_t number); + +/*! @} */ diff --git a/src/libzscanner/scanner.c.g2 b/src/libzscanner/scanner.c.g2 new file mode 100644 index 0000000..ad1d774 --- /dev/null +++ b/src/libzscanner/scanner.c.g2 @@ -0,0 +1,85519 @@ + +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <arpa/inet.h> +#include <fcntl.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <libgen.h> +#include <math.h> +#include <netinet/in.h> +#include <sys/socket.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include "libzscanner/scanner.h" +#include "libzscanner/functions.h" +#include "libknot/descriptor.h" + +/*! \brief Maximal length of rdata item. */ +#define MAX_ITEM_LENGTH 255 + +/*! \brief Latitude value for equator (2^31). */ +#define LOC_LAT_ZERO (uint32_t)2147483648 +/*! \brief Longitude value for meridian (2^31). */ +#define LOC_LONG_ZERO (uint32_t)2147483648 +/*! \brief Zero level altitude value. */ +#define LOC_ALT_ZERO (uint32_t)10000000 + +/*! \brief Shorthand for setting warning data. */ +#define WARN(err_code) { s->error.code = err_code; } +/*! \brief Shorthand for setting error data. */ +#define ERR(err_code) { WARN(err_code); s->error.fatal = true; } +/*! \brief Shorthand for error reset. */ +#define NOERR { WARN(ZS_OK); s->error.fatal = false; } + +/*! + * \brief Writes record type number to r_data. + * + * \param type Type number. + * \param rdata_tail Position where to write type number to. + */ +static inline void type_num(const uint16_t type, uint8_t **rdata_tail) +{ + *((uint16_t *)*rdata_tail) = htons(type); + *rdata_tail += 2; +} + +/*! + * \brief Sets bit to bitmap window. + * + * \param type Type number. + * \param s Scanner context. + */ +static inline void window_add_bit(const uint16_t type, zs_scanner_t *s) { + uint8_t win = type / 256; + uint8_t bit_pos = type % 256; + uint8_t byte_pos = bit_pos / 8; + + ((s->windows[win]).bitmap)[byte_pos] |= 128 >> (bit_pos % 8); + + if ((s->windows[win]).length < byte_pos + 1) { + (s->windows[win]).length = byte_pos + 1; + } + + if (s->last_window < win) { + s->last_window = win; + } +} + +// Include scanner file (in Ragel). + + + + + + +__attribute__((visibility("default"))) +int zs_init( + zs_scanner_t *s, + const char *origin, + const uint16_t rclass, + const uint32_t ttl) +{ + if (s == NULL) { + return -1; + } + + memset(s, 0, sizeof(*s)); + + // Nonzero initial scanner state. + s->cs = 1127; + + // Reset the file descriptor. + s->file.descriptor = -1; + + // Use the root zone as origin if not specified. + if (origin == NULL || strlen(origin) == 0) { + origin = "."; + } + size_t origin_len = strlen(origin); + + // Prepare a zone settings header. + const char *format; + if (origin[origin_len - 1] != '.') { + format = "$ORIGIN %s.\n"; + } else { + format = "$ORIGIN %s\n"; + } + + char settings[1024]; + int ret = snprintf(settings, sizeof(settings), format, origin); + if (ret <= 0 || ret >= sizeof(settings)) { + ERR(ZS_ENOMEM); + return -1; + } + + // Parse the settings to set up the scanner origin. + if (zs_set_input_string(s, settings, ret) != 0 || + zs_parse_all(s) != 0) { + return -1; + } + + // Set scanner defaults. + s->path = strdup("."); + if (s->path == NULL) { + ERR(ZS_ENOMEM); + return -1; + } + s->default_class = rclass; + s->default_ttl = ttl; + s->line_counter = 1; + + s->state = ZS_STATE_NONE; + s->process.automatic = false; + + return 0; +} + +static void input_deinit( + zs_scanner_t *s, + bool keep_filename) +{ + // Deinit the file input. + if (s->file.descriptor != -1) { + // Unmap the file content. + if (s->input.start != NULL) { + if (s->input.mmaped) { + munmap((void *)s->input.start, + s->input.end - s->input.start); + } else { + free((void *)s->input.start); + } + } + + // Close the opened file. + close(s->file.descriptor); + s->file.descriptor = -1; + } + + // Keep file name for possible trailing error report. + if (!keep_filename) { + free(s->file.name); + s->file.name = NULL; + } + + // Unset the input limits. + s->input.start = NULL; + s->input.current = NULL; + s->input.end = NULL; + s->input.eof = false; +} + +__attribute__((visibility("default"))) +void zs_deinit( + zs_scanner_t *s) +{ + if (s == NULL) { + return; + } + + input_deinit(s, false); + free(s->path); +} + +static int set_input_string( + zs_scanner_t *s, + const char *input, + size_t size, + bool final_block) +{ + if (s == NULL) { + return -1; + } + + if (input == NULL) { + ERR(ZS_EINVAL); + return -1; + } + + // Deinit possibly opened file. + input_deinit(s, final_block); + + // Set the scanner input limits. + s->input.start = input; + s->input.current = input; + s->input.end = input + size; + s->input.eof = final_block; + + return 0; +} + +static char *read_file_to_buf( + int fd, + size_t *bufsize) +{ + size_t bufs = 0, newbufs = 8192; + char *buf = malloc(bufs + newbufs); + int ret = 0; + + while (buf != NULL && (ret = read(fd, buf + bufs, newbufs)) == newbufs) { + bufs += newbufs; + newbufs = bufs; + char *newbuf = realloc(buf, bufs + newbufs); + if (newbuf == NULL) { + free(buf); + } + buf = newbuf; + } + if (ret < 0) { + free(buf); + return NULL; + } + + *bufsize = bufs + ret; + return buf; +} + +__attribute__((visibility("default"))) +int zs_set_input_string( + zs_scanner_t *s, + const char *input, + size_t size) +{ + s->state = ZS_STATE_NONE; + + return set_input_string(s, input, size, false); +} + +__attribute__((visibility("default"))) +int zs_set_input_file( + zs_scanner_t *s, + const char *file_name) +{ + if (s == NULL) { + return -1; + } + + if (file_name == NULL) { + ERR(ZS_EINVAL); + return -1; + } + + // Deinit possibly opened file. + input_deinit(s, false); + + // Try to open the file. + s->file.descriptor = open(file_name, O_RDONLY); + if (s->file.descriptor == -1) { + ERR(ZS_FILE_OPEN); + return -1; + } + + char *start = NULL; + size_t size = 0; + + // Check the input. + struct stat file_stat; + if (fstat(s->file.descriptor, &file_stat) == -1) { + ERR(ZS_FILE_INVALID); + input_deinit(s, false); + return -1; + } else if (S_ISCHR(file_stat.st_mode) || + S_ISBLK(file_stat.st_mode) || + S_ISFIFO(file_stat.st_mode)) { + // Workaround if cannot mmap, read to memory. + start = read_file_to_buf(s->file.descriptor, &size); + if (start == NULL) { + ERR(ZS_FILE_INVALID); + input_deinit(s, false); + return -1; + } + } else if (!S_ISREG(file_stat.st_mode)) { // Require regular file. + ERR(ZS_FILE_INVALID); + input_deinit(s, false); + return -1; + } else if (file_stat.st_size > 0) { // Mmap non-emtpy file. + start = mmap(0, file_stat.st_size, PROT_READ, MAP_SHARED, + s->file.descriptor, 0); + if (start == MAP_FAILED) { + ERR(ZS_FILE_INVALID); + input_deinit(s, false); + return -1; + } + + size = file_stat.st_size; + s->input.mmaped = true; + + // Try to set the mapped memory advise to sequential. + (void)madvise(start, size, MADV_SEQUENTIAL); + } + + // Set the scanner input limits. + s->input.start = start; + s->input.current = start; + s->input.end = start + size; + + // Get absolute path of the zone file if possible. + char *full_name = realpath(file_name, NULL); + if (full_name != NULL) { + free(s->path); + s->path = strdup(dirname(full_name)); + free(full_name); + if (s->path == NULL) { + ERR(ZS_ENOMEM); + input_deinit(s, false); + return -1; + } + } + + s->file.name = strdup(file_name); + if (s->file.name == NULL) { + ERR(ZS_ENOMEM); + input_deinit(s, false); + return -1; + } + + s->state = ZS_STATE_NONE; + + return 0; +} + +__attribute__((visibility("default"))) +int zs_set_processing( + zs_scanner_t *s, + void (*process_record)(zs_scanner_t *), + void (*process_error)(zs_scanner_t *), + void *data) +{ + if (s == NULL) { + return -1; + } + + s->process.record = process_record; + s->process.error = process_error; + s->process.data = data; + + return 0; +} + +typedef enum { + WRAP_NONE, // Initial state. + WRAP_DETECTED, // Input block end is a first '\' in rdata. + WRAP_PROCESS // Parsing of auxiliary block = "\". +} wrap_t; + +static void parse( + zs_scanner_t *s, + wrap_t *wrap) +{ + // Restore scanner input limits (Ragel internals). + const char *p = s->input.current; + const char *pe = s->input.end; + const char *eof = s->input.eof ? pe : NULL; + + // Restore state variables (Ragel internals). + int cs = s->cs; + int top = s->top; + int stack[ZS_RAGEL_STACK_SIZE]; + memcpy(stack, s->stack, sizeof(stack)); + + // Next 2 variables are for better performance. + // Restoring r_data pointer to next free space. + uint8_t *rdata_tail = s->r_data + s->r_data_tail; + // Initialization of the last r_data byte. + uint8_t *rdata_stop = s->r_data + ZS_MAX_RDATA_LENGTH - 1; + + // Write scanner body (in C). + + { + short _widec; + if ( p == pe ) + goto _test_eof; + goto _resume; + +_again: + switch ( cs ) { + case 1127: goto st1127; + case 0: goto st0; + case 1: goto st1; + case 2: goto st2; + case 3: goto st3; + case 4: goto st4; + case 5: goto st5; + case 6: goto st6; + case 7: goto st7; + case 8: goto st8; + case 9: goto st9; + case 10: goto st10; + case 11: goto st11; + case 12: goto st12; + case 13: goto st13; + case 1128: goto st1128; + case 14: goto st14; + case 15: goto st15; + case 16: goto st16; + case 17: goto st17; + case 18: goto st18; + case 19: goto st19; + case 20: goto st20; + case 21: goto st21; + case 22: goto st22; + case 23: goto st23; + case 24: goto st24; + case 25: goto st25; + case 26: goto st26; + case 27: goto st27; + case 28: goto st28; + case 29: goto st29; + case 30: goto st30; + case 31: goto st31; + case 32: goto st32; + case 33: goto st33; + case 34: goto st34; + case 35: goto st35; + case 36: goto st36; + case 37: goto st37; + case 38: goto st38; + case 39: goto st39; + case 40: goto st40; + case 41: goto st41; + case 42: goto st42; + case 43: goto st43; + case 44: goto st44; + case 45: goto st45; + case 46: goto st46; + case 47: goto st47; + case 48: goto st48; + case 49: goto st49; + case 50: goto st50; + case 51: goto st51; + case 52: goto st52; + case 53: goto st53; + case 54: goto st54; + case 55: goto st55; + case 56: goto st56; + case 57: goto st57; + case 58: goto st58; + case 59: goto st59; + case 60: goto st60; + case 61: goto st61; + case 62: goto st62; + case 63: goto st63; + case 64: goto st64; + case 65: goto st65; + case 66: goto st66; + case 67: goto st67; + case 68: goto st68; + case 69: goto st69; + case 70: goto st70; + case 71: goto st71; + case 72: goto st72; + case 73: goto st73; + case 74: goto st74; + case 75: goto st75; + case 76: goto st76; + case 77: goto st77; + case 78: goto st78; + case 79: goto st79; + case 80: goto st80; + case 81: goto st81; + case 82: goto st82; + case 83: goto st83; + case 84: goto st84; + case 85: goto st85; + case 86: goto st86; + case 87: goto st87; + case 88: goto st88; + case 89: goto st89; + case 90: goto st90; + case 91: goto st91; + case 92: goto st92; + case 93: goto st93; + case 94: goto st94; + case 95: goto st95; + case 96: goto st96; + case 97: goto st97; + case 98: goto st98; + case 99: goto st99; + case 100: goto st100; + case 101: goto st101; + case 102: goto st102; + case 103: goto st103; + case 104: goto st104; + case 105: goto st105; + case 106: goto st106; + case 107: goto st107; + case 108: goto st108; + case 109: goto st109; + case 110: goto st110; + case 111: goto st111; + case 112: goto st112; + case 113: goto st113; + case 114: goto st114; + case 115: goto st115; + case 116: goto st116; + case 117: goto st117; + case 118: goto st118; + case 119: goto st119; + case 120: goto st120; + case 121: goto st121; + case 122: goto st122; + case 123: goto st123; + case 124: goto st124; + case 125: goto st125; + case 126: goto st126; + case 127: goto st127; + case 128: goto st128; + case 129: goto st129; + case 130: goto st130; + case 131: goto st131; + case 132: goto st132; + case 133: goto st133; + case 134: goto st134; + case 135: goto st135; + case 136: goto st136; + case 137: goto st137; + case 138: goto st138; + case 139: goto st139; + case 140: goto st140; + case 141: goto st141; + case 1129: goto st1129; + case 142: goto st142; + case 143: goto st143; + case 144: goto st144; + case 145: goto st145; + case 146: goto st146; + case 147: goto st147; + case 148: goto st148; + case 149: goto st149; + case 150: goto st150; + case 151: goto st151; + case 1130: goto st1130; + case 152: goto st152; + case 153: goto st153; + case 154: goto st154; + case 155: goto st155; + case 156: goto st156; + case 157: goto st157; + case 158: goto st158; + case 159: goto st159; + case 1131: goto st1131; + case 160: goto st160; + case 161: goto st161; + case 162: goto st162; + case 1132: goto st1132; + case 163: goto st163; + case 164: goto st164; + case 165: goto st165; + case 166: goto st166; + case 167: goto st167; + case 168: goto st168; + case 169: goto st169; + case 170: goto st170; + case 171: goto st171; + case 172: goto st172; + case 173: goto st173; + case 1133: goto st1133; + case 174: goto st174; + case 175: goto st175; + case 176: goto st176; + case 177: goto st177; + case 1134: goto st1134; + case 178: goto st178; + case 179: goto st179; + case 180: goto st180; + case 181: goto st181; + case 182: goto st182; + case 183: goto st183; + case 184: goto st184; + case 185: goto st185; + case 186: goto st186; + case 187: goto st187; + case 188: goto st188; + case 189: goto st189; + case 190: goto st190; + case 191: goto st191; + case 192: goto st192; + case 193: goto st193; + case 1135: goto st1135; + case 194: goto st194; + case 195: goto st195; + case 196: goto st196; + case 197: goto st197; + case 198: goto st198; + case 199: goto st199; + case 200: goto st200; + case 201: goto st201; + case 202: goto st202; + case 203: goto st203; + case 204: goto st204; + case 205: goto st205; + case 206: goto st206; + case 207: goto st207; + case 208: goto st208; + case 209: goto st209; + case 1136: goto st1136; + case 210: goto st210; + case 211: goto st211; + case 212: goto st212; + case 213: goto st213; + case 214: goto st214; + case 215: goto st215; + case 216: goto st216; + case 217: goto st217; + case 218: goto st218; + case 219: goto st219; + case 220: goto st220; + case 221: goto st221; + case 222: goto st222; + case 223: goto st223; + case 224: goto st224; + case 225: goto st225; + case 226: goto st226; + case 227: goto st227; + case 228: goto st228; + case 229: goto st229; + case 230: goto st230; + case 231: goto st231; + case 232: goto st232; + case 233: goto st233; + case 234: goto st234; + case 235: goto st235; + case 236: goto st236; + case 237: goto st237; + case 238: goto st238; + case 239: goto st239; + case 240: goto st240; + case 241: goto st241; + case 242: goto st242; + case 243: goto st243; + case 244: goto st244; + case 245: goto st245; + case 246: goto st246; + case 247: goto st247; + case 248: goto st248; + case 249: goto st249; + case 250: goto st250; + case 251: goto st251; + case 252: goto st252; + case 253: goto st253; + case 254: goto st254; + case 255: goto st255; + case 256: goto st256; + case 257: goto st257; + case 258: goto st258; + case 259: goto st259; + case 260: goto st260; + case 261: goto st261; + case 262: goto st262; + case 263: goto st263; + case 264: goto st264; + case 265: goto st265; + case 266: goto st266; + case 267: goto st267; + case 268: goto st268; + case 269: goto st269; + case 1137: goto st1137; + case 270: goto st270; + case 271: goto st271; + case 1138: goto st1138; + case 272: goto st272; + case 273: goto st273; + case 274: goto st274; + case 275: goto st275; + case 276: goto st276; + case 277: goto st277; + case 278: goto st278; + case 279: goto st279; + case 280: goto st280; + case 1139: goto st1139; + case 1140: goto st1140; + case 281: goto st281; + case 282: goto st282; + case 283: goto st283; + case 284: goto st284; + case 285: goto st285; + case 286: goto st286; + case 287: goto st287; + case 288: goto st288; + case 289: goto st289; + case 290: goto st290; + case 291: goto st291; + case 292: goto st292; + case 293: goto st293; + case 294: goto st294; + case 1141: goto st1141; + case 295: goto st295; + case 296: goto st296; + case 297: goto st297; + case 298: goto st298; + case 299: goto st299; + case 300: goto st300; + case 301: goto st301; + case 302: goto st302; + case 303: goto st303; + case 304: goto st304; + case 1142: goto st1142; + case 305: goto st305; + case 306: goto st306; + case 307: goto st307; + case 308: goto st308; + case 309: goto st309; + case 310: goto st310; + case 311: goto st311; + case 312: goto st312; + case 313: goto st313; + case 314: goto st314; + case 315: goto st315; + case 316: goto st316; + case 317: goto st317; + case 318: goto st318; + case 1143: goto st1143; + case 319: goto st319; + case 320: goto st320; + case 321: goto st321; + case 322: goto st322; + case 323: goto st323; + case 324: goto st324; + case 325: goto st325; + case 1144: goto st1144; + case 326: goto st326; + case 327: goto st327; + case 328: goto st328; + case 329: goto st329; + case 330: goto st330; + case 331: goto st331; + case 332: goto st332; + case 333: goto st333; + case 334: goto st334; + case 1145: goto st1145; + case 1146: goto st1146; + case 1147: goto st1147; + case 335: goto st335; + case 336: goto st336; + case 337: goto st337; + case 338: goto st338; + case 339: goto st339; + case 340: goto st340; + case 341: goto st341; + case 342: goto st342; + case 1148: goto st1148; + case 1149: goto st1149; + case 343: goto st343; + case 344: goto st344; + case 345: goto st345; + case 1150: goto st1150; + case 346: goto st346; + case 347: goto st347; + case 348: goto st348; + case 349: goto st349; + case 350: goto st350; + case 351: goto st351; + case 352: goto st352; + case 353: goto st353; + case 354: goto st354; + case 355: goto st355; + case 356: goto st356; + case 357: goto st357; + case 358: goto st358; + case 359: goto st359; + case 360: goto st360; + case 361: goto st361; + case 362: goto st362; + case 363: goto st363; + case 364: goto st364; + case 365: goto st365; + case 366: goto st366; + case 367: goto st367; + case 368: goto st368; + case 369: goto st369; + case 370: goto st370; + case 371: goto st371; + case 372: goto st372; + case 373: goto st373; + case 374: goto st374; + case 375: goto st375; + case 376: goto st376; + case 377: goto st377; + case 378: goto st378; + case 379: goto st379; + case 380: goto st380; + case 381: goto st381; + case 382: goto st382; + case 383: goto st383; + case 384: goto st384; + case 385: goto st385; + case 386: goto st386; + case 387: goto st387; + case 388: goto st388; + case 389: goto st389; + case 390: goto st390; + case 391: goto st391; + case 392: goto st392; + case 393: goto st393; + case 394: goto st394; + case 395: goto st395; + case 396: goto st396; + case 397: goto st397; + case 398: goto st398; + case 399: goto st399; + case 400: goto st400; + case 401: goto st401; + case 402: goto st402; + case 403: goto st403; + case 404: goto st404; + case 405: goto st405; + case 406: goto st406; + case 407: goto st407; + case 408: goto st408; + case 409: goto st409; + case 410: goto st410; + case 411: goto st411; + case 412: goto st412; + case 413: goto st413; + case 414: goto st414; + case 415: goto st415; + case 416: goto st416; + case 417: goto st417; + case 418: goto st418; + case 419: goto st419; + case 420: goto st420; + case 421: goto st421; + case 422: goto st422; + case 423: goto st423; + case 424: goto st424; + case 425: goto st425; + case 426: goto st426; + case 427: goto st427; + case 428: goto st428; + case 429: goto st429; + case 430: goto st430; + case 431: goto st431; + case 432: goto st432; + case 433: goto st433; + case 434: goto st434; + case 435: goto st435; + case 436: goto st436; + case 437: goto st437; + case 438: goto st438; + case 439: goto st439; + case 440: goto st440; + case 441: goto st441; + case 442: goto st442; + case 443: goto st443; + case 444: goto st444; + case 445: goto st445; + case 446: goto st446; + case 447: goto st447; + case 448: goto st448; + case 449: goto st449; + case 450: goto st450; + case 451: goto st451; + case 452: goto st452; + case 453: goto st453; + case 454: goto st454; + case 455: goto st455; + case 456: goto st456; + case 457: goto st457; + case 458: goto st458; + case 459: goto st459; + case 460: goto st460; + case 461: goto st461; + case 462: goto st462; + case 463: goto st463; + case 464: goto st464; + case 465: goto st465; + case 466: goto st466; + case 467: goto st467; + case 468: goto st468; + case 469: goto st469; + case 470: goto st470; + case 471: goto st471; + case 472: goto st472; + case 473: goto st473; + case 474: goto st474; + case 1151: goto st1151; + case 1152: goto st1152; + case 1153: goto st1153; + case 475: goto st475; + case 476: goto st476; + case 477: goto st477; + case 478: goto st478; + case 479: goto st479; + case 1154: goto st1154; + case 480: goto st480; + case 481: goto st481; + case 482: goto st482; + case 483: goto st483; + case 1155: goto st1155; + case 1156: goto st1156; + case 1157: goto st1157; + case 484: goto st484; + case 485: goto st485; + case 1158: goto st1158; + case 486: goto st486; + case 487: goto st487; + case 488: goto st488; + case 1159: goto st1159; + case 489: goto st489; + case 490: goto st490; + case 491: goto st491; + case 492: goto st492; + case 493: goto st493; + case 494: goto st494; + case 495: goto st495; + case 496: goto st496; + case 497: goto st497; + case 498: goto st498; + case 499: goto st499; + case 500: goto st500; + case 501: goto st501; + case 502: goto st502; + case 503: goto st503; + case 504: goto st504; + case 505: goto st505; + case 506: goto st506; + case 507: goto st507; + case 508: goto st508; + case 509: goto st509; + case 510: goto st510; + case 511: goto st511; + case 512: goto st512; + case 513: goto st513; + case 514: goto st514; + case 515: goto st515; + case 516: goto st516; + case 517: goto st517; + case 518: goto st518; + case 519: goto st519; + case 520: goto st520; + case 521: goto st521; + case 522: goto st522; + case 523: goto st523; + case 524: goto st524; + case 525: goto st525; + case 526: goto st526; + case 527: goto st527; + case 528: goto st528; + case 529: goto st529; + case 530: goto st530; + case 531: goto st531; + case 532: goto st532; + case 533: goto st533; + case 534: goto st534; + case 535: goto st535; + case 536: goto st536; + case 537: goto st537; + case 538: goto st538; + case 539: goto st539; + case 540: goto st540; + case 541: goto st541; + case 542: goto st542; + case 543: goto st543; + case 544: goto st544; + case 545: goto st545; + case 546: goto st546; + case 547: goto st547; + case 548: goto st548; + case 549: goto st549; + case 550: goto st550; + case 551: goto st551; + case 552: goto st552; + case 553: goto st553; + case 554: goto st554; + case 555: goto st555; + case 556: goto st556; + case 557: goto st557; + case 558: goto st558; + case 559: goto st559; + case 560: goto st560; + case 561: goto st561; + case 562: goto st562; + case 563: goto st563; + case 564: goto st564; + case 565: goto st565; + case 566: goto st566; + case 567: goto st567; + case 568: goto st568; + case 569: goto st569; + case 570: goto st570; + case 571: goto st571; + case 572: goto st572; + case 573: goto st573; + case 574: goto st574; + case 575: goto st575; + case 576: goto st576; + case 577: goto st577; + case 578: goto st578; + case 579: goto st579; + case 580: goto st580; + case 581: goto st581; + case 582: goto st582; + case 583: goto st583; + case 584: goto st584; + case 585: goto st585; + case 586: goto st586; + case 587: goto st587; + case 588: goto st588; + case 589: goto st589; + case 590: goto st590; + case 591: goto st591; + case 592: goto st592; + case 1160: goto st1160; + case 593: goto st593; + case 594: goto st594; + case 595: goto st595; + case 596: goto st596; + case 597: goto st597; + case 598: goto st598; + case 599: goto st599; + case 600: goto st600; + case 601: goto st601; + case 602: goto st602; + case 603: goto st603; + case 604: goto st604; + case 605: goto st605; + case 606: goto st606; + case 607: goto st607; + case 608: goto st608; + case 609: goto st609; + case 610: goto st610; + case 611: goto st611; + case 612: goto st612; + case 613: goto st613; + case 614: goto st614; + case 615: goto st615; + case 616: goto st616; + case 617: goto st617; + case 618: goto st618; + case 619: goto st619; + case 620: goto st620; + case 621: goto st621; + case 622: goto st622; + case 623: goto st623; + case 624: goto st624; + case 625: goto st625; + case 626: goto st626; + case 627: goto st627; + case 628: goto st628; + case 629: goto st629; + case 630: goto st630; + case 631: goto st631; + case 632: goto st632; + case 633: goto st633; + case 1161: goto st1161; + case 634: goto st634; + case 635: goto st635; + case 1162: goto st1162; + case 636: goto st636; + case 637: goto st637; + case 638: goto st638; + case 639: goto st639; + case 640: goto st640; + case 641: goto st641; + case 642: goto st642; + case 643: goto st643; + case 644: goto st644; + case 645: goto st645; + case 646: goto st646; + case 647: goto st647; + case 648: goto st648; + case 649: goto st649; + case 1163: goto st1163; + case 650: goto st650; + case 651: goto st651; + case 652: goto st652; + case 653: goto st653; + case 654: goto st654; + case 655: goto st655; + case 656: goto st656; + case 657: goto st657; + case 658: goto st658; + case 659: goto st659; + case 660: goto st660; + case 661: goto st661; + case 662: goto st662; + case 663: goto st663; + case 664: goto st664; + case 665: goto st665; + case 666: goto st666; + case 667: goto st667; + case 668: goto st668; + case 669: goto st669; + case 670: goto st670; + case 671: goto st671; + case 1164: goto st1164; + case 672: goto st672; + case 673: goto st673; + case 674: goto st674; + case 675: goto st675; + case 676: goto st676; + case 1165: goto st1165; + case 677: goto st677; + case 678: goto st678; + case 679: goto st679; + case 680: goto st680; + case 681: goto st681; + case 1166: goto st1166; + case 682: goto st682; + case 683: goto st683; + case 684: goto st684; + case 685: goto st685; + case 686: goto st686; + case 1167: goto st1167; + case 1168: goto st1168; + case 1169: goto st1169; + case 687: goto st687; + case 688: goto st688; + case 1170: goto st1170; + case 689: goto st689; + case 690: goto st690; + case 691: goto st691; + case 692: goto st692; + case 693: goto st693; + case 694: goto st694; + case 695: goto st695; + case 696: goto st696; + case 697: goto st697; + case 698: goto st698; + case 699: goto st699; + case 700: goto st700; + case 701: goto st701; + case 702: goto st702; + case 703: goto st703; + case 704: goto st704; + case 705: goto st705; + case 706: goto st706; + case 707: goto st707; + case 708: goto st708; + case 709: goto st709; + case 710: goto st710; + case 711: goto st711; + case 712: goto st712; + case 713: goto st713; + case 714: goto st714; + case 715: goto st715; + case 1171: goto st1171; + case 1172: goto st1172; + case 1173: goto st1173; + case 716: goto st716; + case 717: goto st717; + case 718: goto st718; + case 1174: goto st1174; + case 1175: goto st1175; + case 719: goto st719; + case 720: goto st720; + case 721: goto st721; + case 722: goto st722; + case 1176: goto st1176; + case 1177: goto st1177; + case 723: goto st723; + case 724: goto st724; + case 725: goto st725; + case 726: goto st726; + case 1178: goto st1178; + case 1179: goto st1179; + case 727: goto st727; + case 728: goto st728; + case 729: goto st729; + case 730: goto st730; + case 731: goto st731; + case 732: goto st732; + case 733: goto st733; + case 734: goto st734; + case 735: goto st735; + case 736: goto st736; + case 737: goto st737; + case 738: goto st738; + case 739: goto st739; + case 740: goto st740; + case 741: goto st741; + case 742: goto st742; + case 743: goto st743; + case 744: goto st744; + case 745: goto st745; + case 746: goto st746; + case 747: goto st747; + case 748: goto st748; + case 749: goto st749; + case 750: goto st750; + case 751: goto st751; + case 1180: goto st1180; + case 752: goto st752; + case 753: goto st753; + case 754: goto st754; + case 755: goto st755; + case 756: goto st756; + case 757: goto st757; + case 758: goto st758; + case 759: goto st759; + case 760: goto st760; + case 761: goto st761; + case 762: goto st762; + case 763: goto st763; + case 764: goto st764; + case 765: goto st765; + case 766: goto st766; + case 1181: goto st1181; + case 767: goto st767; + case 768: goto st768; + case 769: goto st769; + case 770: goto st770; + case 771: goto st771; + case 772: goto st772; + case 773: goto st773; + case 774: goto st774; + case 775: goto st775; + case 776: goto st776; + case 777: goto st777; + case 778: goto st778; + case 779: goto st779; + case 1182: goto st1182; + case 780: goto st780; + case 781: goto st781; + case 782: goto st782; + case 783: goto st783; + case 784: goto st784; + case 785: goto st785; + case 786: goto st786; + case 787: goto st787; + case 788: goto st788; + case 789: goto st789; + case 790: goto st790; + case 1183: goto st1183; + case 1184: goto st1184; + case 791: goto st791; + case 792: goto st792; + case 793: goto st793; + case 1185: goto st1185; + case 794: goto st794; + case 795: goto st795; + case 796: goto st796; + case 797: goto st797; + case 798: goto st798; + case 799: goto st799; + case 800: goto st800; + case 801: goto st801; + case 802: goto st802; + case 803: goto st803; + case 1186: goto st1186; + case 1187: goto st1187; + case 1188: goto st1188; + case 804: goto st804; + case 805: goto st805; + case 806: goto st806; + case 807: goto st807; + case 808: goto st808; + case 809: goto st809; + case 810: goto st810; + case 811: goto st811; + case 812: goto st812; + case 813: goto st813; + case 814: goto st814; + case 1189: goto st1189; + case 1190: goto st1190; + case 1191: goto st1191; + case 815: goto st815; + case 816: goto st816; + case 817: goto st817; + case 818: goto st818; + case 819: goto st819; + case 820: goto st820; + case 821: goto st821; + case 822: goto st822; + case 823: goto st823; + case 824: goto st824; + case 825: goto st825; + case 826: goto st826; + case 1192: goto st1192; + case 827: goto st827; + case 828: goto st828; + case 829: goto st829; + case 1193: goto st1193; + case 1194: goto st1194; + case 830: goto st830; + case 1195: goto st1195; + case 1196: goto st1196; + case 831: goto st831; + case 1197: goto st1197; + case 1198: goto st1198; + case 832: goto st832; + case 833: goto st833; + case 834: goto st834; + case 835: goto st835; + case 836: goto st836; + case 837: goto st837; + case 838: goto st838; + case 839: goto st839; + case 840: goto st840; + case 841: goto st841; + case 842: goto st842; + case 843: goto st843; + case 844: goto st844; + case 845: goto st845; + case 846: goto st846; + case 847: goto st847; + case 848: goto st848; + case 849: goto st849; + case 850: goto st850; + case 851: goto st851; + case 852: goto st852; + case 853: goto st853; + case 854: goto st854; + case 855: goto st855; + case 856: goto st856; + case 857: goto st857; + case 858: goto st858; + case 859: goto st859; + case 860: goto st860; + case 861: goto st861; + case 862: goto st862; + case 863: goto st863; + case 864: goto st864; + case 865: goto st865; + case 866: goto st866; + case 867: goto st867; + case 868: goto st868; + case 869: goto st869; + case 870: goto st870; + case 871: goto st871; + case 872: goto st872; + case 873: goto st873; + case 1199: goto st1199; + case 874: goto st874; + case 875: goto st875; + case 876: goto st876; + case 877: goto st877; + case 878: goto st878; + case 879: goto st879; + case 880: goto st880; + case 881: goto st881; + case 882: goto st882; + case 883: goto st883; + case 884: goto st884; + case 885: goto st885; + case 886: goto st886; + case 887: goto st887; + case 888: goto st888; + case 889: goto st889; + case 890: goto st890; + case 891: goto st891; + case 892: goto st892; + case 893: goto st893; + case 894: goto st894; + case 895: goto st895; + case 896: goto st896; + case 897: goto st897; + case 898: goto st898; + case 899: goto st899; + case 900: goto st900; + case 901: goto st901; + case 902: goto st902; + case 903: goto st903; + case 904: goto st904; + case 905: goto st905; + case 906: goto st906; + case 907: goto st907; + case 908: goto st908; + case 909: goto st909; + case 910: goto st910; + case 911: goto st911; + case 912: goto st912; + case 913: goto st913; + case 914: goto st914; + case 915: goto st915; + case 916: goto st916; + case 917: goto st917; + case 918: goto st918; + case 919: goto st919; + case 920: goto st920; + case 921: goto st921; + case 922: goto st922; + case 923: goto st923; + case 924: goto st924; + case 925: goto st925; + case 926: goto st926; + case 927: goto st927; + case 928: goto st928; + case 929: goto st929; + case 930: goto st930; + case 931: goto st931; + case 932: goto st932; + case 933: goto st933; + case 934: goto st934; + case 935: goto st935; + case 936: goto st936; + case 937: goto st937; + case 938: goto st938; + case 939: goto st939; + case 940: goto st940; + case 941: goto st941; + case 942: goto st942; + case 943: goto st943; + case 944: goto st944; + case 945: goto st945; + case 946: goto st946; + case 947: goto st947; + case 948: goto st948; + case 949: goto st949; + case 950: goto st950; + case 951: goto st951; + case 952: goto st952; + case 953: goto st953; + case 954: goto st954; + case 955: goto st955; + case 956: goto st956; + case 957: goto st957; + case 958: goto st958; + case 959: goto st959; + case 960: goto st960; + case 961: goto st961; + case 962: goto st962; + case 963: goto st963; + case 964: goto st964; + case 965: goto st965; + case 966: goto st966; + case 967: goto st967; + case 968: goto st968; + case 969: goto st969; + case 970: goto st970; + case 971: goto st971; + case 972: goto st972; + case 973: goto st973; + case 974: goto st974; + case 975: goto st975; + case 976: goto st976; + case 977: goto st977; + case 978: goto st978; + case 979: goto st979; + case 980: goto st980; + case 981: goto st981; + case 982: goto st982; + case 983: goto st983; + case 984: goto st984; + case 985: goto st985; + case 986: goto st986; + case 987: goto st987; + case 988: goto st988; + case 989: goto st989; + case 990: goto st990; + case 991: goto st991; + case 992: goto st992; + case 993: goto st993; + case 994: goto st994; + case 995: goto st995; + case 996: goto st996; + case 997: goto st997; + case 998: goto st998; + case 999: goto st999; + case 1000: goto st1000; + case 1001: goto st1001; + case 1002: goto st1002; + case 1003: goto st1003; + case 1004: goto st1004; + case 1005: goto st1005; + case 1006: goto st1006; + case 1007: goto st1007; + case 1008: goto st1008; + case 1009: goto st1009; + case 1010: goto st1010; + case 1011: goto st1011; + case 1012: goto st1012; + case 1200: goto st1200; + case 1013: goto st1013; + case 1014: goto st1014; + case 1015: goto st1015; + case 1016: goto st1016; + case 1017: goto st1017; + case 1018: goto st1018; + case 1019: goto st1019; + case 1020: goto st1020; + case 1201: goto st1201; + case 1021: goto st1021; + case 1022: goto st1022; + case 1023: goto st1023; + case 1024: goto st1024; + case 1025: goto st1025; + case 1202: goto st1202; + case 1026: goto st1026; + case 1027: goto st1027; + case 1028: goto st1028; + case 1029: goto st1029; + case 1030: goto st1030; + case 1031: goto st1031; + case 1032: goto st1032; + case 1033: goto st1033; + case 1034: goto st1034; + case 1035: goto st1035; + case 1036: goto st1036; + case 1037: goto st1037; + case 1038: goto st1038; + case 1039: goto st1039; + case 1040: goto st1040; + case 1041: goto st1041; + case 1042: goto st1042; + case 1043: goto st1043; + case 1203: goto st1203; + case 1044: goto st1044; + case 1045: goto st1045; + case 1046: goto st1046; + case 1047: goto st1047; + case 1048: goto st1048; + case 1049: goto st1049; + case 1050: goto st1050; + case 1051: goto st1051; + case 1052: goto st1052; + case 1053: goto st1053; + case 1054: goto st1054; + case 1055: goto st1055; + case 1056: goto st1056; + case 1057: goto st1057; + case 1058: goto st1058; + case 1059: goto st1059; + case 1060: goto st1060; + case 1061: goto st1061; + case 1062: goto st1062; + case 1204: goto st1204; + case 1063: goto st1063; + case 1064: goto st1064; + case 1065: goto st1065; + case 1066: goto st1066; + case 1067: goto st1067; + case 1068: goto st1068; + case 1069: goto st1069; + case 1070: goto st1070; + case 1071: goto st1071; + case 1072: goto st1072; + case 1073: goto st1073; + case 1074: goto st1074; + case 1075: goto st1075; + case 1076: goto st1076; + case 1077: goto st1077; + case 1205: goto st1205; + case 1206: goto st1206; + case 1207: goto st1207; + case 1078: goto st1078; + case 1079: goto st1079; + case 1080: goto st1080; + case 1081: goto st1081; + case 1082: goto st1082; + case 1083: goto st1083; + case 1084: goto st1084; + case 1208: goto st1208; + case 1085: goto st1085; + case 1086: goto st1086; + case 1087: goto st1087; + case 1088: goto st1088; + case 1089: goto st1089; + case 1090: goto st1090; + case 1091: goto st1091; + case 1092: goto st1092; + case 1093: goto st1093; + case 1094: goto st1094; + case 1095: goto st1095; + case 1096: goto st1096; + case 1097: goto st1097; + case 1209: goto st1209; + case 1098: goto st1098; + case 1099: goto st1099; + case 1100: goto st1100; + case 1101: goto st1101; + case 1102: goto st1102; + case 1103: goto st1103; + case 1104: goto st1104; + case 1210: goto st1210; + case 1105: goto st1105; + case 1106: goto st1106; + case 1107: goto st1107; + case 1108: goto st1108; + case 1109: goto st1109; + case 1110: goto st1110; + case 1211: goto st1211; + case 1111: goto st1111; + case 1112: goto st1112; + case 1113: goto st1113; + case 1114: goto st1114; + case 1115: goto st1115; + case 1116: goto st1116; + case 1212: goto st1212; + case 1117: goto st1117; + case 1118: goto st1118; + case 1119: goto st1119; + case 1120: goto st1120; + case 1121: goto st1121; + case 1122: goto st1122; + case 1123: goto st1123; + case 1124: goto st1124; + case 1213: goto st1213; + case 1125: goto st1125; + case 1126: goto st1126; + default: break; + } + + if ( ++p == pe ) + goto _test_eof; +_resume: + switch ( cs ) + { +tr20: + { + s->line_counter++; + } + goto st1127; +tr83: + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + if (rdata_tail - s->r_data > UINT16_MAX) { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + s->r_data_length = rdata_tail - s->r_data; + + s->state = ZS_STATE_DATA; + + // Execute the record callback. + if (s->process.automatic) { + if (s->process.record != NULL) { + s->process.record(s); + + // Stop if required from the callback. + if (s->state == ZS_STATE_STOP) { + {p++; cs = 1127; goto _out;} + } + } + } else { + // Return if external processing. + p--; {p++; cs = 1127; goto _out;} + } + } + { + s->line_counter++; + } + goto st1127; +tr89: + { + if (rdata_tail - s->r_data > UINT16_MAX) { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + s->r_data_length = rdata_tail - s->r_data; + + s->state = ZS_STATE_DATA; + + // Execute the record callback. + if (s->process.automatic) { + if (s->process.record != NULL) { + s->process.record(s); + + // Stop if required from the callback. + if (s->state == ZS_STATE_STOP) { + {p++; cs = 1127; goto _out;} + } + } + } else { + // Return if external processing. + p--; {p++; cs = 1127; goto _out;} + } + } + { + s->line_counter++; + } + goto st1127; +tr92: + { + s->buffer[s->buffer_length++] = 0; + } + { + if (rdata_tail - s->r_data > UINT16_MAX) { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + s->r_data_length = rdata_tail - s->r_data; + + s->state = ZS_STATE_DATA; + + // Execute the record callback. + if (s->process.automatic) { + if (s->process.record != NULL) { + s->process.record(s); + + // Stop if required from the callback. + if (s->state == ZS_STATE_STOP) { + {p++; cs = 1127; goto _out;} + } + } + } else { + // Return if external processing. + p--; {p++; cs = 1127; goto _out;} + } + } + { + s->line_counter++; + } + goto st1127; +tr666: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st1127; +tr760: + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + s->buffer[s->buffer_length++] = 0; + } + { + if (rdata_tail - s->r_data > UINT16_MAX) { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + s->r_data_length = rdata_tail - s->r_data; + + s->state = ZS_STATE_DATA; + + // Execute the record callback. + if (s->process.automatic) { + if (s->process.record != NULL) { + s->process.record(s); + + // Stop if required from the callback. + if (s->state == ZS_STATE_STOP) { + {p++; cs = 1127; goto _out;} + } + } + } else { + // Return if external processing. + p--; {p++; cs = 1127; goto _out;} + } + } + { + s->line_counter++; + } + goto st1127; +tr878: + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + if (rdata_tail - s->r_data > UINT16_MAX) { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + s->r_data_length = rdata_tail - s->r_data; + + s->state = ZS_STATE_DATA; + + // Execute the record callback. + if (s->process.automatic) { + if (s->process.record != NULL) { + s->process.record(s); + + // Stop if required from the callback. + if (s->state == ZS_STATE_STOP) { + {p++; cs = 1127; goto _out;} + } + } + } else { + // Return if external processing. + p--; {p++; cs = 1127; goto _out;} + } + } + { + s->line_counter++; + } + { + s->buffer[s->buffer_length++] = 0; + } + goto st1127; +tr882: + { + if (rdata_tail - s->r_data > UINT16_MAX) { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + s->r_data_length = rdata_tail - s->r_data; + + s->state = ZS_STATE_DATA; + + // Execute the record callback. + if (s->process.automatic) { + if (s->process.record != NULL) { + s->process.record(s); + + // Stop if required from the callback. + if (s->state == ZS_STATE_STOP) { + {p++; cs = 1127; goto _out;} + } + } + } else { + // Return if external processing. + p--; {p++; cs = 1127; goto _out;} + } + } + { + s->line_counter++; + } + { + s->buffer[s->buffer_length++] = 0; + } + goto st1127; +tr3618: + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + s->line_counter++; + } + goto st1127; +tr3678: + { + NOERR; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + s->line_counter++; + } + goto st1127; +st1127: + if ( ++p == pe ) + goto _test_eof1127; +case 1127: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3613; + case 32: goto tr3613; + case 36: goto st152; + case 40: goto tr3615; + case 41: goto tr3616; + case 42: goto tr3617; + case 92: goto tr3617; + case 95: goto tr3617; + case 778: goto tr3618; + case 827: goto tr3619; + case 1034: goto tr3620; + case 1083: goto tr3621; + } + if ( _widec < 64 ) { + if ( 45 <= _widec && _widec <= 57 ) + goto tr3617; + } else if ( _widec > 90 ) { + if ( 97 <= _widec && _widec <= 122 ) + goto tr3617; + } else + goto tr3617; + goto tr3612; +tr0: + { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + { + WARN(ZS_UNSUPPORTED_TYPE); + p--; {goto st268;} + } + { + WARN(ZS_BAD_REST); + p--; {goto st268;} + } + goto st0; +tr24: + { + WARN(ZS_BAD_TIME_UNIT); + p--; {goto st268;} + } + { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + { + WARN(ZS_UNSUPPORTED_TYPE); + p--; {goto st268;} + } + goto st0; +tr36: + { + WARN(ZS_UNSUPPORTED_TYPE); + p--; {goto st268;} + } + goto st0; +tr57: + { + WARN(ZS_UNSUPPORTED_TYPE); + p--; {goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; {goto st268;} + } + goto st0; +tr71: + { + WARN(ZS_BAD_RDATA); + p--; {goto st268;} + } + goto st0; +tr79: + { + WARN(ZS_BAD_RDATA); + p--; {goto st268;} + } + { + WARN(ZS_BAD_REST); + p--; {goto st268;} + } + goto st0; +tr85: + { + WARN(ZS_BAD_REST); + p--; {goto st268;} + } + goto st0; +tr114: + { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + { + WARN(ZS_UNSUPPORTED_TYPE); + p--; {goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; {goto st268;} + } + { + WARN(ZS_BAD_REST); + p--; {goto st268;} + } + goto st0; +tr139: + { + WARN(ZS_BAD_TIME_UNIT); + p--; {goto st268;} + } + { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + { + WARN(ZS_UNSUPPORTED_TYPE); + p--; {goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; {goto st268;} + } + { + WARN(ZS_BAD_REST); + p--; {goto st268;} + } + goto st0; +tr145: + { + WARN(ZS_UNSUPPORTED_TYPE); + p--; {goto st268;} + } + { + WARN(ZS_BAD_REST); + p--; {goto st268;} + } + goto st0; +tr583: + { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + { + WARN(ZS_UNSUPPORTED_TYPE); + p--; {goto st268;} + } + goto st0; +tr585: + { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + { + WARN(ZS_UNSUPPORTED_TYPE); + p--; {goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; {goto st268;} + } + goto st0; +tr667: + { + ERR(ZS_BAD_DIRECTIVE); + p--; {goto st268;} + } + goto st0; +tr678: + { + s->r_owner_length = 0; + WARN(ZS_BAD_OWNER); + p--; {goto st268;} + } + goto st0; +tr692: + { + s->r_owner_length = 0; + WARN(ZS_BAD_OWNER); + p--; {goto st268;} + } + { + WARN(ZS_BAD_TIME_UNIT); + p--; {goto st268;} + } + { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + { + WARN(ZS_UNSUPPORTED_TYPE); + p--; {goto st268;} + } + goto st0; +tr720: + { + s->r_owner_length = 0; + WARN(ZS_BAD_OWNER); + p--; {goto st268;} + } + { + WARN(ZS_UNSUPPORTED_TYPE); + p--; {goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; {goto st268;} + } + goto st0; +tr735: + { + WARN(ZS_UNSUPPORTED_TYPE); + p--; {goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; {goto st268;} + } + { + WARN(ZS_BAD_REST); + p--; {goto st268;} + } + goto st0; +tr773: + { + WARN(ZS_BAD_RDATA); + p--; {goto st268;} + } + { + WARN(ZS_BAD_REST); + p--; {goto st268;} + } + { + ERR(ZS_BAD_DIRECTIVE); + p--; {goto st268;} + } + goto st0; +tr783: + { + s->r_owner_length = 0; + WARN(ZS_BAD_OWNER); + p--; {goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; {goto st268;} + } + { + WARN(ZS_BAD_REST); + p--; {goto st268;} + } + goto st0; +tr789: + { + s->r_owner_length = 0; + WARN(ZS_BAD_OWNER); + p--; {goto st268;} + } + { + WARN(ZS_BAD_TIME_UNIT); + p--; {goto st268;} + } + { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + { + WARN(ZS_UNSUPPORTED_TYPE); + p--; {goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; {goto st268;} + } + { + WARN(ZS_BAD_REST); + p--; {goto st268;} + } + goto st0; +tr802: + { + s->r_owner_length = 0; + WARN(ZS_BAD_OWNER); + p--; {goto st268;} + } + { + WARN(ZS_UNSUPPORTED_TYPE); + p--; {goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; {goto st268;} + } + { + WARN(ZS_BAD_REST); + p--; {goto st268;} + } + goto st0; +tr816: + { + s->r_owner_length = 0; + WARN(ZS_BAD_OWNER); + p--; {goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; {goto st268;} + } + goto st0; +tr908: + { + s->r_owner_length = 0; + WARN(ZS_BAD_OWNER); + p--; {goto st268;} + } + { + WARN(ZS_UNSUPPORTED_TYPE); + p--; {goto st268;} + } + goto st0; +tr919: + { + WARN(ZS_BAD_DNAME_CHAR); + p--; {goto st268;} + } + goto st0; +tr932: + { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + { + WARN(ZS_BAD_DNAME_CHAR); + p--; {goto st268;} + } + goto st0; +tr940: + { + WARN(ZS_BAD_TEXT_CHAR); + p--; {goto st268;} + } + { + WARN(ZS_BAD_TEXT); + p--; {goto st268;} + } + goto st0; +tr946: + { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + { + WARN(ZS_BAD_TEXT_CHAR); + p--; {goto st268;} + } + { + WARN(ZS_BAD_TEXT); + p--; {goto st268;} + } + goto st0; +tr957: + { + WARN(ZS_BAD_TEXT); + p--; {goto st268;} + } + goto st0; +tr964: + { + ERR(ZS_BAD_TTL); + p--; {goto st268;} + } + goto st0; +tr970: + { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + { + ERR(ZS_BAD_TTL); + p--; {goto st268;} + } + goto st0; +tr972: + { + WARN(ZS_BAD_TIME_UNIT); + p--; {goto st268;} + } + { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + { + WARN(ZS_BAD_REST); + p--; {goto st268;} + } + { + ERR(ZS_BAD_TTL); + p--; {goto st268;} + } + goto st0; +tr984: + { + WARN(ZS_BAD_REST); + p--; {goto st268;} + } + { + ERR(ZS_BAD_TTL); + p--; {goto st268;} + } + goto st0; +tr993: + { + WARN(ZS_BAD_TIME_UNIT); + p--; {goto st268;} + } + { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + { + ERR(ZS_BAD_TTL); + p--; {goto st268;} + } + goto st0; +tr1008: + { + ERR(ZS_BAD_ORIGIN); + p--; {goto st268;} + } + goto st0; +tr1020: + { + WARN(ZS_BAD_REST); + p--; {goto st268;} + } + { + ERR(ZS_BAD_ORIGIN); + p--; {goto st268;} + } + goto st0; +tr1036: + { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + { + ERR(ZS_BAD_ORIGIN); + p--; {goto st268;} + } + goto st0; +tr1050: + { + ERR(ZS_BAD_INCLUDE_FILENAME); + p--; {goto st268;} + } + goto st0; +tr1051: + { + ERR(ZS_BAD_INCLUDE_FILENAME); + p--; {goto st268;} + } + { + WARN(ZS_BAD_REST); + p--; {goto st268;} + } + goto st0; +tr1058: + { + ERR(ZS_BAD_INCLUDE_ORIGIN); + p--; {goto st268;} + } + { + WARN(ZS_BAD_REST); + p--; {goto st268;} + } + goto st0; +tr1068: + { + ERR(ZS_BAD_INCLUDE_ORIGIN); + p--; {goto st268;} + } + goto st0; +tr1085: + { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + { + ERR(ZS_BAD_INCLUDE_ORIGIN); + p--; {goto st268;} + } + goto st0; +tr1102: + { + WARN(ZS_BAD_BASE64_CHAR); + p--; {goto st268;} + } + goto st0; +tr1118: + { + WARN(ZS_BAD_BITMAP); + p--; {goto st268;} + } + goto st0; +tr1611: + { + WARN(ZS_BAD_HEX_RDATA); + p--; {goto st268;} + } + goto st0; +tr1617: + { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + { + WARN(ZS_BAD_HEX_RDATA); + p--; {goto st268;} + } + goto st0; +tr1625: + { + WARN(ZS_BAD_HEX_CHAR); + p--; {goto st268;} + } + { + WARN(ZS_BAD_HEX_RDATA); + p--; {goto st268;} + } + { + WARN(ZS_BAD_HEX_RDATA); + p--; {goto st268;} + } + goto st0; +tr1687: + { + WARN(ZS_BAD_ALGORITHM); + p--; {goto st268;} + } + goto st0; +tr1809: + { + WARN(ZS_BAD_CERT_TYPE); + p--; {goto st268;} + } + goto st0; +tr1862: + { + WARN(ZS_BAD_ADDRESS_CHAR); + p--; {goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; {goto st268;} + } + goto st0; +tr1885: + { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; {goto st268;} + } + goto st0; +tr1904: + { + WARN(ZS_BAD_TIME_UNIT); + p--; {goto st268;} + } + { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; {goto st268;} + } + goto st0; +tr2070: + { + s->long_string = false; + } + { + WARN(ZS_BAD_RDATA); + p--; {goto st268;} + } + goto st0; +tr2093: + { + WARN(ZS_BAD_LOC_DATA); + p--; {goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; {goto st268;} + } + goto st0; +tr2496: + { + WARN(ZS_BAD_APL); + p--; {goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; {goto st268;} + } + goto st0; +tr2511: + { + WARN(ZS_BAD_ADDRESS_CHAR); + p--; {goto st268;} + } + { + WARN(ZS_BAD_APL); + p--; {goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; {goto st268;} + } + goto st0; +tr2556: + { + WARN(ZS_BAD_HEX_CHAR); + p--; {goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; {goto st268;} + } + goto st0; +tr2627: + { + WARN(ZS_BAD_GATEWAY); + p--; {goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; {goto st268;} + } + goto st0; +tr2642: + { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + { + WARN(ZS_BAD_GATEWAY); + p--; {goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; {goto st268;} + } + goto st0; +tr2656: + { + WARN(ZS_BAD_GATEWAY); + p--; {goto st268;} + } + { + WARN(ZS_BAD_GATEWAY_KEY); + p--; {goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; {goto st268;} + } + goto st0; +tr2679: + { + WARN(ZS_BAD_GATEWAY_KEY); + p--; {goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; {goto st268;} + } + goto st0; +tr2703: + { + WARN(ZS_BAD_ADDRESS_CHAR); + p--; {goto st268;} + } + { + WARN(ZS_BAD_GATEWAY); + p--; {goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; {goto st268;} + } + goto st0; +tr2710: + { + WARN(ZS_BAD_ADDRESS_CHAR); + p--; {goto st268;} + } + { + WARN(ZS_BAD_GATEWAY); + p--; {goto st268;} + } + { + WARN(ZS_BAD_GATEWAY_KEY); + p--; {goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; {goto st268;} + } + goto st0; +tr2829: + { + WARN(ZS_UNSUPPORTED_TYPE); + p--; {goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; {goto st268;} + } + goto st0; +tr2883: + { + WARN(ZS_BAD_TIMESTAMP_CHAR); + p--; {goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; {goto st268;} + } + goto st0; +tr3262: + { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + { + WARN(ZS_UNSUPPORTED_TYPE); + p--; {goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; {goto st268;} + } + goto st0; +tr3363: + { + WARN(ZS_BAD_BASE32HEX_CHAR); + p--; {goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; {goto st268;} + } + goto st0; +tr3530: + { + WARN(ZS_BAD_HEX_CHAR); + p--; {goto st268;} + } + { + WARN(ZS_BAD_CHAR_COLON); + p--; {goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; {goto st268;} + } + goto st0; +tr3536: + { + WARN(ZS_BAD_CHAR_COLON); + p--; {goto st268;} + } + { + WARN(ZS_BAD_HEX_CHAR); + p--; {goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; {goto st268;} + } + goto st0; +tr3542: + { + WARN(ZS_BAD_CHAR_DASH); + p--; {goto st268;} + } + { + WARN(ZS_BAD_HEX_CHAR); + p--; {goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; {goto st268;} + } + goto st0; +tr3612: + { + s->r_owner_length = 0; + WARN(ZS_BAD_OWNER); + p--; {goto st268;} + } + { + WARN(ZS_BAD_REST); + p--; {goto st268;} + } + goto st0; +tr3634: + { + s->r_owner_length = 0; + WARN(ZS_BAD_OWNER); + p--; {goto st268;} + } + { + WARN(ZS_UNSUPPORTED_TYPE); + p--; {goto st268;} + } + { + WARN(ZS_BAD_REST); + p--; {goto st268;} + } + goto st0; +tr3655: + { + s->r_owner_length = 0; + WARN(ZS_BAD_OWNER); + p--; {goto st268;} + } + { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + { + WARN(ZS_UNSUPPORTED_TYPE); + p--; {goto st268;} + } + { + WARN(ZS_BAD_REST); + p--; {goto st268;} + } + goto st0; +tr3672: + { + s->r_owner_length = 0; + WARN(ZS_BAD_OWNER); + p--; {goto st268;} + } + { + ERR(ZS_BAD_DIRECTIVE); + p--; {goto st268;} + } + { + WARN(ZS_BAD_REST); + p--; {goto st268;} + } + goto st0; +tr3696: + { + s->r_owner_length = 0; + WARN(ZS_BAD_OWNER); + p--; {goto st268;} + } + { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + { + WARN(ZS_UNSUPPORTED_TYPE); + p--; {goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; {goto st268;} + } + { + WARN(ZS_BAD_REST); + p--; {goto st268;} + } + goto st0; +st0: +cs = 0; + goto _out; +tr2: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st1; +tr3: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st1; +tr3613: + { + if (s->r_owner_length == 0) { + WARN(ZS_BAD_PREVIOUS_OWNER); + p--; {goto st268;} + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st1; +tr3615: + { + if (s->r_owner_length == 0) { + WARN(ZS_BAD_PREVIOUS_OWNER); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st1; +tr3616: + { + if (s->r_owner_length == 0) { + WARN(ZS_BAD_PREVIOUS_OWNER); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st1; +tr3682: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + { + if (s->r_owner_length == 0) { + WARN(ZS_BAD_PREVIOUS_OWNER); + p--; {goto st268;} + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st1; +tr3683: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + { + if (s->r_owner_length == 0) { + WARN(ZS_BAD_PREVIOUS_OWNER); + p--; {goto st268;} + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st1; +tr3673: + { + NOERR; + } + { + if (s->r_owner_length == 0) { + WARN(ZS_BAD_PREVIOUS_OWNER); + p--; {goto st268;} + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st1; +tr3675: + { + NOERR; + } + { + if (s->r_owner_length == 0) { + WARN(ZS_BAD_PREVIOUS_OWNER); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st1; +tr3676: + { + NOERR; + } + { + if (s->r_owner_length == 0) { + WARN(ZS_BAD_PREVIOUS_OWNER); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st1; +st1: + if ( ++p == pe ) + goto _test_eof1; +case 1: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st1; + case 32: goto st1; + case 40: goto tr2; + case 41: goto tr3; + case 65: goto tr5; + case 67: goto tr6; + case 68: goto tr7; + case 69: goto tr8; + case 72: goto tr9; + case 73: goto tr10; + case 75: goto tr11; + case 76: goto tr12; + case 77: goto tr13; + case 78: goto tr14; + case 80: goto tr15; + case 82: goto tr16; + case 83: goto tr17; + case 84: goto tr18; + case 85: goto tr19; + case 97: goto tr5; + case 99: goto tr6; + case 100: goto tr7; + case 101: goto tr8; + case 104: goto tr9; + case 105: goto tr10; + case 107: goto tr11; + case 108: goto tr12; + case 109: goto tr13; + case 110: goto tr14; + case 112: goto tr15; + case 114: goto tr16; + case 115: goto tr17; + case 116: goto tr18; + case 117: goto tr19; + case 778: goto tr20; + case 827: goto tr21; + case 1034: goto tr22; + case 1083: goto tr23; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr4; + goto tr0; +tr4: + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st2; +tr28: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st2; +st2: + if ( ++p == pe ) + goto _test_eof2; +case 2: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr25; + case 32: goto tr25; + case 40: goto tr26; + case 41: goto tr27; + case 68: goto tr29; + case 72: goto tr30; + case 77: goto tr31; + case 83: goto st166; + case 87: goto tr33; + case 100: goto tr29; + case 104: goto tr30; + case 109: goto tr31; + case 115: goto st166; + case 119: goto tr33; + case 1034: goto tr34; + case 1083: goto tr35; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr28; + goto tr24; +tr38: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st3; +tr39: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st3; +tr55: + { + s->line_counter++; + } + goto st3; +tr25: + { + if (s->number64 <= UINT32_MAX) { + s->r_ttl = (uint32_t)(s->number64); + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st3; +tr26: + { + if (s->number64 <= UINT32_MAX) { + s->r_ttl = (uint32_t)(s->number64); + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st3; +tr27: + { + if (s->number64 <= UINT32_MAX) { + s->r_ttl = (uint32_t)(s->number64); + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st3; +tr34: + { + if (s->number64 <= UINT32_MAX) { + s->r_ttl = (uint32_t)(s->number64); + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st3; +tr719: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st3; +tr712: + { + if (s->number64 + s->number64_tmp < UINT32_MAX) { + s->number64 += s->number64_tmp; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= UINT32_MAX) { + s->r_ttl = (uint32_t)(s->number64); + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st3; +tr713: + { + if (s->number64 + s->number64_tmp < UINT32_MAX) { + s->number64 += s->number64_tmp; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= UINT32_MAX) { + s->r_ttl = (uint32_t)(s->number64); + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st3; +tr714: + { + if (s->number64 + s->number64_tmp < UINT32_MAX) { + s->number64 += s->number64_tmp; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= UINT32_MAX) { + s->r_ttl = (uint32_t)(s->number64); + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st3; +tr716: + { + if (s->number64 + s->number64_tmp < UINT32_MAX) { + s->number64 += s->number64_tmp; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= UINT32_MAX) { + s->r_ttl = (uint32_t)(s->number64); + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st3; +st3: + if ( ++p == pe ) + goto _test_eof3; +case 3: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st3; + case 32: goto st3; + case 40: goto tr38; + case 41: goto tr39; + case 65: goto tr40; + case 67: goto tr41; + case 68: goto tr42; + case 69: goto tr43; + case 72: goto tr44; + case 73: goto tr45; + case 75: goto tr46; + case 76: goto tr47; + case 77: goto tr48; + case 78: goto tr49; + case 80: goto tr50; + case 82: goto tr51; + case 83: goto tr52; + case 84: goto tr53; + case 85: goto tr54; + case 97: goto tr40; + case 99: goto tr41; + case 100: goto tr42; + case 101: goto tr43; + case 104: goto tr44; + case 105: goto tr45; + case 107: goto tr46; + case 108: goto tr47; + case 109: goto tr48; + case 110: goto tr49; + case 112: goto tr50; + case 114: goto tr51; + case 115: goto tr52; + case 116: goto tr53; + case 117: goto tr54; + case 1034: goto tr55; + case 1083: goto tr56; + } + goto tr36; +tr5: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + goto st4; +tr40: + { + s->r_class = s->default_class; + } + goto st4; +tr622: + { + s->r_ttl = s->default_ttl; + } + goto st4; +st4: + if ( ++p == pe ) + goto _test_eof4; +case 4: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr58; + case 32: goto tr58; + case 40: goto tr59; + case 41: goto tr60; + case 65: goto st230; + case 70: goto st233; + case 80: goto st237; + case 97: goto st230; + case 102: goto st233; + case 112: goto st237; + case 2058: goto tr64; + case 2107: goto tr65; + case 2314: goto tr66; + case 2363: goto tr66; + case 2570: goto tr67; + case 2619: goto tr68; + } + goto tr57; +tr72: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st5; +tr73: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st5; +tr75: + { + s->line_counter++; + } + goto st5; +tr58: + { s->r_type = KNOT_RRTYPE_A; } + { + rdata_tail = s->r_data; + } + goto st5; +tr59: + { s->r_type = KNOT_RRTYPE_A; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st5; +tr60: + { s->r_type = KNOT_RRTYPE_A; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st5; +tr64: + { s->r_type = KNOT_RRTYPE_A; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + goto st5; +tr96: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st5; +tr156: + { s->r_type = KNOT_RRTYPE_CAA; } + { + rdata_tail = s->r_data; + } + goto st5; +tr157: + { s->r_type = KNOT_RRTYPE_CAA; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st5; +tr158: + { s->r_type = KNOT_RRTYPE_CAA; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st5; +tr159: + { s->r_type = KNOT_RRTYPE_CAA; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + goto st5; +tr178: + { s->r_type = KNOT_RRTYPE_CDNSKEY; } + { + rdata_tail = s->r_data; + } + goto st5; +tr179: + { s->r_type = KNOT_RRTYPE_CDNSKEY; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st5; +tr180: + { s->r_type = KNOT_RRTYPE_CDNSKEY; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st5; +tr181: + { s->r_type = KNOT_RRTYPE_CDNSKEY; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + goto st5; +tr186: + { s->r_type = KNOT_RRTYPE_CDS; } + { + rdata_tail = s->r_data; + } + goto st5; +tr187: + { s->r_type = KNOT_RRTYPE_CDS; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st5; +tr188: + { s->r_type = KNOT_RRTYPE_CDS; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st5; +tr189: + { s->r_type = KNOT_RRTYPE_CDS; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + goto st5; +tr196: + { s->r_type = KNOT_RRTYPE_CERT; } + { + rdata_tail = s->r_data; + } + goto st5; +tr197: + { s->r_type = KNOT_RRTYPE_CERT; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st5; +tr198: + { s->r_type = KNOT_RRTYPE_CERT; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st5; +tr199: + { s->r_type = KNOT_RRTYPE_CERT; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + goto st5; +tr207: + { s->r_type = KNOT_RRTYPE_CNAME; } + { + rdata_tail = s->r_data; + } + goto st5; +tr208: + { s->r_type = KNOT_RRTYPE_CNAME; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st5; +tr209: + { s->r_type = KNOT_RRTYPE_CNAME; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st5; +tr210: + { s->r_type = KNOT_RRTYPE_CNAME; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + goto st5; +tr221: + { s->r_type = KNOT_RRTYPE_DHCID; } + { + rdata_tail = s->r_data; + } + goto st5; +tr222: + { s->r_type = KNOT_RRTYPE_DHCID; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st5; +tr223: + { s->r_type = KNOT_RRTYPE_DHCID; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st5; +tr224: + { s->r_type = KNOT_RRTYPE_DHCID; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + goto st5; +tr233: + { s->r_type = KNOT_RRTYPE_DNAME; } + { + rdata_tail = s->r_data; + } + goto st5; +tr234: + { s->r_type = KNOT_RRTYPE_DNAME; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st5; +tr235: + { s->r_type = KNOT_RRTYPE_DNAME; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st5; +tr236: + { s->r_type = KNOT_RRTYPE_DNAME; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + goto st5; +tr244: + { s->r_type = KNOT_RRTYPE_DNSKEY; } + { + rdata_tail = s->r_data; + } + goto st5; +tr245: + { s->r_type = KNOT_RRTYPE_DNSKEY; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st5; +tr246: + { s->r_type = KNOT_RRTYPE_DNSKEY; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st5; +tr247: + { s->r_type = KNOT_RRTYPE_DNSKEY; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + goto st5; +tr252: + { s->r_type = KNOT_RRTYPE_DS; } + { + rdata_tail = s->r_data; + } + goto st5; +tr253: + { s->r_type = KNOT_RRTYPE_DS; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st5; +tr254: + { s->r_type = KNOT_RRTYPE_DS; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st5; +tr255: + { s->r_type = KNOT_RRTYPE_DS; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + goto st5; +tr265: + { s->r_type = KNOT_RRTYPE_EUI48; } + { + rdata_tail = s->r_data; + } + goto st5; +tr266: + { s->r_type = KNOT_RRTYPE_EUI48; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st5; +tr267: + { s->r_type = KNOT_RRTYPE_EUI48; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st5; +tr268: + { s->r_type = KNOT_RRTYPE_EUI48; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + goto st5; +tr274: + { s->r_type = KNOT_RRTYPE_EUI64; } + { + rdata_tail = s->r_data; + } + goto st5; +tr275: + { s->r_type = KNOT_RRTYPE_EUI64; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st5; +tr276: + { s->r_type = KNOT_RRTYPE_EUI64; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st5; +tr277: + { s->r_type = KNOT_RRTYPE_EUI64; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + goto st5; +tr286: + { s->r_type = KNOT_RRTYPE_HINFO; } + { + rdata_tail = s->r_data; + } + goto st5; +tr287: + { s->r_type = KNOT_RRTYPE_HINFO; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st5; +tr288: + { s->r_type = KNOT_RRTYPE_HINFO; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st5; +tr289: + { s->r_type = KNOT_RRTYPE_HINFO; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + goto st5; +tr327: + { s->r_type = KNOT_RRTYPE_IPSECKEY; } + { + rdata_tail = s->r_data; + } + goto st5; +tr328: + { s->r_type = KNOT_RRTYPE_IPSECKEY; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st5; +tr329: + { s->r_type = KNOT_RRTYPE_IPSECKEY; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st5; +tr330: + { s->r_type = KNOT_RRTYPE_IPSECKEY; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + goto st5; +tr338: + { s->r_type = KNOT_RRTYPE_KEY; } + { + rdata_tail = s->r_data; + } + goto st5; +tr339: + { s->r_type = KNOT_RRTYPE_KEY; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st5; +tr340: + { s->r_type = KNOT_RRTYPE_KEY; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st5; +tr341: + { s->r_type = KNOT_RRTYPE_KEY; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + goto st5; +tr346: + { s->r_type = KNOT_RRTYPE_KX; } + { + rdata_tail = s->r_data; + } + goto st5; +tr347: + { s->r_type = KNOT_RRTYPE_KX; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st5; +tr348: + { s->r_type = KNOT_RRTYPE_KX; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st5; +tr349: + { s->r_type = KNOT_RRTYPE_KX; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + goto st5; +tr359: + { s->r_type = KNOT_RRTYPE_L32; } + { + rdata_tail = s->r_data; + } + goto st5; +tr360: + { s->r_type = KNOT_RRTYPE_L32; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st5; +tr361: + { s->r_type = KNOT_RRTYPE_L32; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st5; +tr362: + { s->r_type = KNOT_RRTYPE_L32; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + goto st5; +tr368: + { s->r_type = KNOT_RRTYPE_L64; } + { + rdata_tail = s->r_data; + } + goto st5; +tr369: + { s->r_type = KNOT_RRTYPE_L64; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st5; +tr370: + { s->r_type = KNOT_RRTYPE_L64; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st5; +tr371: + { s->r_type = KNOT_RRTYPE_L64; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + goto st5; +tr377: + { s->r_type = KNOT_RRTYPE_LOC; } + { + rdata_tail = s->r_data; + } + goto st5; +tr378: + { s->r_type = KNOT_RRTYPE_LOC; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st5; +tr379: + { s->r_type = KNOT_RRTYPE_LOC; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st5; +tr380: + { s->r_type = KNOT_RRTYPE_LOC; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + goto st5; +tr385: + { s->r_type = KNOT_RRTYPE_LP; } + { + rdata_tail = s->r_data; + } + goto st5; +tr386: + { s->r_type = KNOT_RRTYPE_LP; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st5; +tr387: + { s->r_type = KNOT_RRTYPE_LP; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st5; +tr388: + { s->r_type = KNOT_RRTYPE_LP; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + goto st5; +tr398: + { s->r_type = KNOT_RRTYPE_MINFO; } + { + rdata_tail = s->r_data; + } + goto st5; +tr399: + { s->r_type = KNOT_RRTYPE_MINFO; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st5; +tr400: + { s->r_type = KNOT_RRTYPE_MINFO; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st5; +tr401: + { s->r_type = KNOT_RRTYPE_MINFO; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + goto st5; +tr406: + { s->r_type = KNOT_RRTYPE_MX; } + { + rdata_tail = s->r_data; + } + goto st5; +tr407: + { s->r_type = KNOT_RRTYPE_MX; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st5; +tr408: + { s->r_type = KNOT_RRTYPE_MX; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st5; +tr409: + { s->r_type = KNOT_RRTYPE_MX; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + goto st5; +tr420: + { s->r_type = KNOT_RRTYPE_NAPTR; } + { + rdata_tail = s->r_data; + } + goto st5; +tr421: + { s->r_type = KNOT_RRTYPE_NAPTR; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st5; +tr422: + { s->r_type = KNOT_RRTYPE_NAPTR; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st5; +tr423: + { s->r_type = KNOT_RRTYPE_NAPTR; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + goto st5; +tr429: + { s->r_type = KNOT_RRTYPE_NID; } + { + rdata_tail = s->r_data; + } + goto st5; +tr430: + { s->r_type = KNOT_RRTYPE_NID; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st5; +tr431: + { s->r_type = KNOT_RRTYPE_NID; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st5; +tr432: + { s->r_type = KNOT_RRTYPE_NID; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + goto st5; +tr437: + { s->r_type = KNOT_RRTYPE_NS; } + { + rdata_tail = s->r_data; + } + goto st5; +tr438: + { s->r_type = KNOT_RRTYPE_NS; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st5; +tr439: + { s->r_type = KNOT_RRTYPE_NS; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st5; +tr441: + { s->r_type = KNOT_RRTYPE_NS; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + goto st5; +tr447: + { s->r_type = KNOT_RRTYPE_NSEC; } + { + rdata_tail = s->r_data; + } + goto st5; +tr448: + { s->r_type = KNOT_RRTYPE_NSEC; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st5; +tr449: + { s->r_type = KNOT_RRTYPE_NSEC; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st5; +tr451: + { s->r_type = KNOT_RRTYPE_NSEC; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + goto st5; +tr456: + { s->r_type = KNOT_RRTYPE_NSEC3; } + { + rdata_tail = s->r_data; + } + goto st5; +tr457: + { s->r_type = KNOT_RRTYPE_NSEC3; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st5; +tr458: + { s->r_type = KNOT_RRTYPE_NSEC3; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st5; +tr460: + { s->r_type = KNOT_RRTYPE_NSEC3; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + goto st5; +tr469: + { s->r_type = KNOT_RRTYPE_NSEC3PARAM; } + { + rdata_tail = s->r_data; + } + goto st5; +tr470: + { s->r_type = KNOT_RRTYPE_NSEC3PARAM; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st5; +tr471: + { s->r_type = KNOT_RRTYPE_NSEC3PARAM; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st5; +tr472: + { s->r_type = KNOT_RRTYPE_NSEC3PARAM; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + goto st5; +tr479: + { s->r_type = KNOT_RRTYPE_PTR; } + { + rdata_tail = s->r_data; + } + goto st5; +tr480: + { s->r_type = KNOT_RRTYPE_PTR; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st5; +tr481: + { s->r_type = KNOT_RRTYPE_PTR; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st5; +tr482: + { s->r_type = KNOT_RRTYPE_PTR; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + goto st5; +tr490: + { s->r_type = KNOT_RRTYPE_RP; } + { + rdata_tail = s->r_data; + } + goto st5; +tr491: + { s->r_type = KNOT_RRTYPE_RP; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st5; +tr492: + { s->r_type = KNOT_RRTYPE_RP; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st5; +tr493: + { s->r_type = KNOT_RRTYPE_RP; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + goto st5; +tr501: + { s->r_type = KNOT_RRTYPE_RRSIG; } + { + rdata_tail = s->r_data; + } + goto st5; +tr502: + { s->r_type = KNOT_RRTYPE_RRSIG; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st5; +tr503: + { s->r_type = KNOT_RRTYPE_RRSIG; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st5; +tr504: + { s->r_type = KNOT_RRTYPE_RRSIG; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + goto st5; +tr509: + { s->r_type = KNOT_RRTYPE_RT; } + { + rdata_tail = s->r_data; + } + goto st5; +tr510: + { s->r_type = KNOT_RRTYPE_RT; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st5; +tr511: + { s->r_type = KNOT_RRTYPE_RT; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st5; +tr512: + { s->r_type = KNOT_RRTYPE_RT; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + goto st5; +tr522: + { s->r_type = KNOT_RRTYPE_SOA; } + { + rdata_tail = s->r_data; + } + goto st5; +tr523: + { s->r_type = KNOT_RRTYPE_SOA; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st5; +tr524: + { s->r_type = KNOT_RRTYPE_SOA; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st5; +tr525: + { s->r_type = KNOT_RRTYPE_SOA; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + goto st5; +tr531: + { s->r_type = KNOT_RRTYPE_SPF; } + { + rdata_tail = s->r_data; + } + goto st5; +tr532: + { s->r_type = KNOT_RRTYPE_SPF; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st5; +tr533: + { s->r_type = KNOT_RRTYPE_SPF; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st5; +tr534: + { s->r_type = KNOT_RRTYPE_SPF; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + goto st5; +tr540: + { s->r_type = KNOT_RRTYPE_SRV; } + { + rdata_tail = s->r_data; + } + goto st5; +tr541: + { s->r_type = KNOT_RRTYPE_SRV; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st5; +tr542: + { s->r_type = KNOT_RRTYPE_SRV; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st5; +tr543: + { s->r_type = KNOT_RRTYPE_SRV; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + goto st5; +tr551: + { s->r_type = KNOT_RRTYPE_SSHFP; } + { + rdata_tail = s->r_data; + } + goto st5; +tr552: + { s->r_type = KNOT_RRTYPE_SSHFP; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st5; +tr553: + { s->r_type = KNOT_RRTYPE_SSHFP; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st5; +tr554: + { s->r_type = KNOT_RRTYPE_SSHFP; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + goto st5; +tr564: + { s->r_type = KNOT_RRTYPE_TLSA; } + { + rdata_tail = s->r_data; + } + goto st5; +tr565: + { s->r_type = KNOT_RRTYPE_TLSA; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st5; +tr566: + { s->r_type = KNOT_RRTYPE_TLSA; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st5; +tr567: + { s->r_type = KNOT_RRTYPE_TLSA; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + goto st5; +tr573: + { s->r_type = KNOT_RRTYPE_TXT; } + { + rdata_tail = s->r_data; + } + goto st5; +tr574: + { s->r_type = KNOT_RRTYPE_TXT; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st5; +tr575: + { s->r_type = KNOT_RRTYPE_TXT; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st5; +tr576: + { s->r_type = KNOT_RRTYPE_TXT; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + goto st5; +tr586: + { + if (s->number64 <= UINT16_MAX) { + s->r_type = (uint16_t)(s->number64); + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + rdata_tail = s->r_data; + } + goto st5; +tr587: + { + if (s->number64 <= UINT16_MAX) { + s->r_type = (uint16_t)(s->number64); + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st5; +tr588: + { + if (s->number64 <= UINT16_MAX) { + s->r_type = (uint16_t)(s->number64); + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st5; +tr590: + { + if (s->number64 <= UINT16_MAX) { + s->r_type = (uint16_t)(s->number64); + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + goto st5; +tr597: + { s->r_type = KNOT_RRTYPE_URI; } + { + rdata_tail = s->r_data; + } + goto st5; +tr598: + { s->r_type = KNOT_RRTYPE_URI; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st5; +tr599: + { s->r_type = KNOT_RRTYPE_URI; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st5; +tr600: + { s->r_type = KNOT_RRTYPE_URI; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + goto st5; +tr845: + { s->r_type = KNOT_RRTYPE_AAAA; } + { + rdata_tail = s->r_data; + } + goto st5; +tr846: + { s->r_type = KNOT_RRTYPE_AAAA; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st5; +tr847: + { s->r_type = KNOT_RRTYPE_AAAA; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st5; +tr848: + { s->r_type = KNOT_RRTYPE_AAAA; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + goto st5; +tr856: + { s->r_type = KNOT_RRTYPE_AFSDB; } + { + rdata_tail = s->r_data; + } + goto st5; +tr857: + { s->r_type = KNOT_RRTYPE_AFSDB; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st5; +tr858: + { s->r_type = KNOT_RRTYPE_AFSDB; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st5; +tr859: + { s->r_type = KNOT_RRTYPE_AFSDB; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + goto st5; +tr865: + { s->r_type = KNOT_RRTYPE_APL; } + { + rdata_tail = s->r_data; + } + goto st5; +tr866: + { s->r_type = KNOT_RRTYPE_APL; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st5; +tr867: + { s->r_type = KNOT_RRTYPE_APL; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st5; +tr868: + { s->r_type = KNOT_RRTYPE_APL; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + goto st5; +st5: + if ( ++p == pe ) + goto _test_eof5; +case 5: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto st5; + case 32: goto st5; + case 40: goto tr72; + case 41: goto tr73; + case 92: goto tr74; + case 2058: goto tr75; + case 2107: goto tr76; + case 2314: goto tr69; + case 2363: goto tr69; + case 2570: goto tr77; + case 2619: goto tr78; + } + if ( _widec < 11 ) { + if ( _widec <= 8 ) + goto tr69; + } else if ( _widec > 58 ) { + if ( 60 <= _widec ) + goto tr69; + } else + goto tr69; + goto tr71; +tr66: + { s->r_type = KNOT_RRTYPE_A; } + { + rdata_tail = s->r_data; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 6;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 6;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 6;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 6;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 6;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 6;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 6;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 6;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 6;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 6;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 6;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 6;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 6;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 6;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 6;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 6;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 6;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 6;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 6;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 6;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 6;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 6;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 6;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 6;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 6;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 6;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 6;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 6;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 6;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st6; +tr69: + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 6;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 6;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 6;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 6;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 6;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 6;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 6;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 6;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 6;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 6;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 6;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 6;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 6;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 6;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 6;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 6;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 6;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 6;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 6;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 6;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 6;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 6;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 6;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 6;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 6;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 6;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 6;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 6;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 6;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st6; +tr93: + { + if (*wrap == WRAP_NONE) { + p--; + } + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 6;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 6;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 6;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 6;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 6;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 6;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 6;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 6;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 6;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 6;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 6;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 6;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 6;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 6;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 6;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 6;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 6;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 6;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 6;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 6;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 6;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 6;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 6;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 6;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 6;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 6;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 6;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 6;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 6;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st6; +tr94: + { + switch (s->r_type) { + // Next types must not have empty rdata. + case KNOT_RRTYPE_A: + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + case KNOT_RRTYPE_SOA: + case KNOT_RRTYPE_HINFO: + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + case KNOT_RRTYPE_RP: + case KNOT_RRTYPE_AAAA: + case KNOT_RRTYPE_LOC: + case KNOT_RRTYPE_SRV: + case KNOT_RRTYPE_NAPTR: + case KNOT_RRTYPE_CERT: + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_SSHFP: + case KNOT_RRTYPE_IPSECKEY: + case KNOT_RRTYPE_RRSIG: + case KNOT_RRTYPE_NSEC: + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_DHCID: + case KNOT_RRTYPE_NSEC3: + case KNOT_RRTYPE_NSEC3PARAM: + case KNOT_RRTYPE_TLSA: + case KNOT_RRTYPE_CDS: + case KNOT_RRTYPE_CDNSKEY: + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L32: + case KNOT_RRTYPE_L64: + case KNOT_RRTYPE_LP: + case KNOT_RRTYPE_EUI48: + case KNOT_RRTYPE_EUI64: + case KNOT_RRTYPE_URI: + case KNOT_RRTYPE_CAA: + {stack[top++] = 6;goto st468;} + // Next types can have empty rdata. + case KNOT_RRTYPE_APL: + default: + {stack[top++] = 6;goto st477;} + } + } + goto st6; +tr161: + { s->r_type = KNOT_RRTYPE_CAA; } + { + rdata_tail = s->r_data; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 6;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 6;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 6;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 6;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 6;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 6;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 6;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 6;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 6;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 6;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 6;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 6;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 6;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 6;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 6;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 6;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 6;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 6;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 6;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 6;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 6;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 6;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 6;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 6;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 6;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 6;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 6;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 6;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 6;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st6; +tr183: + { s->r_type = KNOT_RRTYPE_CDNSKEY; } + { + rdata_tail = s->r_data; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 6;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 6;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 6;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 6;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 6;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 6;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 6;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 6;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 6;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 6;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 6;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 6;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 6;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 6;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 6;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 6;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 6;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 6;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 6;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 6;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 6;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 6;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 6;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 6;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 6;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 6;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 6;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 6;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 6;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st6; +tr191: + { s->r_type = KNOT_RRTYPE_CDS; } + { + rdata_tail = s->r_data; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 6;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 6;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 6;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 6;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 6;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 6;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 6;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 6;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 6;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 6;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 6;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 6;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 6;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 6;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 6;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 6;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 6;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 6;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 6;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 6;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 6;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 6;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 6;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 6;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 6;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 6;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 6;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 6;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 6;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st6; +tr201: + { s->r_type = KNOT_RRTYPE_CERT; } + { + rdata_tail = s->r_data; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 6;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 6;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 6;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 6;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 6;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 6;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 6;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 6;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 6;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 6;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 6;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 6;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 6;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 6;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 6;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 6;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 6;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 6;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 6;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 6;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 6;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 6;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 6;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 6;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 6;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 6;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 6;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 6;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 6;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st6; +tr212: + { s->r_type = KNOT_RRTYPE_CNAME; } + { + rdata_tail = s->r_data; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 6;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 6;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 6;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 6;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 6;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 6;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 6;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 6;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 6;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 6;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 6;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 6;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 6;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 6;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 6;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 6;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 6;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 6;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 6;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 6;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 6;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 6;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 6;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 6;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 6;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 6;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 6;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 6;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 6;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st6; +tr226: + { s->r_type = KNOT_RRTYPE_DHCID; } + { + rdata_tail = s->r_data; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 6;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 6;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 6;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 6;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 6;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 6;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 6;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 6;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 6;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 6;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 6;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 6;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 6;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 6;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 6;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 6;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 6;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 6;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 6;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 6;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 6;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 6;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 6;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 6;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 6;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 6;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 6;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 6;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 6;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st6; +tr238: + { s->r_type = KNOT_RRTYPE_DNAME; } + { + rdata_tail = s->r_data; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 6;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 6;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 6;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 6;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 6;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 6;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 6;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 6;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 6;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 6;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 6;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 6;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 6;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 6;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 6;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 6;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 6;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 6;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 6;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 6;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 6;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 6;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 6;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 6;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 6;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 6;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 6;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 6;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 6;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st6; +tr249: + { s->r_type = KNOT_RRTYPE_DNSKEY; } + { + rdata_tail = s->r_data; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 6;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 6;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 6;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 6;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 6;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 6;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 6;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 6;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 6;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 6;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 6;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 6;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 6;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 6;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 6;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 6;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 6;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 6;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 6;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 6;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 6;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 6;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 6;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 6;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 6;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 6;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 6;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 6;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 6;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st6; +tr257: + { s->r_type = KNOT_RRTYPE_DS; } + { + rdata_tail = s->r_data; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 6;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 6;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 6;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 6;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 6;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 6;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 6;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 6;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 6;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 6;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 6;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 6;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 6;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 6;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 6;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 6;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 6;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 6;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 6;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 6;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 6;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 6;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 6;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 6;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 6;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 6;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 6;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 6;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 6;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st6; +tr270: + { s->r_type = KNOT_RRTYPE_EUI48; } + { + rdata_tail = s->r_data; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 6;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 6;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 6;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 6;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 6;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 6;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 6;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 6;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 6;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 6;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 6;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 6;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 6;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 6;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 6;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 6;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 6;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 6;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 6;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 6;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 6;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 6;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 6;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 6;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 6;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 6;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 6;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 6;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 6;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st6; +tr279: + { s->r_type = KNOT_RRTYPE_EUI64; } + { + rdata_tail = s->r_data; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 6;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 6;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 6;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 6;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 6;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 6;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 6;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 6;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 6;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 6;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 6;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 6;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 6;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 6;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 6;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 6;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 6;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 6;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 6;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 6;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 6;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 6;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 6;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 6;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 6;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 6;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 6;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 6;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 6;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st6; +tr291: + { s->r_type = KNOT_RRTYPE_HINFO; } + { + rdata_tail = s->r_data; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 6;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 6;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 6;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 6;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 6;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 6;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 6;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 6;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 6;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 6;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 6;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 6;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 6;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 6;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 6;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 6;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 6;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 6;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 6;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 6;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 6;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 6;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 6;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 6;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 6;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 6;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 6;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 6;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 6;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st6; +tr332: + { s->r_type = KNOT_RRTYPE_IPSECKEY; } + { + rdata_tail = s->r_data; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 6;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 6;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 6;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 6;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 6;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 6;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 6;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 6;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 6;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 6;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 6;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 6;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 6;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 6;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 6;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 6;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 6;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 6;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 6;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 6;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 6;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 6;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 6;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 6;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 6;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 6;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 6;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 6;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 6;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st6; +tr343: + { s->r_type = KNOT_RRTYPE_KEY; } + { + rdata_tail = s->r_data; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 6;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 6;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 6;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 6;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 6;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 6;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 6;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 6;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 6;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 6;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 6;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 6;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 6;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 6;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 6;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 6;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 6;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 6;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 6;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 6;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 6;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 6;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 6;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 6;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 6;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 6;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 6;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 6;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 6;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st6; +tr351: + { s->r_type = KNOT_RRTYPE_KX; } + { + rdata_tail = s->r_data; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 6;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 6;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 6;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 6;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 6;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 6;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 6;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 6;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 6;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 6;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 6;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 6;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 6;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 6;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 6;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 6;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 6;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 6;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 6;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 6;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 6;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 6;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 6;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 6;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 6;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 6;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 6;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 6;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 6;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st6; +tr364: + { s->r_type = KNOT_RRTYPE_L32; } + { + rdata_tail = s->r_data; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 6;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 6;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 6;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 6;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 6;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 6;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 6;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 6;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 6;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 6;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 6;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 6;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 6;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 6;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 6;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 6;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 6;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 6;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 6;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 6;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 6;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 6;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 6;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 6;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 6;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 6;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 6;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 6;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 6;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st6; +tr373: + { s->r_type = KNOT_RRTYPE_L64; } + { + rdata_tail = s->r_data; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 6;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 6;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 6;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 6;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 6;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 6;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 6;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 6;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 6;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 6;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 6;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 6;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 6;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 6;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 6;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 6;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 6;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 6;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 6;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 6;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 6;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 6;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 6;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 6;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 6;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 6;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 6;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 6;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 6;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st6; +tr382: + { s->r_type = KNOT_RRTYPE_LOC; } + { + rdata_tail = s->r_data; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 6;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 6;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 6;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 6;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 6;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 6;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 6;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 6;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 6;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 6;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 6;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 6;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 6;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 6;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 6;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 6;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 6;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 6;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 6;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 6;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 6;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 6;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 6;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 6;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 6;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 6;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 6;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 6;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 6;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st6; +tr390: + { s->r_type = KNOT_RRTYPE_LP; } + { + rdata_tail = s->r_data; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 6;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 6;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 6;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 6;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 6;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 6;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 6;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 6;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 6;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 6;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 6;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 6;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 6;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 6;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 6;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 6;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 6;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 6;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 6;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 6;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 6;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 6;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 6;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 6;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 6;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 6;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 6;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 6;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 6;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st6; +tr403: + { s->r_type = KNOT_RRTYPE_MINFO; } + { + rdata_tail = s->r_data; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 6;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 6;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 6;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 6;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 6;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 6;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 6;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 6;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 6;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 6;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 6;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 6;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 6;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 6;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 6;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 6;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 6;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 6;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 6;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 6;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 6;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 6;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 6;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 6;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 6;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 6;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 6;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 6;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 6;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st6; +tr411: + { s->r_type = KNOT_RRTYPE_MX; } + { + rdata_tail = s->r_data; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 6;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 6;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 6;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 6;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 6;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 6;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 6;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 6;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 6;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 6;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 6;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 6;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 6;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 6;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 6;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 6;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 6;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 6;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 6;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 6;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 6;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 6;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 6;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 6;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 6;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 6;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 6;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 6;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 6;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st6; +tr425: + { s->r_type = KNOT_RRTYPE_NAPTR; } + { + rdata_tail = s->r_data; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 6;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 6;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 6;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 6;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 6;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 6;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 6;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 6;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 6;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 6;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 6;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 6;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 6;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 6;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 6;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 6;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 6;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 6;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 6;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 6;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 6;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 6;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 6;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 6;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 6;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 6;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 6;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 6;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 6;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st6; +tr434: + { s->r_type = KNOT_RRTYPE_NID; } + { + rdata_tail = s->r_data; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 6;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 6;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 6;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 6;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 6;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 6;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 6;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 6;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 6;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 6;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 6;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 6;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 6;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 6;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 6;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 6;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 6;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 6;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 6;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 6;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 6;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 6;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 6;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 6;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 6;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 6;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 6;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 6;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 6;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st6; +tr443: + { s->r_type = KNOT_RRTYPE_NS; } + { + rdata_tail = s->r_data; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 6;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 6;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 6;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 6;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 6;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 6;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 6;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 6;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 6;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 6;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 6;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 6;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 6;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 6;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 6;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 6;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 6;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 6;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 6;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 6;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 6;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 6;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 6;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 6;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 6;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 6;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 6;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 6;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 6;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st6; +tr453: + { s->r_type = KNOT_RRTYPE_NSEC; } + { + rdata_tail = s->r_data; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 6;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 6;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 6;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 6;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 6;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 6;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 6;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 6;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 6;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 6;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 6;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 6;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 6;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 6;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 6;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 6;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 6;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 6;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 6;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 6;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 6;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 6;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 6;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 6;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 6;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 6;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 6;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 6;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 6;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st6; +tr462: + { s->r_type = KNOT_RRTYPE_NSEC3; } + { + rdata_tail = s->r_data; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 6;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 6;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 6;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 6;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 6;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 6;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 6;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 6;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 6;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 6;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 6;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 6;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 6;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 6;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 6;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 6;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 6;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 6;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 6;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 6;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 6;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 6;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 6;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 6;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 6;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 6;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 6;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 6;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 6;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st6; +tr474: + { s->r_type = KNOT_RRTYPE_NSEC3PARAM; } + { + rdata_tail = s->r_data; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 6;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 6;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 6;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 6;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 6;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 6;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 6;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 6;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 6;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 6;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 6;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 6;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 6;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 6;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 6;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 6;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 6;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 6;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 6;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 6;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 6;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 6;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 6;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 6;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 6;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 6;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 6;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 6;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 6;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st6; +tr484: + { s->r_type = KNOT_RRTYPE_PTR; } + { + rdata_tail = s->r_data; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 6;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 6;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 6;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 6;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 6;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 6;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 6;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 6;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 6;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 6;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 6;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 6;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 6;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 6;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 6;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 6;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 6;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 6;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 6;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 6;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 6;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 6;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 6;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 6;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 6;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 6;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 6;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 6;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 6;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st6; +tr495: + { s->r_type = KNOT_RRTYPE_RP; } + { + rdata_tail = s->r_data; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 6;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 6;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 6;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 6;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 6;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 6;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 6;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 6;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 6;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 6;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 6;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 6;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 6;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 6;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 6;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 6;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 6;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 6;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 6;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 6;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 6;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 6;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 6;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 6;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 6;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 6;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 6;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 6;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 6;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st6; +tr506: + { s->r_type = KNOT_RRTYPE_RRSIG; } + { + rdata_tail = s->r_data; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 6;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 6;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 6;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 6;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 6;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 6;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 6;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 6;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 6;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 6;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 6;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 6;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 6;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 6;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 6;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 6;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 6;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 6;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 6;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 6;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 6;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 6;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 6;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 6;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 6;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 6;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 6;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 6;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 6;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st6; +tr514: + { s->r_type = KNOT_RRTYPE_RT; } + { + rdata_tail = s->r_data; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 6;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 6;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 6;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 6;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 6;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 6;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 6;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 6;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 6;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 6;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 6;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 6;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 6;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 6;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 6;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 6;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 6;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 6;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 6;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 6;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 6;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 6;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 6;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 6;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 6;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 6;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 6;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 6;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 6;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st6; +tr527: + { s->r_type = KNOT_RRTYPE_SOA; } + { + rdata_tail = s->r_data; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 6;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 6;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 6;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 6;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 6;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 6;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 6;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 6;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 6;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 6;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 6;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 6;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 6;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 6;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 6;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 6;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 6;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 6;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 6;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 6;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 6;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 6;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 6;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 6;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 6;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 6;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 6;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 6;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 6;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st6; +tr536: + { s->r_type = KNOT_RRTYPE_SPF; } + { + rdata_tail = s->r_data; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 6;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 6;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 6;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 6;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 6;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 6;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 6;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 6;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 6;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 6;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 6;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 6;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 6;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 6;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 6;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 6;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 6;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 6;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 6;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 6;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 6;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 6;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 6;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 6;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 6;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 6;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 6;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 6;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 6;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st6; +tr545: + { s->r_type = KNOT_RRTYPE_SRV; } + { + rdata_tail = s->r_data; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 6;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 6;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 6;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 6;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 6;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 6;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 6;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 6;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 6;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 6;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 6;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 6;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 6;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 6;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 6;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 6;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 6;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 6;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 6;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 6;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 6;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 6;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 6;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 6;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 6;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 6;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 6;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 6;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 6;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st6; +tr556: + { s->r_type = KNOT_RRTYPE_SSHFP; } + { + rdata_tail = s->r_data; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 6;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 6;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 6;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 6;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 6;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 6;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 6;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 6;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 6;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 6;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 6;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 6;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 6;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 6;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 6;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 6;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 6;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 6;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 6;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 6;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 6;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 6;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 6;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 6;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 6;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 6;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 6;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 6;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 6;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st6; +tr569: + { s->r_type = KNOT_RRTYPE_TLSA; } + { + rdata_tail = s->r_data; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 6;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 6;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 6;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 6;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 6;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 6;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 6;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 6;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 6;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 6;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 6;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 6;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 6;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 6;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 6;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 6;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 6;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 6;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 6;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 6;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 6;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 6;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 6;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 6;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 6;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 6;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 6;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 6;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 6;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st6; +tr578: + { s->r_type = KNOT_RRTYPE_TXT; } + { + rdata_tail = s->r_data; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 6;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 6;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 6;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 6;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 6;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 6;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 6;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 6;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 6;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 6;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 6;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 6;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 6;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 6;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 6;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 6;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 6;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 6;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 6;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 6;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 6;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 6;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 6;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 6;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 6;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 6;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 6;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 6;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 6;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st6; +tr592: + { + if (s->number64 <= UINT16_MAX) { + s->r_type = (uint16_t)(s->number64); + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + rdata_tail = s->r_data; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 6;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 6;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 6;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 6;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 6;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 6;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 6;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 6;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 6;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 6;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 6;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 6;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 6;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 6;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 6;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 6;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 6;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 6;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 6;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 6;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 6;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 6;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 6;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 6;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 6;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 6;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 6;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 6;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 6;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st6; +tr602: + { s->r_type = KNOT_RRTYPE_URI; } + { + rdata_tail = s->r_data; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 6;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 6;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 6;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 6;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 6;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 6;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 6;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 6;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 6;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 6;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 6;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 6;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 6;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 6;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 6;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 6;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 6;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 6;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 6;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 6;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 6;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 6;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 6;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 6;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 6;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 6;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 6;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 6;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 6;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st6; +tr850: + { s->r_type = KNOT_RRTYPE_AAAA; } + { + rdata_tail = s->r_data; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 6;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 6;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 6;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 6;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 6;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 6;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 6;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 6;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 6;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 6;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 6;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 6;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 6;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 6;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 6;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 6;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 6;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 6;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 6;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 6;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 6;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 6;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 6;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 6;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 6;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 6;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 6;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 6;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 6;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st6; +tr861: + { s->r_type = KNOT_RRTYPE_AFSDB; } + { + rdata_tail = s->r_data; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 6;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 6;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 6;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 6;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 6;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 6;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 6;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 6;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 6;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 6;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 6;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 6;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 6;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 6;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 6;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 6;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 6;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 6;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 6;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 6;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 6;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 6;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 6;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 6;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 6;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 6;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 6;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 6;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 6;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st6; +tr870: + { s->r_type = KNOT_RRTYPE_APL; } + { + rdata_tail = s->r_data; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 6;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 6;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 6;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 6;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 6;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 6;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 6;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 6;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 6;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 6;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 6;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 6;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 6;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 6;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 6;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 6;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 6;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 6;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 6;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 6;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 6;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 6;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 6;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 6;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 6;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 6;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 6;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 6;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 6;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st6; +st6: + if ( ++p == pe ) + goto _test_eof6; +case 6: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr80; + case 32: goto tr80; + case 40: goto tr81; + case 41: goto tr82; + case 778: goto tr83; + case 827: goto tr84; + case 1034: goto tr83; + case 1083: goto tr84; + } + goto tr79; +tr87: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st7; +tr88: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st7; +tr80: + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st7; +tr81: + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st7; +tr82: + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st7; +st7: + if ( ++p == pe ) + goto _test_eof7; +case 7: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st7; + case 32: goto st7; + case 40: goto tr87; + case 41: goto tr88; + case 778: goto tr89; + case 827: goto tr90; + case 1034: goto tr89; + case 1083: goto tr90; + } + goto tr85; +tr90: + { + s->buffer_length = 0; + } + goto st8; +tr84: + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + s->buffer_length = 0; + } + goto st8; +tr91: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st8; +tr761: + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + s->buffer_length = 0; + } + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st8; +tr765: + { + s->buffer_length = 0; + } + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st8; +st8: + if ( ++p == pe ) + goto _test_eof8; +case 8: + if ( (*p) == 10 ) + goto tr92; + goto tr91; +tr74: + { + if (pe - p == 1) { + *wrap = WRAP_DETECTED; + } + } + goto st9; +st9: + if ( ++p == pe ) + goto _test_eof9; +case 9: + if ( (*p) == 35 ) + goto tr94; + goto tr93; +tr76: + { + s->buffer_length = 0; + } + goto st10; +tr65: + { s->r_type = KNOT_RRTYPE_A; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + goto st10; +tr95: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st10; +tr160: + { s->r_type = KNOT_RRTYPE_CAA; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + goto st10; +tr182: + { s->r_type = KNOT_RRTYPE_CDNSKEY; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + goto st10; +tr190: + { s->r_type = KNOT_RRTYPE_CDS; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + goto st10; +tr200: + { s->r_type = KNOT_RRTYPE_CERT; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + goto st10; +tr211: + { s->r_type = KNOT_RRTYPE_CNAME; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + goto st10; +tr225: + { s->r_type = KNOT_RRTYPE_DHCID; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + goto st10; +tr237: + { s->r_type = KNOT_RRTYPE_DNAME; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + goto st10; +tr248: + { s->r_type = KNOT_RRTYPE_DNSKEY; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + goto st10; +tr256: + { s->r_type = KNOT_RRTYPE_DS; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + goto st10; +tr269: + { s->r_type = KNOT_RRTYPE_EUI48; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + goto st10; +tr278: + { s->r_type = KNOT_RRTYPE_EUI64; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + goto st10; +tr290: + { s->r_type = KNOT_RRTYPE_HINFO; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + goto st10; +tr331: + { s->r_type = KNOT_RRTYPE_IPSECKEY; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + goto st10; +tr342: + { s->r_type = KNOT_RRTYPE_KEY; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + goto st10; +tr350: + { s->r_type = KNOT_RRTYPE_KX; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + goto st10; +tr363: + { s->r_type = KNOT_RRTYPE_L32; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + goto st10; +tr372: + { s->r_type = KNOT_RRTYPE_L64; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + goto st10; +tr381: + { s->r_type = KNOT_RRTYPE_LOC; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + goto st10; +tr389: + { s->r_type = KNOT_RRTYPE_LP; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + goto st10; +tr402: + { s->r_type = KNOT_RRTYPE_MINFO; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + goto st10; +tr410: + { s->r_type = KNOT_RRTYPE_MX; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + goto st10; +tr424: + { s->r_type = KNOT_RRTYPE_NAPTR; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + goto st10; +tr433: + { s->r_type = KNOT_RRTYPE_NID; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + goto st10; +tr442: + { s->r_type = KNOT_RRTYPE_NS; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + goto st10; +tr452: + { s->r_type = KNOT_RRTYPE_NSEC; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + goto st10; +tr461: + { s->r_type = KNOT_RRTYPE_NSEC3; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + goto st10; +tr473: + { s->r_type = KNOT_RRTYPE_NSEC3PARAM; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + goto st10; +tr483: + { s->r_type = KNOT_RRTYPE_PTR; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + goto st10; +tr494: + { s->r_type = KNOT_RRTYPE_RP; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + goto st10; +tr505: + { s->r_type = KNOT_RRTYPE_RRSIG; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + goto st10; +tr513: + { s->r_type = KNOT_RRTYPE_RT; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + goto st10; +tr526: + { s->r_type = KNOT_RRTYPE_SOA; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + goto st10; +tr535: + { s->r_type = KNOT_RRTYPE_SPF; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + goto st10; +tr544: + { s->r_type = KNOT_RRTYPE_SRV; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + goto st10; +tr555: + { s->r_type = KNOT_RRTYPE_SSHFP; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + goto st10; +tr568: + { s->r_type = KNOT_RRTYPE_TLSA; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + goto st10; +tr577: + { s->r_type = KNOT_RRTYPE_TXT; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + goto st10; +tr591: + { + if (s->number64 <= UINT16_MAX) { + s->r_type = (uint16_t)(s->number64); + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + goto st10; +tr601: + { s->r_type = KNOT_RRTYPE_URI; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + goto st10; +tr849: + { s->r_type = KNOT_RRTYPE_AAAA; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + goto st10; +tr860: + { s->r_type = KNOT_RRTYPE_AFSDB; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + goto st10; +tr869: + { s->r_type = KNOT_RRTYPE_APL; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + goto st10; +st10: + if ( ++p == pe ) + goto _test_eof10; +case 10: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr96; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr95; + goto tr71; +tr67: + { s->r_type = KNOT_RRTYPE_A; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 11;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 11;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 11;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 11;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 11;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 11;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 11;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 11;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 11;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 11;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 11;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 11;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 11;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 11;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 11;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 11;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 11;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 11;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 11;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 11;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 11;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 11;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 11;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 11;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 11;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 11;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 11;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 11;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 11;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st11; +tr77: + { + s->line_counter++; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 11;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 11;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 11;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 11;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 11;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 11;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 11;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 11;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 11;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 11;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 11;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 11;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 11;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 11;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 11;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 11;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 11;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 11;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 11;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 11;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 11;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 11;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 11;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 11;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 11;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 11;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 11;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 11;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 11;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st11; +tr162: + { s->r_type = KNOT_RRTYPE_CAA; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 11;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 11;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 11;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 11;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 11;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 11;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 11;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 11;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 11;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 11;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 11;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 11;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 11;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 11;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 11;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 11;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 11;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 11;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 11;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 11;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 11;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 11;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 11;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 11;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 11;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 11;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 11;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 11;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 11;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st11; +tr184: + { s->r_type = KNOT_RRTYPE_CDNSKEY; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 11;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 11;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 11;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 11;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 11;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 11;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 11;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 11;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 11;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 11;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 11;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 11;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 11;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 11;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 11;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 11;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 11;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 11;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 11;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 11;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 11;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 11;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 11;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 11;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 11;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 11;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 11;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 11;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 11;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st11; +tr192: + { s->r_type = KNOT_RRTYPE_CDS; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 11;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 11;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 11;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 11;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 11;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 11;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 11;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 11;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 11;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 11;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 11;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 11;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 11;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 11;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 11;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 11;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 11;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 11;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 11;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 11;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 11;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 11;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 11;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 11;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 11;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 11;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 11;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 11;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 11;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st11; +tr202: + { s->r_type = KNOT_RRTYPE_CERT; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 11;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 11;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 11;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 11;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 11;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 11;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 11;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 11;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 11;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 11;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 11;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 11;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 11;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 11;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 11;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 11;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 11;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 11;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 11;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 11;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 11;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 11;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 11;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 11;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 11;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 11;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 11;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 11;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 11;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st11; +tr213: + { s->r_type = KNOT_RRTYPE_CNAME; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 11;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 11;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 11;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 11;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 11;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 11;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 11;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 11;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 11;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 11;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 11;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 11;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 11;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 11;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 11;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 11;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 11;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 11;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 11;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 11;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 11;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 11;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 11;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 11;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 11;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 11;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 11;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 11;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 11;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st11; +tr227: + { s->r_type = KNOT_RRTYPE_DHCID; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 11;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 11;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 11;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 11;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 11;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 11;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 11;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 11;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 11;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 11;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 11;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 11;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 11;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 11;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 11;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 11;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 11;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 11;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 11;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 11;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 11;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 11;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 11;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 11;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 11;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 11;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 11;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 11;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 11;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st11; +tr239: + { s->r_type = KNOT_RRTYPE_DNAME; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 11;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 11;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 11;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 11;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 11;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 11;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 11;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 11;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 11;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 11;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 11;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 11;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 11;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 11;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 11;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 11;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 11;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 11;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 11;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 11;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 11;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 11;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 11;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 11;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 11;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 11;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 11;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 11;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 11;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st11; +tr250: + { s->r_type = KNOT_RRTYPE_DNSKEY; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 11;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 11;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 11;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 11;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 11;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 11;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 11;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 11;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 11;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 11;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 11;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 11;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 11;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 11;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 11;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 11;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 11;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 11;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 11;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 11;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 11;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 11;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 11;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 11;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 11;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 11;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 11;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 11;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 11;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st11; +tr258: + { s->r_type = KNOT_RRTYPE_DS; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 11;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 11;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 11;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 11;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 11;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 11;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 11;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 11;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 11;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 11;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 11;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 11;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 11;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 11;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 11;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 11;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 11;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 11;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 11;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 11;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 11;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 11;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 11;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 11;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 11;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 11;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 11;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 11;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 11;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st11; +tr271: + { s->r_type = KNOT_RRTYPE_EUI48; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 11;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 11;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 11;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 11;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 11;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 11;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 11;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 11;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 11;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 11;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 11;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 11;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 11;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 11;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 11;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 11;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 11;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 11;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 11;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 11;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 11;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 11;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 11;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 11;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 11;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 11;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 11;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 11;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 11;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st11; +tr280: + { s->r_type = KNOT_RRTYPE_EUI64; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 11;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 11;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 11;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 11;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 11;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 11;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 11;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 11;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 11;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 11;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 11;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 11;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 11;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 11;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 11;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 11;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 11;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 11;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 11;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 11;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 11;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 11;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 11;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 11;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 11;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 11;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 11;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 11;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 11;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st11; +tr292: + { s->r_type = KNOT_RRTYPE_HINFO; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 11;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 11;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 11;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 11;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 11;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 11;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 11;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 11;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 11;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 11;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 11;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 11;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 11;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 11;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 11;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 11;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 11;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 11;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 11;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 11;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 11;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 11;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 11;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 11;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 11;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 11;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 11;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 11;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 11;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st11; +tr333: + { s->r_type = KNOT_RRTYPE_IPSECKEY; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 11;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 11;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 11;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 11;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 11;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 11;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 11;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 11;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 11;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 11;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 11;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 11;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 11;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 11;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 11;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 11;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 11;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 11;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 11;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 11;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 11;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 11;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 11;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 11;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 11;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 11;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 11;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 11;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 11;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st11; +tr344: + { s->r_type = KNOT_RRTYPE_KEY; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 11;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 11;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 11;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 11;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 11;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 11;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 11;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 11;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 11;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 11;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 11;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 11;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 11;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 11;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 11;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 11;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 11;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 11;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 11;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 11;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 11;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 11;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 11;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 11;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 11;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 11;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 11;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 11;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 11;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st11; +tr352: + { s->r_type = KNOT_RRTYPE_KX; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 11;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 11;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 11;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 11;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 11;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 11;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 11;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 11;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 11;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 11;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 11;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 11;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 11;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 11;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 11;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 11;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 11;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 11;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 11;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 11;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 11;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 11;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 11;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 11;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 11;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 11;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 11;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 11;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 11;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st11; +tr365: + { s->r_type = KNOT_RRTYPE_L32; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 11;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 11;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 11;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 11;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 11;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 11;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 11;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 11;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 11;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 11;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 11;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 11;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 11;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 11;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 11;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 11;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 11;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 11;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 11;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 11;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 11;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 11;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 11;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 11;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 11;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 11;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 11;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 11;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 11;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st11; +tr374: + { s->r_type = KNOT_RRTYPE_L64; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 11;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 11;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 11;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 11;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 11;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 11;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 11;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 11;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 11;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 11;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 11;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 11;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 11;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 11;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 11;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 11;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 11;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 11;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 11;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 11;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 11;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 11;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 11;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 11;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 11;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 11;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 11;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 11;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 11;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st11; +tr383: + { s->r_type = KNOT_RRTYPE_LOC; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 11;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 11;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 11;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 11;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 11;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 11;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 11;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 11;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 11;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 11;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 11;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 11;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 11;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 11;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 11;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 11;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 11;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 11;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 11;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 11;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 11;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 11;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 11;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 11;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 11;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 11;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 11;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 11;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 11;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st11; +tr391: + { s->r_type = KNOT_RRTYPE_LP; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 11;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 11;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 11;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 11;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 11;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 11;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 11;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 11;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 11;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 11;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 11;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 11;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 11;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 11;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 11;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 11;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 11;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 11;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 11;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 11;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 11;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 11;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 11;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 11;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 11;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 11;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 11;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 11;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 11;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st11; +tr404: + { s->r_type = KNOT_RRTYPE_MINFO; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 11;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 11;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 11;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 11;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 11;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 11;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 11;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 11;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 11;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 11;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 11;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 11;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 11;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 11;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 11;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 11;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 11;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 11;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 11;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 11;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 11;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 11;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 11;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 11;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 11;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 11;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 11;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 11;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 11;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st11; +tr412: + { s->r_type = KNOT_RRTYPE_MX; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 11;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 11;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 11;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 11;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 11;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 11;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 11;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 11;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 11;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 11;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 11;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 11;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 11;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 11;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 11;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 11;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 11;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 11;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 11;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 11;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 11;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 11;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 11;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 11;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 11;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 11;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 11;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 11;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 11;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st11; +tr426: + { s->r_type = KNOT_RRTYPE_NAPTR; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 11;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 11;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 11;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 11;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 11;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 11;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 11;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 11;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 11;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 11;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 11;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 11;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 11;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 11;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 11;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 11;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 11;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 11;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 11;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 11;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 11;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 11;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 11;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 11;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 11;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 11;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 11;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 11;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 11;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st11; +tr435: + { s->r_type = KNOT_RRTYPE_NID; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 11;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 11;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 11;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 11;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 11;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 11;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 11;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 11;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 11;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 11;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 11;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 11;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 11;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 11;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 11;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 11;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 11;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 11;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 11;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 11;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 11;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 11;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 11;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 11;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 11;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 11;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 11;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 11;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 11;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st11; +tr444: + { s->r_type = KNOT_RRTYPE_NS; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 11;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 11;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 11;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 11;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 11;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 11;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 11;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 11;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 11;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 11;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 11;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 11;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 11;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 11;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 11;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 11;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 11;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 11;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 11;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 11;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 11;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 11;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 11;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 11;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 11;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 11;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 11;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 11;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 11;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st11; +tr454: + { s->r_type = KNOT_RRTYPE_NSEC; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 11;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 11;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 11;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 11;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 11;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 11;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 11;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 11;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 11;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 11;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 11;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 11;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 11;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 11;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 11;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 11;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 11;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 11;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 11;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 11;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 11;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 11;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 11;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 11;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 11;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 11;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 11;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 11;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 11;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st11; +tr463: + { s->r_type = KNOT_RRTYPE_NSEC3; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 11;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 11;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 11;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 11;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 11;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 11;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 11;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 11;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 11;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 11;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 11;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 11;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 11;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 11;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 11;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 11;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 11;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 11;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 11;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 11;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 11;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 11;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 11;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 11;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 11;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 11;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 11;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 11;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 11;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st11; +tr475: + { s->r_type = KNOT_RRTYPE_NSEC3PARAM; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 11;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 11;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 11;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 11;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 11;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 11;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 11;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 11;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 11;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 11;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 11;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 11;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 11;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 11;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 11;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 11;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 11;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 11;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 11;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 11;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 11;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 11;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 11;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 11;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 11;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 11;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 11;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 11;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 11;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st11; +tr485: + { s->r_type = KNOT_RRTYPE_PTR; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 11;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 11;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 11;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 11;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 11;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 11;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 11;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 11;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 11;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 11;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 11;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 11;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 11;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 11;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 11;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 11;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 11;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 11;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 11;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 11;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 11;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 11;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 11;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 11;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 11;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 11;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 11;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 11;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 11;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st11; +tr496: + { s->r_type = KNOT_RRTYPE_RP; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 11;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 11;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 11;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 11;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 11;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 11;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 11;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 11;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 11;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 11;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 11;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 11;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 11;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 11;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 11;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 11;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 11;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 11;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 11;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 11;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 11;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 11;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 11;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 11;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 11;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 11;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 11;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 11;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 11;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st11; +tr507: + { s->r_type = KNOT_RRTYPE_RRSIG; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 11;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 11;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 11;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 11;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 11;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 11;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 11;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 11;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 11;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 11;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 11;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 11;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 11;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 11;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 11;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 11;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 11;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 11;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 11;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 11;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 11;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 11;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 11;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 11;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 11;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 11;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 11;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 11;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 11;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st11; +tr515: + { s->r_type = KNOT_RRTYPE_RT; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 11;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 11;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 11;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 11;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 11;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 11;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 11;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 11;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 11;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 11;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 11;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 11;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 11;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 11;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 11;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 11;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 11;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 11;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 11;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 11;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 11;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 11;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 11;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 11;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 11;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 11;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 11;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 11;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 11;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st11; +tr528: + { s->r_type = KNOT_RRTYPE_SOA; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 11;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 11;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 11;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 11;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 11;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 11;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 11;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 11;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 11;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 11;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 11;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 11;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 11;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 11;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 11;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 11;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 11;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 11;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 11;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 11;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 11;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 11;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 11;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 11;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 11;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 11;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 11;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 11;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 11;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st11; +tr537: + { s->r_type = KNOT_RRTYPE_SPF; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 11;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 11;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 11;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 11;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 11;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 11;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 11;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 11;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 11;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 11;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 11;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 11;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 11;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 11;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 11;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 11;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 11;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 11;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 11;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 11;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 11;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 11;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 11;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 11;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 11;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 11;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 11;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 11;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 11;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st11; +tr546: + { s->r_type = KNOT_RRTYPE_SRV; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 11;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 11;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 11;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 11;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 11;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 11;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 11;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 11;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 11;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 11;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 11;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 11;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 11;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 11;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 11;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 11;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 11;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 11;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 11;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 11;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 11;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 11;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 11;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 11;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 11;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 11;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 11;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 11;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 11;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st11; +tr557: + { s->r_type = KNOT_RRTYPE_SSHFP; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 11;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 11;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 11;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 11;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 11;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 11;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 11;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 11;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 11;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 11;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 11;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 11;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 11;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 11;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 11;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 11;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 11;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 11;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 11;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 11;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 11;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 11;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 11;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 11;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 11;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 11;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 11;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 11;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 11;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st11; +tr570: + { s->r_type = KNOT_RRTYPE_TLSA; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 11;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 11;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 11;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 11;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 11;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 11;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 11;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 11;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 11;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 11;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 11;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 11;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 11;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 11;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 11;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 11;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 11;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 11;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 11;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 11;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 11;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 11;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 11;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 11;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 11;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 11;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 11;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 11;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 11;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st11; +tr579: + { s->r_type = KNOT_RRTYPE_TXT; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 11;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 11;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 11;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 11;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 11;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 11;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 11;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 11;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 11;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 11;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 11;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 11;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 11;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 11;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 11;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 11;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 11;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 11;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 11;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 11;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 11;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 11;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 11;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 11;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 11;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 11;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 11;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 11;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 11;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st11; +tr593: + { + if (s->number64 <= UINT16_MAX) { + s->r_type = (uint16_t)(s->number64); + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 11;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 11;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 11;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 11;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 11;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 11;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 11;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 11;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 11;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 11;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 11;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 11;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 11;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 11;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 11;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 11;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 11;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 11;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 11;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 11;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 11;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 11;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 11;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 11;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 11;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 11;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 11;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 11;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 11;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st11; +tr603: + { s->r_type = KNOT_RRTYPE_URI; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 11;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 11;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 11;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 11;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 11;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 11;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 11;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 11;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 11;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 11;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 11;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 11;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 11;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 11;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 11;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 11;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 11;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 11;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 11;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 11;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 11;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 11;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 11;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 11;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 11;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 11;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 11;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 11;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 11;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st11; +tr851: + { s->r_type = KNOT_RRTYPE_AAAA; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 11;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 11;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 11;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 11;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 11;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 11;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 11;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 11;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 11;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 11;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 11;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 11;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 11;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 11;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 11;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 11;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 11;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 11;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 11;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 11;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 11;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 11;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 11;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 11;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 11;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 11;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 11;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 11;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 11;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st11; +tr862: + { s->r_type = KNOT_RRTYPE_AFSDB; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 11;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 11;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 11;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 11;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 11;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 11;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 11;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 11;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 11;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 11;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 11;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 11;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 11;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 11;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 11;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 11;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 11;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 11;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 11;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 11;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 11;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 11;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 11;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 11;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 11;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 11;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 11;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 11;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 11;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st11; +tr871: + { s->r_type = KNOT_RRTYPE_APL; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 11;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 11;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 11;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 11;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 11;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 11;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 11;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 11;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 11;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 11;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 11;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 11;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 11;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 11;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 11;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 11;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 11;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 11;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 11;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 11;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 11;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 11;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 11;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 11;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 11;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 11;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 11;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 11;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 11;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st11; +st11: + if ( ++p == pe ) + goto _test_eof11; +case 11: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr97; + case 32: goto tr97; + case 40: goto tr98; + case 41: goto tr99; + case 92: goto tr74; + case 1802: goto tr83; + case 1851: goto tr84; + case 2058: goto tr83; + case 2107: goto tr100; + case 2314: goto tr101; + case 2363: goto tr102; + case 2570: goto tr83; + case 2619: goto tr103; + } + if ( _widec < 11 ) { + if ( _widec <= 8 ) + goto tr69; + } else if ( _widec > 58 ) { + if ( 60 <= _widec ) + goto tr69; + } else + goto tr69; + goto tr79; +tr105: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st12; +tr106: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st12; +tr97: + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st12; +tr98: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st12; +tr99: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st12; +st12: + if ( ++p == pe ) + goto _test_eof12; +case 12: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto st12; + case 32: goto st12; + case 40: goto tr105; + case 41: goto tr106; + case 92: goto tr74; + case 1802: goto tr89; + case 1851: goto tr90; + case 2058: goto tr89; + case 2107: goto tr107; + case 2314: goto tr108; + case 2363: goto tr109; + case 2570: goto tr89; + case 2619: goto tr110; + } + if ( _widec < 11 ) { + if ( _widec <= 8 ) + goto tr69; + } else if ( _widec > 58 ) { + if ( 60 <= _widec ) + goto tr69; + } else + goto tr69; + goto tr79; +tr107: + { + s->buffer_length = 0; + } + goto st13; +tr111: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st13; +tr100: + { + s->buffer_length = 0; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st13; +tr167: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + s->buffer_length = 0; + } + goto st13; +tr171: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + { + s->buffer_length = 0; + } + goto st13; +tr740: + { s->r_type = KNOT_RRTYPE_A; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st13; +st13: + if ( ++p == pe ) + goto _test_eof13; +case 13: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 778: goto tr92; + case 1034: goto tr112; + } + if ( _widec > 895 ) { + if ( 896 <= _widec && _widec <= 1151 ) + goto tr111; + } else if ( _widec >= 640 ) + goto tr91; + goto tr79; +tr112: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + { + if (rdata_tail - s->r_data > UINT16_MAX) { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + s->r_data_length = rdata_tail - s->r_data; + + s->state = ZS_STATE_DATA; + + // Execute the record callback. + if (s->process.automatic) { + if (s->process.record != NULL) { + s->process.record(s); + + // Stop if required from the callback. + if (s->state == ZS_STATE_STOP) { + {p++; cs = 1128; goto _out;} + } + } + } else { + // Return if external processing. + p--; {p++; cs = 1128; goto _out;} + } + } + goto st1128; +tr748: + { + s->line_counter++; + } + { + if (rdata_tail - s->r_data > UINT16_MAX) { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + s->r_data_length = rdata_tail - s->r_data; + + s->state = ZS_STATE_DATA; + + // Execute the record callback. + if (s->process.automatic) { + if (s->process.record != NULL) { + s->process.record(s); + + // Stop if required from the callback. + if (s->state == ZS_STATE_STOP) { + {p++; cs = 1128; goto _out;} + } + } + } else { + // Return if external processing. + p--; {p++; cs = 1128; goto _out;} + } + } + goto st1128; +tr739: + { s->r_type = KNOT_RRTYPE_A; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + if (rdata_tail - s->r_data > UINT16_MAX) { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + s->r_data_length = rdata_tail - s->r_data; + + s->state = ZS_STATE_DATA; + + // Execute the record callback. + if (s->process.automatic) { + if (s->process.record != NULL) { + s->process.record(s); + + // Stop if required from the callback. + if (s->state == ZS_STATE_STOP) { + {p++; cs = 1128; goto _out;} + } + } + } else { + // Return if external processing. + p--; {p++; cs = 1128; goto _out;} + } + } + goto st1128; +st1128: + if ( ++p == pe ) + goto _test_eof1128; +case 1128: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr3622; + case 32: goto tr3622; + case 36: goto tr3623; + case 40: goto tr3624; + case 41: goto tr3625; + case 42: goto tr3626; + case 58: goto tr69; + case 92: goto tr3627; + case 95: goto tr3626; + case 1802: goto tr3618; + case 1851: goto tr3619; + case 2058: goto tr3628; + case 2107: goto tr3629; + case 2314: goto tr3630; + case 2363: goto tr3631; + case 2570: goto tr3632; + case 2619: goto tr3633; + } + if ( _widec < 60 ) { + if ( _widec < 11 ) { + if ( _widec <= 8 ) + goto tr69; + } else if ( _widec > 44 ) { + if ( 45 <= _widec && _widec <= 57 ) + goto tr3626; + } else + goto tr69; + } else if ( _widec > 63 ) { + if ( _widec < 91 ) { + if ( 64 <= _widec && _widec <= 90 ) + goto tr3626; + } else if ( _widec > 96 ) { + if ( _widec > 122 ) { + if ( 123 <= _widec ) + goto tr69; + } else if ( _widec >= 97 ) + goto tr3626; + } else + goto tr69; + } else + goto tr69; + goto tr783; +tr115: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st14; +tr116: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st14; +tr3622: + { + if (s->r_owner_length == 0) { + WARN(ZS_BAD_PREVIOUS_OWNER); + p--; {goto st268;} + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st14; +tr3624: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + { + if (s->r_owner_length == 0) { + WARN(ZS_BAD_PREVIOUS_OWNER); + p--; {goto st268;} + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st14; +tr3625: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + { + if (s->r_owner_length == 0) { + WARN(ZS_BAD_PREVIOUS_OWNER); + p--; {goto st268;} + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st14; +st14: + if ( ++p == pe ) + goto _test_eof14; +case 14: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto st14; + case 32: goto st14; + case 40: goto tr115; + case 41: goto tr116; + case 58: goto tr69; + case 65: goto tr118; + case 67: goto tr119; + case 68: goto tr120; + case 69: goto tr121; + case 72: goto tr122; + case 73: goto tr123; + case 75: goto tr124; + case 76: goto tr125; + case 77: goto tr126; + case 78: goto tr127; + case 80: goto tr128; + case 82: goto tr129; + case 83: goto tr130; + case 84: goto tr131; + case 85: goto tr132; + case 92: goto tr74; + case 97: goto tr118; + case 99: goto tr119; + case 100: goto tr120; + case 101: goto tr121; + case 104: goto tr122; + case 105: goto tr123; + case 107: goto tr124; + case 108: goto tr125; + case 109: goto tr126; + case 110: goto tr127; + case 112: goto tr128; + case 114: goto tr129; + case 115: goto tr130; + case 116: goto tr131; + case 117: goto tr132; + case 1802: goto tr20; + case 1851: goto tr21; + case 2058: goto tr133; + case 2107: goto tr134; + case 2314: goto tr135; + case 2363: goto tr136; + case 2570: goto tr137; + case 2619: goto tr138; + } + if ( _widec < 11 ) { + if ( _widec <= 8 ) + goto tr69; + } else if ( _widec > 47 ) { + if ( _widec > 57 ) { + if ( 60 <= _widec ) + goto tr69; + } else if ( _widec >= 48 ) + goto tr117; + } else + goto tr69; + goto tr114; +tr117: + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 15;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 15;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 15;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 15;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 15;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 15;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 15;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 15;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 15;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 15;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 15;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 15;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 15;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 15;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 15;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 15;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 15;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 15;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 15;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 15;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 15;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 15;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 15;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 15;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 15;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 15;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 15;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 15;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 15;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st15; +st15: + if ( ++p == pe ) + goto _test_eof15; +case 15: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr140; + case 32: goto tr140; + case 40: goto tr141; + case 41: goto tr142; + case 68: goto tr29; + case 72: goto tr30; + case 77: goto tr31; + case 83: goto st166; + case 87: goto tr33; + case 100: goto tr29; + case 104: goto tr30; + case 109: goto tr31; + case 115: goto st166; + case 119: goto tr33; + case 778: goto tr83; + case 827: goto tr84; + case 1034: goto tr143; + case 1083: goto tr144; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr28; + goto tr139; +tr147: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st16; +tr148: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st16; +tr140: + { + if (s->number64 <= UINT32_MAX) { + s->r_ttl = (uint32_t)(s->number64); + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st16; +tr141: + { + if (s->number64 <= UINT32_MAX) { + s->r_ttl = (uint32_t)(s->number64); + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st16; +tr142: + { + if (s->number64 <= UINT32_MAX) { + s->r_ttl = (uint32_t)(s->number64); + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st16; +st16: + if ( ++p == pe ) + goto _test_eof16; +case 16: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st16; + case 32: goto st16; + case 40: goto tr147; + case 41: goto tr148; + case 65: goto tr40; + case 67: goto tr41; + case 68: goto tr42; + case 69: goto tr43; + case 72: goto tr44; + case 73: goto tr45; + case 75: goto tr46; + case 76: goto tr47; + case 77: goto tr48; + case 78: goto tr49; + case 80: goto tr50; + case 82: goto tr51; + case 83: goto tr52; + case 84: goto tr53; + case 85: goto tr54; + case 97: goto tr40; + case 99: goto tr41; + case 100: goto tr42; + case 101: goto tr43; + case 104: goto tr44; + case 105: goto tr45; + case 107: goto tr46; + case 108: goto tr47; + case 109: goto tr48; + case 110: goto tr49; + case 112: goto tr50; + case 114: goto tr51; + case 115: goto tr52; + case 116: goto tr53; + case 117: goto tr54; + case 778: goto tr89; + case 827: goto tr90; + case 1034: goto tr149; + case 1083: goto tr150; + } + goto tr145; +tr6: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + goto st17; +tr41: + { + s->r_class = s->default_class; + } + goto st17; +tr623: + { + s->r_ttl = s->default_ttl; + } + goto st17; +st17: + if ( ++p == pe ) + goto _test_eof17; +case 17: + switch( (*p) ) { + case 65: goto st18; + case 68: goto st22; + case 69: goto st29; + case 78: goto st32; + case 97: goto st18; + case 100: goto st22; + case 101: goto st29; + case 110: goto st32; + } + goto tr36; +st18: + if ( ++p == pe ) + goto _test_eof18; +case 18: + switch( (*p) ) { + case 65: goto st19; + case 97: goto st19; + } + goto tr36; +st19: + if ( ++p == pe ) + goto _test_eof19; +case 19: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr156; + case 32: goto tr156; + case 40: goto tr157; + case 41: goto tr158; + case 2058: goto tr159; + case 2107: goto tr160; + case 2314: goto tr161; + case 2363: goto tr161; + case 2570: goto tr162; + case 2619: goto tr163; + } + goto tr57; +tr68: + { s->r_type = KNOT_RRTYPE_A; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 20;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 20;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 20;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 20;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 20;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 20;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 20;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 20;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 20;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 20;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 20;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 20;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 20;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 20;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 20;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 20;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 20;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 20;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 20;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 20;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 20;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 20;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 20;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 20;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 20;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 20;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 20;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 20;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 20;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st20; +tr78: + { + s->buffer_length = 0; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 20;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 20;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 20;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 20;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 20;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 20;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 20;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 20;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 20;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 20;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 20;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 20;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 20;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 20;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 20;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 20;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 20;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 20;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 20;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 20;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 20;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 20;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 20;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 20;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 20;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 20;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 20;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 20;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 20;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st20; +tr163: + { s->r_type = KNOT_RRTYPE_CAA; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 20;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 20;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 20;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 20;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 20;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 20;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 20;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 20;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 20;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 20;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 20;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 20;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 20;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 20;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 20;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 20;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 20;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 20;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 20;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 20;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 20;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 20;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 20;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 20;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 20;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 20;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 20;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 20;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 20;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st20; +tr185: + { s->r_type = KNOT_RRTYPE_CDNSKEY; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 20;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 20;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 20;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 20;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 20;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 20;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 20;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 20;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 20;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 20;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 20;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 20;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 20;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 20;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 20;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 20;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 20;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 20;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 20;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 20;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 20;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 20;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 20;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 20;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 20;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 20;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 20;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 20;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 20;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st20; +tr193: + { s->r_type = KNOT_RRTYPE_CDS; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 20;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 20;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 20;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 20;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 20;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 20;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 20;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 20;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 20;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 20;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 20;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 20;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 20;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 20;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 20;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 20;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 20;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 20;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 20;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 20;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 20;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 20;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 20;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 20;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 20;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 20;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 20;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 20;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 20;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st20; +tr203: + { s->r_type = KNOT_RRTYPE_CERT; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 20;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 20;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 20;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 20;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 20;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 20;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 20;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 20;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 20;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 20;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 20;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 20;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 20;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 20;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 20;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 20;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 20;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 20;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 20;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 20;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 20;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 20;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 20;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 20;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 20;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 20;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 20;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 20;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 20;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st20; +tr214: + { s->r_type = KNOT_RRTYPE_CNAME; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 20;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 20;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 20;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 20;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 20;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 20;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 20;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 20;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 20;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 20;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 20;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 20;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 20;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 20;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 20;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 20;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 20;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 20;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 20;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 20;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 20;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 20;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 20;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 20;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 20;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 20;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 20;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 20;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 20;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st20; +tr228: + { s->r_type = KNOT_RRTYPE_DHCID; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 20;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 20;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 20;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 20;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 20;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 20;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 20;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 20;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 20;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 20;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 20;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 20;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 20;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 20;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 20;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 20;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 20;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 20;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 20;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 20;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 20;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 20;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 20;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 20;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 20;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 20;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 20;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 20;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 20;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st20; +tr240: + { s->r_type = KNOT_RRTYPE_DNAME; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 20;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 20;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 20;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 20;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 20;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 20;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 20;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 20;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 20;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 20;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 20;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 20;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 20;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 20;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 20;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 20;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 20;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 20;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 20;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 20;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 20;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 20;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 20;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 20;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 20;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 20;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 20;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 20;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 20;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st20; +tr251: + { s->r_type = KNOT_RRTYPE_DNSKEY; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 20;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 20;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 20;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 20;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 20;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 20;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 20;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 20;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 20;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 20;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 20;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 20;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 20;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 20;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 20;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 20;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 20;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 20;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 20;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 20;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 20;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 20;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 20;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 20;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 20;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 20;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 20;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 20;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 20;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st20; +tr259: + { s->r_type = KNOT_RRTYPE_DS; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 20;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 20;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 20;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 20;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 20;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 20;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 20;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 20;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 20;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 20;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 20;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 20;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 20;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 20;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 20;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 20;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 20;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 20;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 20;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 20;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 20;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 20;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 20;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 20;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 20;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 20;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 20;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 20;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 20;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st20; +tr272: + { s->r_type = KNOT_RRTYPE_EUI48; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 20;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 20;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 20;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 20;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 20;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 20;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 20;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 20;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 20;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 20;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 20;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 20;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 20;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 20;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 20;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 20;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 20;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 20;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 20;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 20;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 20;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 20;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 20;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 20;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 20;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 20;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 20;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 20;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 20;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st20; +tr281: + { s->r_type = KNOT_RRTYPE_EUI64; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 20;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 20;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 20;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 20;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 20;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 20;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 20;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 20;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 20;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 20;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 20;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 20;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 20;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 20;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 20;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 20;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 20;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 20;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 20;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 20;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 20;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 20;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 20;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 20;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 20;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 20;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 20;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 20;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 20;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st20; +tr293: + { s->r_type = KNOT_RRTYPE_HINFO; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 20;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 20;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 20;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 20;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 20;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 20;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 20;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 20;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 20;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 20;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 20;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 20;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 20;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 20;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 20;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 20;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 20;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 20;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 20;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 20;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 20;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 20;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 20;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 20;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 20;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 20;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 20;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 20;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 20;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st20; +tr334: + { s->r_type = KNOT_RRTYPE_IPSECKEY; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 20;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 20;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 20;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 20;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 20;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 20;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 20;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 20;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 20;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 20;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 20;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 20;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 20;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 20;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 20;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 20;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 20;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 20;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 20;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 20;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 20;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 20;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 20;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 20;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 20;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 20;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 20;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 20;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 20;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st20; +tr345: + { s->r_type = KNOT_RRTYPE_KEY; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 20;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 20;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 20;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 20;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 20;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 20;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 20;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 20;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 20;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 20;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 20;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 20;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 20;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 20;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 20;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 20;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 20;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 20;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 20;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 20;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 20;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 20;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 20;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 20;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 20;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 20;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 20;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 20;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 20;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st20; +tr353: + { s->r_type = KNOT_RRTYPE_KX; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 20;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 20;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 20;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 20;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 20;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 20;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 20;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 20;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 20;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 20;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 20;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 20;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 20;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 20;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 20;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 20;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 20;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 20;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 20;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 20;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 20;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 20;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 20;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 20;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 20;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 20;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 20;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 20;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 20;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st20; +tr366: + { s->r_type = KNOT_RRTYPE_L32; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 20;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 20;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 20;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 20;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 20;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 20;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 20;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 20;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 20;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 20;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 20;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 20;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 20;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 20;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 20;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 20;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 20;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 20;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 20;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 20;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 20;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 20;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 20;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 20;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 20;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 20;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 20;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 20;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 20;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st20; +tr375: + { s->r_type = KNOT_RRTYPE_L64; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 20;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 20;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 20;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 20;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 20;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 20;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 20;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 20;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 20;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 20;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 20;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 20;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 20;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 20;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 20;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 20;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 20;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 20;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 20;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 20;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 20;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 20;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 20;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 20;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 20;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 20;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 20;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 20;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 20;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st20; +tr384: + { s->r_type = KNOT_RRTYPE_LOC; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 20;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 20;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 20;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 20;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 20;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 20;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 20;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 20;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 20;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 20;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 20;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 20;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 20;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 20;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 20;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 20;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 20;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 20;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 20;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 20;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 20;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 20;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 20;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 20;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 20;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 20;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 20;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 20;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 20;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st20; +tr392: + { s->r_type = KNOT_RRTYPE_LP; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 20;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 20;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 20;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 20;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 20;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 20;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 20;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 20;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 20;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 20;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 20;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 20;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 20;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 20;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 20;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 20;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 20;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 20;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 20;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 20;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 20;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 20;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 20;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 20;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 20;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 20;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 20;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 20;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 20;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st20; +tr405: + { s->r_type = KNOT_RRTYPE_MINFO; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 20;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 20;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 20;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 20;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 20;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 20;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 20;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 20;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 20;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 20;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 20;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 20;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 20;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 20;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 20;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 20;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 20;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 20;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 20;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 20;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 20;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 20;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 20;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 20;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 20;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 20;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 20;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 20;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 20;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st20; +tr413: + { s->r_type = KNOT_RRTYPE_MX; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 20;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 20;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 20;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 20;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 20;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 20;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 20;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 20;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 20;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 20;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 20;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 20;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 20;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 20;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 20;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 20;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 20;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 20;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 20;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 20;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 20;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 20;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 20;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 20;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 20;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 20;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 20;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 20;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 20;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st20; +tr427: + { s->r_type = KNOT_RRTYPE_NAPTR; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 20;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 20;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 20;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 20;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 20;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 20;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 20;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 20;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 20;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 20;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 20;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 20;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 20;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 20;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 20;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 20;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 20;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 20;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 20;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 20;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 20;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 20;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 20;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 20;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 20;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 20;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 20;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 20;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 20;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st20; +tr436: + { s->r_type = KNOT_RRTYPE_NID; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 20;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 20;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 20;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 20;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 20;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 20;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 20;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 20;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 20;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 20;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 20;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 20;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 20;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 20;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 20;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 20;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 20;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 20;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 20;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 20;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 20;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 20;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 20;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 20;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 20;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 20;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 20;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 20;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 20;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st20; +tr445: + { s->r_type = KNOT_RRTYPE_NS; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 20;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 20;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 20;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 20;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 20;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 20;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 20;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 20;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 20;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 20;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 20;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 20;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 20;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 20;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 20;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 20;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 20;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 20;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 20;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 20;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 20;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 20;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 20;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 20;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 20;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 20;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 20;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 20;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 20;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st20; +tr455: + { s->r_type = KNOT_RRTYPE_NSEC; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 20;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 20;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 20;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 20;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 20;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 20;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 20;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 20;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 20;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 20;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 20;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 20;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 20;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 20;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 20;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 20;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 20;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 20;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 20;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 20;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 20;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 20;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 20;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 20;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 20;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 20;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 20;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 20;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 20;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st20; +tr464: + { s->r_type = KNOT_RRTYPE_NSEC3; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 20;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 20;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 20;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 20;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 20;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 20;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 20;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 20;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 20;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 20;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 20;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 20;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 20;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 20;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 20;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 20;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 20;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 20;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 20;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 20;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 20;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 20;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 20;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 20;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 20;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 20;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 20;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 20;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 20;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st20; +tr476: + { s->r_type = KNOT_RRTYPE_NSEC3PARAM; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 20;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 20;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 20;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 20;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 20;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 20;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 20;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 20;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 20;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 20;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 20;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 20;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 20;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 20;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 20;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 20;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 20;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 20;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 20;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 20;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 20;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 20;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 20;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 20;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 20;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 20;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 20;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 20;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 20;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st20; +tr486: + { s->r_type = KNOT_RRTYPE_PTR; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 20;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 20;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 20;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 20;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 20;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 20;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 20;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 20;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 20;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 20;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 20;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 20;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 20;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 20;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 20;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 20;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 20;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 20;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 20;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 20;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 20;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 20;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 20;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 20;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 20;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 20;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 20;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 20;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 20;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st20; +tr497: + { s->r_type = KNOT_RRTYPE_RP; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 20;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 20;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 20;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 20;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 20;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 20;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 20;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 20;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 20;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 20;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 20;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 20;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 20;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 20;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 20;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 20;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 20;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 20;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 20;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 20;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 20;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 20;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 20;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 20;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 20;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 20;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 20;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 20;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 20;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st20; +tr508: + { s->r_type = KNOT_RRTYPE_RRSIG; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 20;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 20;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 20;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 20;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 20;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 20;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 20;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 20;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 20;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 20;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 20;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 20;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 20;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 20;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 20;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 20;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 20;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 20;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 20;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 20;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 20;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 20;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 20;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 20;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 20;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 20;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 20;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 20;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 20;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st20; +tr516: + { s->r_type = KNOT_RRTYPE_RT; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 20;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 20;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 20;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 20;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 20;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 20;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 20;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 20;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 20;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 20;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 20;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 20;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 20;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 20;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 20;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 20;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 20;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 20;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 20;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 20;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 20;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 20;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 20;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 20;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 20;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 20;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 20;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 20;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 20;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st20; +tr529: + { s->r_type = KNOT_RRTYPE_SOA; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 20;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 20;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 20;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 20;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 20;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 20;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 20;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 20;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 20;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 20;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 20;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 20;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 20;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 20;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 20;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 20;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 20;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 20;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 20;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 20;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 20;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 20;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 20;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 20;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 20;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 20;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 20;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 20;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 20;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st20; +tr538: + { s->r_type = KNOT_RRTYPE_SPF; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 20;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 20;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 20;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 20;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 20;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 20;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 20;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 20;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 20;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 20;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 20;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 20;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 20;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 20;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 20;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 20;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 20;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 20;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 20;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 20;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 20;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 20;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 20;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 20;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 20;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 20;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 20;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 20;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 20;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st20; +tr547: + { s->r_type = KNOT_RRTYPE_SRV; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 20;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 20;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 20;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 20;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 20;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 20;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 20;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 20;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 20;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 20;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 20;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 20;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 20;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 20;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 20;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 20;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 20;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 20;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 20;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 20;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 20;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 20;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 20;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 20;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 20;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 20;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 20;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 20;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 20;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st20; +tr558: + { s->r_type = KNOT_RRTYPE_SSHFP; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 20;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 20;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 20;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 20;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 20;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 20;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 20;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 20;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 20;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 20;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 20;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 20;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 20;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 20;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 20;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 20;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 20;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 20;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 20;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 20;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 20;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 20;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 20;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 20;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 20;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 20;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 20;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 20;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 20;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st20; +tr571: + { s->r_type = KNOT_RRTYPE_TLSA; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 20;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 20;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 20;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 20;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 20;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 20;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 20;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 20;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 20;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 20;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 20;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 20;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 20;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 20;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 20;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 20;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 20;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 20;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 20;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 20;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 20;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 20;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 20;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 20;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 20;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 20;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 20;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 20;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 20;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st20; +tr580: + { s->r_type = KNOT_RRTYPE_TXT; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 20;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 20;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 20;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 20;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 20;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 20;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 20;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 20;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 20;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 20;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 20;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 20;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 20;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 20;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 20;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 20;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 20;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 20;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 20;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 20;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 20;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 20;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 20;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 20;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 20;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 20;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 20;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 20;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 20;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st20; +tr594: + { + if (s->number64 <= UINT16_MAX) { + s->r_type = (uint16_t)(s->number64); + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 20;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 20;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 20;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 20;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 20;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 20;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 20;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 20;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 20;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 20;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 20;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 20;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 20;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 20;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 20;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 20;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 20;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 20;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 20;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 20;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 20;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 20;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 20;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 20;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 20;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 20;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 20;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 20;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 20;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st20; +tr604: + { s->r_type = KNOT_RRTYPE_URI; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 20;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 20;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 20;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 20;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 20;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 20;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 20;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 20;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 20;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 20;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 20;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 20;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 20;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 20;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 20;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 20;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 20;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 20;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 20;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 20;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 20;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 20;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 20;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 20;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 20;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 20;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 20;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 20;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 20;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st20; +tr852: + { s->r_type = KNOT_RRTYPE_AAAA; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 20;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 20;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 20;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 20;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 20;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 20;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 20;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 20;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 20;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 20;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 20;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 20;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 20;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 20;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 20;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 20;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 20;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 20;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 20;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 20;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 20;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 20;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 20;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 20;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 20;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 20;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 20;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 20;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 20;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st20; +tr863: + { s->r_type = KNOT_RRTYPE_AFSDB; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 20;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 20;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 20;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 20;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 20;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 20;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 20;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 20;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 20;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 20;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 20;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 20;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 20;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 20;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 20;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 20;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 20;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 20;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 20;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 20;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 20;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 20;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 20;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 20;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 20;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 20;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 20;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 20;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 20;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st20; +tr872: + { s->r_type = KNOT_RRTYPE_APL; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 20;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 20;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 20;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 20;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 20;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 20;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 20;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 20;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 20;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 20;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 20;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 20;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 20;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 20;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 20;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 20;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 20;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 20;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 20;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 20;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 20;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 20;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 20;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 20;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 20;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 20;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 20;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 20;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 20;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st20; +st20: + if ( ++p == pe ) + goto _test_eof20; +case 20: + _widec = (*p); + if ( (*p) < 11 ) { + if ( (*p) > 9 ) { + if ( 10 <= (*p) && (*p) <= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 58 ) { + if ( (*p) > 59 ) { + if ( 60 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 777: goto tr80; + case 778: goto tr83; + case 800: goto tr80; + case 808: goto tr81; + case 809: goto tr82; + case 827: goto tr84; + case 1033: goto tr164; + case 1034: goto tr83; + case 1056: goto tr164; + case 1064: goto tr165; + case 1065: goto tr166; + case 1083: goto tr167; + } + if ( 896 <= _widec && _widec <= 1151 ) + goto tr95; + goto tr79; +tr168: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st21; +tr164: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st21; +tr165: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st21; +tr166: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st21; +tr169: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st21; +tr170: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st21; +st21: + if ( ++p == pe ) + goto _test_eof21; +case 21: + _widec = (*p); + if ( (*p) < 11 ) { + if ( (*p) > 9 ) { + if ( 10 <= (*p) && (*p) <= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 58 ) { + if ( (*p) > 59 ) { + if ( 60 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 777: goto st7; + case 778: goto tr89; + case 800: goto st7; + case 808: goto tr87; + case 809: goto tr88; + case 827: goto tr90; + case 1033: goto tr168; + case 1034: goto tr89; + case 1056: goto tr168; + case 1064: goto tr169; + case 1065: goto tr170; + case 1083: goto tr171; + } + if ( 896 <= _widec && _widec <= 1151 ) + goto tr95; + goto tr79; +st22: + if ( ++p == pe ) + goto _test_eof22; +case 22: + switch( (*p) ) { + case 78: goto st23; + case 83: goto st28; + case 110: goto st23; + case 115: goto st28; + } + goto tr36; +st23: + if ( ++p == pe ) + goto _test_eof23; +case 23: + switch( (*p) ) { + case 83: goto st24; + case 115: goto st24; + } + goto tr36; +st24: + if ( ++p == pe ) + goto _test_eof24; +case 24: + switch( (*p) ) { + case 75: goto st25; + case 107: goto st25; + } + goto tr36; +st25: + if ( ++p == pe ) + goto _test_eof25; +case 25: + switch( (*p) ) { + case 69: goto st26; + case 101: goto st26; + } + goto tr36; +st26: + if ( ++p == pe ) + goto _test_eof26; +case 26: + switch( (*p) ) { + case 89: goto st27; + case 121: goto st27; + } + goto tr36; +st27: + if ( ++p == pe ) + goto _test_eof27; +case 27: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr178; + case 32: goto tr178; + case 40: goto tr179; + case 41: goto tr180; + case 2058: goto tr181; + case 2107: goto tr182; + case 2314: goto tr183; + case 2363: goto tr183; + case 2570: goto tr184; + case 2619: goto tr185; + } + goto tr57; +st28: + if ( ++p == pe ) + goto _test_eof28; +case 28: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr186; + case 32: goto tr186; + case 40: goto tr187; + case 41: goto tr188; + case 2058: goto tr189; + case 2107: goto tr190; + case 2314: goto tr191; + case 2363: goto tr191; + case 2570: goto tr192; + case 2619: goto tr193; + } + goto tr57; +st29: + if ( ++p == pe ) + goto _test_eof29; +case 29: + switch( (*p) ) { + case 82: goto st30; + case 114: goto st30; + } + goto tr36; +st30: + if ( ++p == pe ) + goto _test_eof30; +case 30: + switch( (*p) ) { + case 84: goto st31; + case 116: goto st31; + } + goto tr36; +st31: + if ( ++p == pe ) + goto _test_eof31; +case 31: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr196; + case 32: goto tr196; + case 40: goto tr197; + case 41: goto tr198; + case 2058: goto tr199; + case 2107: goto tr200; + case 2314: goto tr201; + case 2363: goto tr201; + case 2570: goto tr202; + case 2619: goto tr203; + } + goto tr57; +st32: + if ( ++p == pe ) + goto _test_eof32; +case 32: + switch( (*p) ) { + case 65: goto st33; + case 97: goto st33; + } + goto tr36; +st33: + if ( ++p == pe ) + goto _test_eof33; +case 33: + switch( (*p) ) { + case 77: goto st34; + case 109: goto st34; + } + goto tr36; +st34: + if ( ++p == pe ) + goto _test_eof34; +case 34: + switch( (*p) ) { + case 69: goto st35; + case 101: goto st35; + } + goto tr36; +st35: + if ( ++p == pe ) + goto _test_eof35; +case 35: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr207; + case 32: goto tr207; + case 40: goto tr208; + case 41: goto tr209; + case 2058: goto tr210; + case 2107: goto tr211; + case 2314: goto tr212; + case 2363: goto tr212; + case 2570: goto tr213; + case 2619: goto tr214; + } + goto tr57; +tr7: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + goto st36; +tr42: + { + s->r_class = s->default_class; + } + goto st36; +tr624: + { + s->r_ttl = s->default_ttl; + } + goto st36; +st36: + if ( ++p == pe ) + goto _test_eof36; +case 36: + switch( (*p) ) { + case 72: goto st37; + case 78: goto st41; + case 83: goto st49; + case 104: goto st37; + case 110: goto st41; + case 115: goto st49; + } + goto tr36; +st37: + if ( ++p == pe ) + goto _test_eof37; +case 37: + switch( (*p) ) { + case 67: goto st38; + case 99: goto st38; + } + goto tr36; +st38: + if ( ++p == pe ) + goto _test_eof38; +case 38: + switch( (*p) ) { + case 73: goto st39; + case 105: goto st39; + } + goto tr36; +st39: + if ( ++p == pe ) + goto _test_eof39; +case 39: + switch( (*p) ) { + case 68: goto st40; + case 100: goto st40; + } + goto tr36; +st40: + if ( ++p == pe ) + goto _test_eof40; +case 40: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr221; + case 32: goto tr221; + case 40: goto tr222; + case 41: goto tr223; + case 2058: goto tr224; + case 2107: goto tr225; + case 2314: goto tr226; + case 2363: goto tr226; + case 2570: goto tr227; + case 2619: goto tr228; + } + goto tr57; +st41: + if ( ++p == pe ) + goto _test_eof41; +case 41: + switch( (*p) ) { + case 65: goto st42; + case 83: goto st45; + case 97: goto st42; + case 115: goto st45; + } + goto tr36; +st42: + if ( ++p == pe ) + goto _test_eof42; +case 42: + switch( (*p) ) { + case 77: goto st43; + case 109: goto st43; + } + goto tr36; +st43: + if ( ++p == pe ) + goto _test_eof43; +case 43: + switch( (*p) ) { + case 69: goto st44; + case 101: goto st44; + } + goto tr36; +st44: + if ( ++p == pe ) + goto _test_eof44; +case 44: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr233; + case 32: goto tr233; + case 40: goto tr234; + case 41: goto tr235; + case 2058: goto tr236; + case 2107: goto tr237; + case 2314: goto tr238; + case 2363: goto tr238; + case 2570: goto tr239; + case 2619: goto tr240; + } + goto tr57; +st45: + if ( ++p == pe ) + goto _test_eof45; +case 45: + switch( (*p) ) { + case 75: goto st46; + case 107: goto st46; + } + goto tr36; +st46: + if ( ++p == pe ) + goto _test_eof46; +case 46: + switch( (*p) ) { + case 69: goto st47; + case 101: goto st47; + } + goto tr36; +st47: + if ( ++p == pe ) + goto _test_eof47; +case 47: + switch( (*p) ) { + case 89: goto st48; + case 121: goto st48; + } + goto tr36; +st48: + if ( ++p == pe ) + goto _test_eof48; +case 48: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr244; + case 32: goto tr244; + case 40: goto tr245; + case 41: goto tr246; + case 2058: goto tr247; + case 2107: goto tr248; + case 2314: goto tr249; + case 2363: goto tr249; + case 2570: goto tr250; + case 2619: goto tr251; + } + goto tr57; +st49: + if ( ++p == pe ) + goto _test_eof49; +case 49: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr252; + case 32: goto tr252; + case 40: goto tr253; + case 41: goto tr254; + case 2058: goto tr255; + case 2107: goto tr256; + case 2314: goto tr257; + case 2363: goto tr257; + case 2570: goto tr258; + case 2619: goto tr259; + } + goto tr57; +tr8: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + goto st50; +tr43: + { + s->r_class = s->default_class; + } + goto st50; +tr625: + { + s->r_ttl = s->default_ttl; + } + goto st50; +st50: + if ( ++p == pe ) + goto _test_eof50; +case 50: + switch( (*p) ) { + case 85: goto st51; + case 117: goto st51; + } + goto tr36; +st51: + if ( ++p == pe ) + goto _test_eof51; +case 51: + switch( (*p) ) { + case 73: goto st52; + case 105: goto st52; + } + goto tr36; +st52: + if ( ++p == pe ) + goto _test_eof52; +case 52: + switch( (*p) ) { + case 52: goto st53; + case 54: goto st55; + } + goto tr36; +st53: + if ( ++p == pe ) + goto _test_eof53; +case 53: + if ( (*p) == 56 ) + goto st54; + goto tr36; +st54: + if ( ++p == pe ) + goto _test_eof54; +case 54: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr265; + case 32: goto tr265; + case 40: goto tr266; + case 41: goto tr267; + case 2058: goto tr268; + case 2107: goto tr269; + case 2314: goto tr270; + case 2363: goto tr270; + case 2570: goto tr271; + case 2619: goto tr272; + } + goto tr57; +st55: + if ( ++p == pe ) + goto _test_eof55; +case 55: + if ( (*p) == 52 ) + goto st56; + goto tr36; +st56: + if ( ++p == pe ) + goto _test_eof56; +case 56: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr274; + case 32: goto tr274; + case 40: goto tr275; + case 41: goto tr276; + case 2058: goto tr277; + case 2107: goto tr278; + case 2314: goto tr279; + case 2363: goto tr279; + case 2570: goto tr280; + case 2619: goto tr281; + } + goto tr57; +tr9: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + goto st57; +tr44: + { + s->r_class = s->default_class; + } + goto st57; +tr626: + { + s->r_ttl = s->default_ttl; + } + goto st57; +st57: + if ( ++p == pe ) + goto _test_eof57; +case 57: + switch( (*p) ) { + case 73: goto st58; + case 105: goto st58; + } + goto tr36; +st58: + if ( ++p == pe ) + goto _test_eof58; +case 58: + switch( (*p) ) { + case 78: goto st59; + case 110: goto st59; + } + goto tr36; +st59: + if ( ++p == pe ) + goto _test_eof59; +case 59: + switch( (*p) ) { + case 70: goto st60; + case 102: goto st60; + } + goto tr36; +st60: + if ( ++p == pe ) + goto _test_eof60; +case 60: + switch( (*p) ) { + case 79: goto st61; + case 111: goto st61; + } + goto tr36; +st61: + if ( ++p == pe ) + goto _test_eof61; +case 61: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr286; + case 32: goto tr286; + case 40: goto tr287; + case 41: goto tr288; + case 2058: goto tr289; + case 2107: goto tr290; + case 2314: goto tr291; + case 2363: goto tr291; + case 2570: goto tr292; + case 2619: goto tr293; + } + goto tr57; +tr45: + { + s->r_class = s->default_class; + } + goto st62; +st62: + if ( ++p == pe ) + goto _test_eof62; +case 62: + switch( (*p) ) { + case 78: goto st63; + case 80: goto st66; + case 110: goto st63; + case 112: goto st66; + } + goto tr36; +st63: + if ( ++p == pe ) + goto _test_eof63; +case 63: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr296; + case 32: goto tr296; + case 40: goto tr297; + case 41: goto tr298; + case 1034: goto tr299; + case 1083: goto tr300; + } + goto tr36; +tr302: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st64; +tr303: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st64; +tr319: + { + s->line_counter++; + } + goto st64; +tr639: + { + if (s->number64 <= UINT32_MAX) { + s->r_ttl = (uint32_t)(s->number64); + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st64; +tr640: + { + if (s->number64 <= UINT32_MAX) { + s->r_ttl = (uint32_t)(s->number64); + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st64; +tr641: + { + if (s->number64 <= UINT32_MAX) { + s->r_ttl = (uint32_t)(s->number64); + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st64; +tr648: + { + if (s->number64 <= UINT32_MAX) { + s->r_ttl = (uint32_t)(s->number64); + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st64; +tr606: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st64; +tr296: + { + s->r_class = KNOT_CLASS_IN; + } + goto st64; +tr297: + { + s->r_class = KNOT_CLASS_IN; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st64; +tr298: + { + s->r_class = KNOT_CLASS_IN; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st64; +tr299: + { + s->r_class = KNOT_CLASS_IN; + } + { + s->line_counter++; + } + goto st64; +tr657: + { + if (s->number64 + s->number64_tmp < UINT32_MAX) { + s->number64 += s->number64_tmp; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= UINT32_MAX) { + s->r_ttl = (uint32_t)(s->number64); + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st64; +tr658: + { + if (s->number64 + s->number64_tmp < UINT32_MAX) { + s->number64 += s->number64_tmp; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= UINT32_MAX) { + s->r_ttl = (uint32_t)(s->number64); + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st64; +tr659: + { + if (s->number64 + s->number64_tmp < UINT32_MAX) { + s->number64 += s->number64_tmp; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= UINT32_MAX) { + s->r_ttl = (uint32_t)(s->number64); + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st64; +tr661: + { + if (s->number64 + s->number64_tmp < UINT32_MAX) { + s->number64 += s->number64_tmp; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= UINT32_MAX) { + s->r_ttl = (uint32_t)(s->number64); + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st64; +st64: + if ( ++p == pe ) + goto _test_eof64; +case 64: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st64; + case 32: goto st64; + case 40: goto tr302; + case 41: goto tr303; + case 65: goto st4; + case 67: goto st17; + case 68: goto st36; + case 69: goto st50; + case 72: goto st57; + case 73: goto st65; + case 75: goto st73; + case 76: goto st77; + case 77: goto st85; + case 78: goto st91; + case 80: goto st107; + case 82: goto st110; + case 83: goto st117; + case 84: goto st128; + case 85: goto st138; + case 97: goto st4; + case 99: goto st17; + case 100: goto st36; + case 101: goto st50; + case 104: goto st57; + case 105: goto st65; + case 107: goto st73; + case 108: goto st77; + case 109: goto st85; + case 110: goto st91; + case 112: goto st107; + case 114: goto st110; + case 115: goto st117; + case 116: goto st128; + case 117: goto st138; + case 1034: goto tr319; + case 1083: goto tr320; + } + goto tr36; +tr627: + { + s->r_ttl = s->default_ttl; + } + goto st65; +st65: + if ( ++p == pe ) + goto _test_eof65; +case 65: + switch( (*p) ) { + case 80: goto st66; + case 112: goto st66; + } + goto tr36; +st66: + if ( ++p == pe ) + goto _test_eof66; +case 66: + switch( (*p) ) { + case 83: goto st67; + case 115: goto st67; + } + goto tr36; +st67: + if ( ++p == pe ) + goto _test_eof67; +case 67: + switch( (*p) ) { + case 69: goto st68; + case 101: goto st68; + } + goto tr36; +st68: + if ( ++p == pe ) + goto _test_eof68; +case 68: + switch( (*p) ) { + case 67: goto st69; + case 99: goto st69; + } + goto tr36; +st69: + if ( ++p == pe ) + goto _test_eof69; +case 69: + switch( (*p) ) { + case 75: goto st70; + case 107: goto st70; + } + goto tr36; +st70: + if ( ++p == pe ) + goto _test_eof70; +case 70: + switch( (*p) ) { + case 69: goto st71; + case 101: goto st71; + } + goto tr36; +st71: + if ( ++p == pe ) + goto _test_eof71; +case 71: + switch( (*p) ) { + case 89: goto st72; + case 121: goto st72; + } + goto tr36; +st72: + if ( ++p == pe ) + goto _test_eof72; +case 72: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr327; + case 32: goto tr327; + case 40: goto tr328; + case 41: goto tr329; + case 2058: goto tr330; + case 2107: goto tr331; + case 2314: goto tr332; + case 2363: goto tr332; + case 2570: goto tr333; + case 2619: goto tr334; + } + goto tr57; +tr11: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + goto st73; +tr46: + { + s->r_class = s->default_class; + } + goto st73; +tr628: + { + s->r_ttl = s->default_ttl; + } + goto st73; +st73: + if ( ++p == pe ) + goto _test_eof73; +case 73: + switch( (*p) ) { + case 69: goto st74; + case 88: goto st76; + case 101: goto st74; + case 120: goto st76; + } + goto tr36; +st74: + if ( ++p == pe ) + goto _test_eof74; +case 74: + switch( (*p) ) { + case 89: goto st75; + case 121: goto st75; + } + goto tr36; +st75: + if ( ++p == pe ) + goto _test_eof75; +case 75: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr338; + case 32: goto tr338; + case 40: goto tr339; + case 41: goto tr340; + case 2058: goto tr341; + case 2107: goto tr342; + case 2314: goto tr343; + case 2363: goto tr343; + case 2570: goto tr344; + case 2619: goto tr345; + } + goto tr57; +st76: + if ( ++p == pe ) + goto _test_eof76; +case 76: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr346; + case 32: goto tr346; + case 40: goto tr347; + case 41: goto tr348; + case 2058: goto tr349; + case 2107: goto tr350; + case 2314: goto tr351; + case 2363: goto tr351; + case 2570: goto tr352; + case 2619: goto tr353; + } + goto tr57; +tr12: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + goto st77; +tr47: + { + s->r_class = s->default_class; + } + goto st77; +tr629: + { + s->r_ttl = s->default_ttl; + } + goto st77; +st77: + if ( ++p == pe ) + goto _test_eof77; +case 77: + switch( (*p) ) { + case 51: goto st78; + case 54: goto st80; + case 79: goto st82; + case 80: goto st84; + case 111: goto st82; + case 112: goto st84; + } + goto tr36; +st78: + if ( ++p == pe ) + goto _test_eof78; +case 78: + if ( (*p) == 50 ) + goto st79; + goto tr36; +st79: + if ( ++p == pe ) + goto _test_eof79; +case 79: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr359; + case 32: goto tr359; + case 40: goto tr360; + case 41: goto tr361; + case 2058: goto tr362; + case 2107: goto tr363; + case 2314: goto tr364; + case 2363: goto tr364; + case 2570: goto tr365; + case 2619: goto tr366; + } + goto tr57; +st80: + if ( ++p == pe ) + goto _test_eof80; +case 80: + if ( (*p) == 52 ) + goto st81; + goto tr36; +st81: + if ( ++p == pe ) + goto _test_eof81; +case 81: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr368; + case 32: goto tr368; + case 40: goto tr369; + case 41: goto tr370; + case 2058: goto tr371; + case 2107: goto tr372; + case 2314: goto tr373; + case 2363: goto tr373; + case 2570: goto tr374; + case 2619: goto tr375; + } + goto tr57; +st82: + if ( ++p == pe ) + goto _test_eof82; +case 82: + switch( (*p) ) { + case 67: goto st83; + case 99: goto st83; + } + goto tr36; +st83: + if ( ++p == pe ) + goto _test_eof83; +case 83: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr377; + case 32: goto tr377; + case 40: goto tr378; + case 41: goto tr379; + case 2058: goto tr380; + case 2107: goto tr381; + case 2314: goto tr382; + case 2363: goto tr382; + case 2570: goto tr383; + case 2619: goto tr384; + } + goto tr57; +st84: + if ( ++p == pe ) + goto _test_eof84; +case 84: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr385; + case 32: goto tr385; + case 40: goto tr386; + case 41: goto tr387; + case 2058: goto tr388; + case 2107: goto tr389; + case 2314: goto tr390; + case 2363: goto tr390; + case 2570: goto tr391; + case 2619: goto tr392; + } + goto tr57; +tr13: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + goto st85; +tr48: + { + s->r_class = s->default_class; + } + goto st85; +tr630: + { + s->r_ttl = s->default_ttl; + } + goto st85; +st85: + if ( ++p == pe ) + goto _test_eof85; +case 85: + switch( (*p) ) { + case 73: goto st86; + case 88: goto st90; + case 105: goto st86; + case 120: goto st90; + } + goto tr36; +st86: + if ( ++p == pe ) + goto _test_eof86; +case 86: + switch( (*p) ) { + case 78: goto st87; + case 110: goto st87; + } + goto tr36; +st87: + if ( ++p == pe ) + goto _test_eof87; +case 87: + switch( (*p) ) { + case 70: goto st88; + case 102: goto st88; + } + goto tr36; +st88: + if ( ++p == pe ) + goto _test_eof88; +case 88: + switch( (*p) ) { + case 79: goto st89; + case 111: goto st89; + } + goto tr36; +st89: + if ( ++p == pe ) + goto _test_eof89; +case 89: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr398; + case 32: goto tr398; + case 40: goto tr399; + case 41: goto tr400; + case 2058: goto tr401; + case 2107: goto tr402; + case 2314: goto tr403; + case 2363: goto tr403; + case 2570: goto tr404; + case 2619: goto tr405; + } + goto tr57; +st90: + if ( ++p == pe ) + goto _test_eof90; +case 90: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr406; + case 32: goto tr406; + case 40: goto tr407; + case 41: goto tr408; + case 2058: goto tr409; + case 2107: goto tr410; + case 2314: goto tr411; + case 2363: goto tr411; + case 2570: goto tr412; + case 2619: goto tr413; + } + goto tr57; +tr14: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + goto st91; +tr49: + { + s->r_class = s->default_class; + } + goto st91; +tr631: + { + s->r_ttl = s->default_ttl; + } + goto st91; +st91: + if ( ++p == pe ) + goto _test_eof91; +case 91: + switch( (*p) ) { + case 65: goto st92; + case 73: goto st96; + case 83: goto st98; + case 97: goto st92; + case 105: goto st96; + case 115: goto st98; + } + goto tr36; +st92: + if ( ++p == pe ) + goto _test_eof92; +case 92: + switch( (*p) ) { + case 80: goto st93; + case 112: goto st93; + } + goto tr36; +st93: + if ( ++p == pe ) + goto _test_eof93; +case 93: + switch( (*p) ) { + case 84: goto st94; + case 116: goto st94; + } + goto tr36; +st94: + if ( ++p == pe ) + goto _test_eof94; +case 94: + switch( (*p) ) { + case 82: goto st95; + case 114: goto st95; + } + goto tr36; +st95: + if ( ++p == pe ) + goto _test_eof95; +case 95: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr420; + case 32: goto tr420; + case 40: goto tr421; + case 41: goto tr422; + case 2058: goto tr423; + case 2107: goto tr424; + case 2314: goto tr425; + case 2363: goto tr425; + case 2570: goto tr426; + case 2619: goto tr427; + } + goto tr57; +st96: + if ( ++p == pe ) + goto _test_eof96; +case 96: + switch( (*p) ) { + case 68: goto st97; + case 100: goto st97; + } + goto tr36; +st97: + if ( ++p == pe ) + goto _test_eof97; +case 97: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr429; + case 32: goto tr429; + case 40: goto tr430; + case 41: goto tr431; + case 2058: goto tr432; + case 2107: goto tr433; + case 2314: goto tr434; + case 2363: goto tr434; + case 2570: goto tr435; + case 2619: goto tr436; + } + goto tr57; +st98: + if ( ++p == pe ) + goto _test_eof98; +case 98: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr437; + case 32: goto tr437; + case 40: goto tr438; + case 41: goto tr439; + case 69: goto st99; + case 101: goto st99; + case 2058: goto tr441; + case 2107: goto tr442; + case 2314: goto tr443; + case 2363: goto tr443; + case 2570: goto tr444; + case 2619: goto tr445; + } + goto tr57; +st99: + if ( ++p == pe ) + goto _test_eof99; +case 99: + switch( (*p) ) { + case 67: goto st100; + case 99: goto st100; + } + goto tr36; +st100: + if ( ++p == pe ) + goto _test_eof100; +case 100: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr447; + case 32: goto tr447; + case 40: goto tr448; + case 41: goto tr449; + case 51: goto st101; + case 2058: goto tr451; + case 2107: goto tr452; + case 2314: goto tr453; + case 2363: goto tr453; + case 2570: goto tr454; + case 2619: goto tr455; + } + goto tr57; +st101: + if ( ++p == pe ) + goto _test_eof101; +case 101: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr456; + case 32: goto tr456; + case 40: goto tr457; + case 41: goto tr458; + case 80: goto st102; + case 112: goto st102; + case 2058: goto tr460; + case 2107: goto tr461; + case 2314: goto tr462; + case 2363: goto tr462; + case 2570: goto tr463; + case 2619: goto tr464; + } + goto tr57; +st102: + if ( ++p == pe ) + goto _test_eof102; +case 102: + switch( (*p) ) { + case 65: goto st103; + case 97: goto st103; + } + goto tr36; +st103: + if ( ++p == pe ) + goto _test_eof103; +case 103: + switch( (*p) ) { + case 82: goto st104; + case 114: goto st104; + } + goto tr36; +st104: + if ( ++p == pe ) + goto _test_eof104; +case 104: + switch( (*p) ) { + case 65: goto st105; + case 97: goto st105; + } + goto tr36; +st105: + if ( ++p == pe ) + goto _test_eof105; +case 105: + switch( (*p) ) { + case 77: goto st106; + case 109: goto st106; + } + goto tr36; +st106: + if ( ++p == pe ) + goto _test_eof106; +case 106: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr469; + case 32: goto tr469; + case 40: goto tr470; + case 41: goto tr471; + case 2058: goto tr472; + case 2107: goto tr473; + case 2314: goto tr474; + case 2363: goto tr474; + case 2570: goto tr475; + case 2619: goto tr476; + } + goto tr57; +tr15: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + goto st107; +tr50: + { + s->r_class = s->default_class; + } + goto st107; +tr632: + { + s->r_ttl = s->default_ttl; + } + goto st107; +st107: + if ( ++p == pe ) + goto _test_eof107; +case 107: + switch( (*p) ) { + case 84: goto st108; + case 116: goto st108; + } + goto tr36; +st108: + if ( ++p == pe ) + goto _test_eof108; +case 108: + switch( (*p) ) { + case 82: goto st109; + case 114: goto st109; + } + goto tr36; +st109: + if ( ++p == pe ) + goto _test_eof109; +case 109: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr479; + case 32: goto tr479; + case 40: goto tr480; + case 41: goto tr481; + case 2058: goto tr482; + case 2107: goto tr483; + case 2314: goto tr484; + case 2363: goto tr484; + case 2570: goto tr485; + case 2619: goto tr486; + } + goto tr57; +tr16: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + goto st110; +tr51: + { + s->r_class = s->default_class; + } + goto st110; +tr633: + { + s->r_ttl = s->default_ttl; + } + goto st110; +st110: + if ( ++p == pe ) + goto _test_eof110; +case 110: + switch( (*p) ) { + case 80: goto st111; + case 82: goto st112; + case 84: goto st116; + case 112: goto st111; + case 114: goto st112; + case 116: goto st116; + } + goto tr36; +st111: + if ( ++p == pe ) + goto _test_eof111; +case 111: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr490; + case 32: goto tr490; + case 40: goto tr491; + case 41: goto tr492; + case 2058: goto tr493; + case 2107: goto tr494; + case 2314: goto tr495; + case 2363: goto tr495; + case 2570: goto tr496; + case 2619: goto tr497; + } + goto tr57; +st112: + if ( ++p == pe ) + goto _test_eof112; +case 112: + switch( (*p) ) { + case 83: goto st113; + case 115: goto st113; + } + goto tr36; +st113: + if ( ++p == pe ) + goto _test_eof113; +case 113: + switch( (*p) ) { + case 73: goto st114; + case 105: goto st114; + } + goto tr36; +st114: + if ( ++p == pe ) + goto _test_eof114; +case 114: + switch( (*p) ) { + case 71: goto st115; + case 103: goto st115; + } + goto tr36; +st115: + if ( ++p == pe ) + goto _test_eof115; +case 115: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr501; + case 32: goto tr501; + case 40: goto tr502; + case 41: goto tr503; + case 2058: goto tr504; + case 2107: goto tr505; + case 2314: goto tr506; + case 2363: goto tr506; + case 2570: goto tr507; + case 2619: goto tr508; + } + goto tr57; +st116: + if ( ++p == pe ) + goto _test_eof116; +case 116: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr509; + case 32: goto tr509; + case 40: goto tr510; + case 41: goto tr511; + case 2058: goto tr512; + case 2107: goto tr513; + case 2314: goto tr514; + case 2363: goto tr514; + case 2570: goto tr515; + case 2619: goto tr516; + } + goto tr57; +tr17: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + goto st117; +tr52: + { + s->r_class = s->default_class; + } + goto st117; +tr634: + { + s->r_ttl = s->default_ttl; + } + goto st117; +st117: + if ( ++p == pe ) + goto _test_eof117; +case 117: + switch( (*p) ) { + case 79: goto st118; + case 80: goto st120; + case 82: goto st122; + case 83: goto st124; + case 111: goto st118; + case 112: goto st120; + case 114: goto st122; + case 115: goto st124; + } + goto tr36; +st118: + if ( ++p == pe ) + goto _test_eof118; +case 118: + switch( (*p) ) { + case 65: goto st119; + case 97: goto st119; + } + goto tr36; +st119: + if ( ++p == pe ) + goto _test_eof119; +case 119: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr522; + case 32: goto tr522; + case 40: goto tr523; + case 41: goto tr524; + case 2058: goto tr525; + case 2107: goto tr526; + case 2314: goto tr527; + case 2363: goto tr527; + case 2570: goto tr528; + case 2619: goto tr529; + } + goto tr57; +st120: + if ( ++p == pe ) + goto _test_eof120; +case 120: + switch( (*p) ) { + case 70: goto st121; + case 102: goto st121; + } + goto tr36; +st121: + if ( ++p == pe ) + goto _test_eof121; +case 121: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr531; + case 32: goto tr531; + case 40: goto tr532; + case 41: goto tr533; + case 2058: goto tr534; + case 2107: goto tr535; + case 2314: goto tr536; + case 2363: goto tr536; + case 2570: goto tr537; + case 2619: goto tr538; + } + goto tr57; +st122: + if ( ++p == pe ) + goto _test_eof122; +case 122: + switch( (*p) ) { + case 86: goto st123; + case 118: goto st123; + } + goto tr36; +st123: + if ( ++p == pe ) + goto _test_eof123; +case 123: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr540; + case 32: goto tr540; + case 40: goto tr541; + case 41: goto tr542; + case 2058: goto tr543; + case 2107: goto tr544; + case 2314: goto tr545; + case 2363: goto tr545; + case 2570: goto tr546; + case 2619: goto tr547; + } + goto tr57; +st124: + if ( ++p == pe ) + goto _test_eof124; +case 124: + switch( (*p) ) { + case 72: goto st125; + case 104: goto st125; + } + goto tr36; +st125: + if ( ++p == pe ) + goto _test_eof125; +case 125: + switch( (*p) ) { + case 70: goto st126; + case 102: goto st126; + } + goto tr36; +st126: + if ( ++p == pe ) + goto _test_eof126; +case 126: + switch( (*p) ) { + case 80: goto st127; + case 112: goto st127; + } + goto tr36; +st127: + if ( ++p == pe ) + goto _test_eof127; +case 127: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr551; + case 32: goto tr551; + case 40: goto tr552; + case 41: goto tr553; + case 2058: goto tr554; + case 2107: goto tr555; + case 2314: goto tr556; + case 2363: goto tr556; + case 2570: goto tr557; + case 2619: goto tr558; + } + goto tr57; +tr18: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + goto st128; +tr53: + { + s->r_class = s->default_class; + } + goto st128; +tr635: + { + s->r_ttl = s->default_ttl; + } + goto st128; +st128: + if ( ++p == pe ) + goto _test_eof128; +case 128: + switch( (*p) ) { + case 76: goto st129; + case 88: goto st132; + case 89: goto st134; + case 108: goto st129; + case 120: goto st132; + case 121: goto st134; + } + goto tr36; +st129: + if ( ++p == pe ) + goto _test_eof129; +case 129: + switch( (*p) ) { + case 83: goto st130; + case 115: goto st130; + } + goto tr36; +st130: + if ( ++p == pe ) + goto _test_eof130; +case 130: + switch( (*p) ) { + case 65: goto st131; + case 97: goto st131; + } + goto tr36; +st131: + if ( ++p == pe ) + goto _test_eof131; +case 131: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr564; + case 32: goto tr564; + case 40: goto tr565; + case 41: goto tr566; + case 2058: goto tr567; + case 2107: goto tr568; + case 2314: goto tr569; + case 2363: goto tr569; + case 2570: goto tr570; + case 2619: goto tr571; + } + goto tr57; +st132: + if ( ++p == pe ) + goto _test_eof132; +case 132: + switch( (*p) ) { + case 84: goto st133; + case 116: goto st133; + } + goto tr36; +st133: + if ( ++p == pe ) + goto _test_eof133; +case 133: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr573; + case 32: goto tr573; + case 40: goto tr574; + case 41: goto tr575; + case 2058: goto tr576; + case 2107: goto tr577; + case 2314: goto tr578; + case 2363: goto tr578; + case 2570: goto tr579; + case 2619: goto tr580; + } + goto tr57; +st134: + if ( ++p == pe ) + goto _test_eof134; +case 134: + switch( (*p) ) { + case 80: goto st135; + case 112: goto st135; + } + goto tr36; +st135: + if ( ++p == pe ) + goto _test_eof135; +case 135: + switch( (*p) ) { + case 69: goto st136; + case 101: goto st136; + } + goto tr36; +st136: + if ( ++p == pe ) + goto _test_eof136; +case 136: + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr584; + goto tr583; +tr584: + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st137; +tr589: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st137; +st137: + if ( ++p == pe ) + goto _test_eof137; +case 137: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr586; + case 32: goto tr586; + case 40: goto tr587; + case 41: goto tr588; + case 2058: goto tr590; + case 2107: goto tr591; + case 2314: goto tr592; + case 2363: goto tr592; + case 2570: goto tr593; + case 2619: goto tr594; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr589; + goto tr585; +tr19: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + goto st138; +tr54: + { + s->r_class = s->default_class; + } + goto st138; +tr636: + { + s->r_ttl = s->default_ttl; + } + goto st138; +st138: + if ( ++p == pe ) + goto _test_eof138; +case 138: + switch( (*p) ) { + case 82: goto st139; + case 114: goto st139; + } + goto tr36; +st139: + if ( ++p == pe ) + goto _test_eof139; +case 139: + switch( (*p) ) { + case 73: goto st140; + case 105: goto st140; + } + goto tr36; +st140: + if ( ++p == pe ) + goto _test_eof140; +case 140: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr597; + case 32: goto tr597; + case 40: goto tr598; + case 41: goto tr599; + case 2058: goto tr600; + case 2107: goto tr601; + case 2314: goto tr602; + case 2363: goto tr602; + case 2570: goto tr603; + case 2619: goto tr604; + } + goto tr57; +tr320: + { + s->buffer_length = 0; + } + goto st141; +tr649: + { + if (s->number64 <= UINT32_MAX) { + s->r_ttl = (uint32_t)(s->number64); + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st141; +tr605: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st141; +tr300: + { + s->r_class = KNOT_CLASS_IN; + } + { + s->buffer_length = 0; + } + goto st141; +tr662: + { + if (s->number64 + s->number64_tmp < UINT32_MAX) { + s->number64 += s->number64_tmp; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= UINT32_MAX) { + s->r_ttl = (uint32_t)(s->number64); + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st141; +st141: + if ( ++p == pe ) + goto _test_eof141; +case 141: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr606; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr605; + goto tr36; +tr914: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + { + if (rdata_tail - s->r_data > UINT16_MAX) { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + s->r_data_length = rdata_tail - s->r_data; + + s->state = ZS_STATE_DATA; + + // Execute the record callback. + if (s->process.automatic) { + if (s->process.record != NULL) { + s->process.record(s); + + // Stop if required from the callback. + if (s->state == ZS_STATE_STOP) { + {p++; cs = 1129; goto _out;} + } + } + } else { + // Return if external processing. + p--; {p++; cs = 1129; goto _out;} + } + } + goto st1129; +tr143: + { + if (s->number64 <= UINT32_MAX) { + s->r_ttl = (uint32_t)(s->number64); + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + if (rdata_tail - s->r_data > UINT16_MAX) { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + s->r_data_length = rdata_tail - s->r_data; + + s->state = ZS_STATE_DATA; + + // Execute the record callback. + if (s->process.automatic) { + if (s->process.record != NULL) { + s->process.record(s); + + // Stop if required from the callback. + if (s->state == ZS_STATE_STOP) { + {p++; cs = 1129; goto _out;} + } + } + } else { + // Return if external processing. + p--; {p++; cs = 1129; goto _out;} + } + } + goto st1129; +tr149: + { + s->line_counter++; + } + { + if (rdata_tail - s->r_data > UINT16_MAX) { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + s->r_data_length = rdata_tail - s->r_data; + + s->state = ZS_STATE_DATA; + + // Execute the record callback. + if (s->process.automatic) { + if (s->process.record != NULL) { + s->process.record(s); + + // Stop if required from the callback. + if (s->state == ZS_STATE_STOP) { + {p++; cs = 1129; goto _out;} + } + } + } else { + // Return if external processing. + p--; {p++; cs = 1129; goto _out;} + } + } + goto st1129; +st1129: + if ( ++p == pe ) + goto _test_eof1129; +case 1129: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3635; + case 32: goto tr3635; + case 36: goto st152; + case 40: goto tr3636; + case 41: goto tr3637; + case 42: goto tr3617; + case 65: goto tr3638; + case 67: goto tr3639; + case 68: goto tr3640; + case 69: goto tr3641; + case 72: goto tr3642; + case 73: goto tr3643; + case 75: goto tr3644; + case 76: goto tr3645; + case 77: goto tr3646; + case 78: goto tr3647; + case 80: goto tr3648; + case 82: goto tr3649; + case 83: goto tr3650; + case 84: goto tr3651; + case 85: goto tr3652; + case 92: goto tr3617; + case 95: goto tr3617; + case 97: goto tr3638; + case 99: goto tr3639; + case 100: goto tr3640; + case 101: goto tr3641; + case 104: goto tr3642; + case 105: goto tr3643; + case 107: goto tr3644; + case 108: goto tr3645; + case 109: goto tr3646; + case 110: goto tr3647; + case 112: goto tr3648; + case 114: goto tr3649; + case 115: goto tr3650; + case 116: goto tr3651; + case 117: goto tr3652; + case 778: goto tr3618; + case 827: goto tr3619; + case 1034: goto tr3653; + case 1083: goto tr3654; + } + if ( _widec < 64 ) { + if ( 45 <= _widec && _widec <= 57 ) + goto tr3617; + } else if ( _widec > 90 ) { + if ( 98 <= _widec && _widec <= 122 ) + goto tr3617; + } else + goto tr3617; + goto tr3634; +tr608: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st142; +tr609: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st142; +tr3635: + { + if (s->r_owner_length == 0) { + WARN(ZS_BAD_PREVIOUS_OWNER); + p--; {goto st268;} + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st142; +tr3636: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + { + if (s->r_owner_length == 0) { + WARN(ZS_BAD_PREVIOUS_OWNER); + p--; {goto st268;} + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st142; +tr3637: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + { + if (s->r_owner_length == 0) { + WARN(ZS_BAD_PREVIOUS_OWNER); + p--; {goto st268;} + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st142; +st142: + if ( ++p == pe ) + goto _test_eof142; +case 142: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st142; + case 32: goto st142; + case 40: goto tr608; + case 41: goto tr609; + case 65: goto tr5; + case 67: goto tr6; + case 68: goto tr7; + case 69: goto tr8; + case 72: goto tr9; + case 73: goto tr10; + case 75: goto tr11; + case 76: goto tr12; + case 77: goto tr13; + case 78: goto tr14; + case 80: goto tr15; + case 82: goto tr16; + case 83: goto tr17; + case 84: goto tr18; + case 85: goto tr19; + case 97: goto tr5; + case 99: goto tr6; + case 100: goto tr7; + case 101: goto tr8; + case 104: goto tr9; + case 105: goto tr10; + case 107: goto tr11; + case 108: goto tr12; + case 109: goto tr13; + case 110: goto tr14; + case 112: goto tr15; + case 114: goto tr16; + case 115: goto tr17; + case 116: goto tr18; + case 117: goto tr19; + case 778: goto tr20; + case 827: goto tr21; + case 1034: goto tr610; + case 1083: goto tr611; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr4; + goto tr0; +tr10: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + goto st143; +st143: + if ( ++p == pe ) + goto _test_eof143; +case 143: + switch( (*p) ) { + case 78: goto st144; + case 80: goto st66; + case 110: goto st144; + case 112: goto st66; + } + goto tr36; +st144: + if ( ++p == pe ) + goto _test_eof144; +case 144: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr613; + case 32: goto tr613; + case 40: goto tr614; + case 41: goto tr615; + case 1034: goto tr616; + case 1083: goto tr617; + } + goto tr36; +tr619: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st145; +tr620: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st145; +tr637: + { + s->line_counter++; + } + goto st145; +tr664: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st145; +tr613: + { + s->r_class = KNOT_CLASS_IN; + } + goto st145; +tr614: + { + s->r_class = KNOT_CLASS_IN; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st145; +tr615: + { + s->r_class = KNOT_CLASS_IN; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st145; +tr616: + { + s->r_class = KNOT_CLASS_IN; + } + { + s->line_counter++; + } + goto st145; +st145: + if ( ++p == pe ) + goto _test_eof145; +case 145: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st145; + case 32: goto st145; + case 40: goto tr619; + case 41: goto tr620; + case 65: goto tr622; + case 67: goto tr623; + case 68: goto tr624; + case 69: goto tr625; + case 72: goto tr626; + case 73: goto tr627; + case 75: goto tr628; + case 76: goto tr629; + case 77: goto tr630; + case 78: goto tr631; + case 80: goto tr632; + case 82: goto tr633; + case 83: goto tr634; + case 84: goto tr635; + case 85: goto tr636; + case 97: goto tr622; + case 99: goto tr623; + case 100: goto tr624; + case 101: goto tr625; + case 104: goto tr626; + case 105: goto tr627; + case 107: goto tr628; + case 108: goto tr629; + case 109: goto tr630; + case 110: goto tr631; + case 112: goto tr632; + case 114: goto tr633; + case 115: goto tr634; + case 116: goto tr635; + case 117: goto tr636; + case 1034: goto tr637; + case 1083: goto tr638; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr621; + goto tr583; +tr621: + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st146; +tr642: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st146; +st146: + if ( ++p == pe ) + goto _test_eof146; +case 146: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr639; + case 32: goto tr639; + case 40: goto tr640; + case 41: goto tr641; + case 68: goto tr643; + case 72: goto tr644; + case 77: goto tr645; + case 83: goto st147; + case 87: goto tr647; + case 100: goto tr643; + case 104: goto tr644; + case 109: goto tr645; + case 115: goto st147; + case 119: goto tr647; + case 1034: goto tr648; + case 1083: goto tr649; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr642; + goto tr24; +tr643: + { if (s->number64 <= (UINT32_MAX / 86400)) { + s->number64 *= 86400; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st147; +tr644: + { if (s->number64 <= (UINT32_MAX / 3600)) { + s->number64 *= 3600; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st147; +tr645: + { if (s->number64 <= (UINT32_MAX / 60)) { + s->number64 *= 60; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st147; +tr647: + { if (s->number64 <= (UINT32_MAX / 604800)) { + s->number64 *= 604800; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st147; +st147: + if ( ++p == pe ) + goto _test_eof147; +case 147: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr639; + case 32: goto tr639; + case 40: goto tr640; + case 41: goto tr641; + case 1034: goto tr648; + case 1083: goto tr649; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr650; + goto tr24; +tr651: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st148; +tr650: + { + s->number64_tmp = s->number64; + } + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st148; +tr660: + { + if (s->number64 + s->number64_tmp < UINT32_MAX) { + s->number64 += s->number64_tmp; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + s->number64_tmp = s->number64; + } + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st148; +st148: + if ( ++p == pe ) + goto _test_eof148; +case 148: + switch( (*p) ) { + case 68: goto tr652; + case 72: goto tr653; + case 77: goto tr654; + case 83: goto st149; + case 87: goto tr656; + case 100: goto tr652; + case 104: goto tr653; + case 109: goto tr654; + case 115: goto st149; + case 119: goto tr656; + } + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr651; + goto tr24; +tr652: + { if (s->number64 <= (UINT32_MAX / 86400)) { + s->number64 *= 86400; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st149; +tr653: + { if (s->number64 <= (UINT32_MAX / 3600)) { + s->number64 *= 3600; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st149; +tr654: + { if (s->number64 <= (UINT32_MAX / 60)) { + s->number64 *= 60; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st149; +tr656: + { if (s->number64 <= (UINT32_MAX / 604800)) { + s->number64 *= 604800; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st149; +st149: + if ( ++p == pe ) + goto _test_eof149; +case 149: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr657; + case 32: goto tr657; + case 40: goto tr658; + case 41: goto tr659; + case 1034: goto tr661; + case 1083: goto tr662; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr660; + goto tr24; +tr638: + { + s->buffer_length = 0; + } + goto st150; +tr663: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st150; +tr617: + { + s->r_class = KNOT_CLASS_IN; + } + { + s->buffer_length = 0; + } + goto st150; +st150: + if ( ++p == pe ) + goto _test_eof150; +case 150: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr664; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr663; + goto tr36; +tr21: + { + s->buffer_length = 0; + } + goto st151; +tr3619: + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + s->buffer_length = 0; + } + goto st151; +tr665: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st151; +tr3679: + { + NOERR; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + s->buffer_length = 0; + } + goto st151; +st151: + if ( ++p == pe ) + goto _test_eof151; +case 151: + if ( (*p) == 10 ) + goto tr666; + goto tr665; +tr610: + { + s->line_counter++; + } + goto st1130; +tr912: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st1130; +tr801: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + { + if (rdata_tail - s->r_data > UINT16_MAX) { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + s->r_data_length = rdata_tail - s->r_data; + + s->state = ZS_STATE_DATA; + + // Execute the record callback. + if (s->process.automatic) { + if (s->process.record != NULL) { + s->process.record(s); + + // Stop if required from the callback. + if (s->state == ZS_STATE_STOP) { + {p++; cs = 1130; goto _out;} + } + } + } else { + // Return if external processing. + p--; {p++; cs = 1130; goto _out;} + } + } + goto st1130; +tr798: + { + s->line_counter++; + } + { + if (rdata_tail - s->r_data > UINT16_MAX) { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + s->r_data_length = rdata_tail - s->r_data; + + s->state = ZS_STATE_DATA; + + // Execute the record callback. + if (s->process.automatic) { + if (s->process.record != NULL) { + s->process.record(s); + + // Stop if required from the callback. + if (s->state == ZS_STATE_STOP) { + {p++; cs = 1130; goto _out;} + } + } + } else { + // Return if external processing. + p--; {p++; cs = 1130; goto _out;} + } + } + goto st1130; +tr793: + { + s->r_owner_length = s->dname_tmp_length; + } + { + s->line_counter++; + } + { + if (s->number64 <= UINT32_MAX) { + s->r_ttl = (uint32_t)(s->number64); + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + if (rdata_tail - s->r_data > UINT16_MAX) { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + s->r_data_length = rdata_tail - s->r_data; + + s->state = ZS_STATE_DATA; + + // Execute the record callback. + if (s->process.automatic) { + if (s->process.record != NULL) { + s->process.record(s); + + // Stop if required from the callback. + if (s->state == ZS_STATE_STOP) { + {p++; cs = 1130; goto _out;} + } + } + } else { + // Return if external processing. + p--; {p++; cs = 1130; goto _out;} + } + } + goto st1130; +tr3653: + { + s->line_counter++; + } + { + if (s->r_owner_length == 0) { + WARN(ZS_BAD_PREVIOUS_OWNER); + p--; {goto st268;} + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st1130; +st1130: + if ( ++p == pe ) + goto _test_eof1130; +case 1130: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3635; + case 32: goto tr3635; + case 36: goto st152; + case 40: goto tr3636; + case 41: goto tr3637; + case 42: goto tr3617; + case 65: goto tr3657; + case 67: goto tr3658; + case 68: goto tr3659; + case 69: goto tr3660; + case 72: goto tr3661; + case 73: goto tr3662; + case 75: goto tr3663; + case 76: goto tr3664; + case 77: goto tr3665; + case 78: goto tr3666; + case 80: goto tr3667; + case 82: goto tr3668; + case 83: goto tr3669; + case 84: goto tr3670; + case 85: goto tr3671; + case 92: goto tr3617; + case 95: goto tr3617; + case 97: goto tr3657; + case 99: goto tr3658; + case 100: goto tr3659; + case 101: goto tr3660; + case 104: goto tr3661; + case 105: goto tr3662; + case 107: goto tr3663; + case 108: goto tr3664; + case 109: goto tr3665; + case 110: goto tr3666; + case 112: goto tr3667; + case 114: goto tr3668; + case 115: goto tr3669; + case 116: goto tr3670; + case 117: goto tr3671; + case 778: goto tr3618; + case 827: goto tr3619; + case 1034: goto tr3653; + case 1083: goto tr3654; + } + if ( _widec < 48 ) { + if ( 45 <= _widec && _widec <= 47 ) + goto tr3617; + } else if ( _widec > 57 ) { + if ( _widec > 90 ) { + if ( 98 <= _widec && _widec <= 122 ) + goto tr3617; + } else if ( _widec >= 64 ) + goto tr3617; + } else + goto tr3656; + goto tr3655; +tr3674: + { + NOERR; + } + goto st152; +st152: + if ( ++p == pe ) + goto _test_eof152; +case 152: + switch( (*p) ) { + case 73: goto tr668; + case 79: goto tr669; + case 84: goto tr670; + case 105: goto tr668; + case 111: goto tr669; + case 116: goto tr670; + } + goto tr667; +tr668: + { + ERR(ZS_OK); + } + goto st153; +st153: + if ( ++p == pe ) + goto _test_eof153; +case 153: + switch( (*p) ) { + case 78: goto st154; + case 110: goto st154; + } + goto tr667; +st154: + if ( ++p == pe ) + goto _test_eof154; +case 154: + switch( (*p) ) { + case 67: goto st155; + case 99: goto st155; + } + goto tr667; +st155: + if ( ++p == pe ) + goto _test_eof155; +case 155: + switch( (*p) ) { + case 76: goto st156; + case 108: goto st156; + } + goto tr667; +st156: + if ( ++p == pe ) + goto _test_eof156; +case 156: + switch( (*p) ) { + case 85: goto st157; + case 117: goto st157; + } + goto tr667; +st157: + if ( ++p == pe ) + goto _test_eof157; +case 157: + switch( (*p) ) { + case 68: goto st158; + case 100: goto st158; + } + goto tr667; +st158: + if ( ++p == pe ) + goto _test_eof158; +case 158: + switch( (*p) ) { + case 69: goto st159; + case 101: goto st159; + } + goto tr667; +st159: + if ( ++p == pe ) + goto _test_eof159; +case 159: + switch( (*p) ) { + case 32: goto tr677; + case 59: goto tr677; + } + if ( (*p) > 10 ) { + if ( 40 <= (*p) && (*p) <= 41 ) + goto tr677; + } else if ( (*p) >= 9 ) + goto tr677; + goto tr667; +tr677: + { p--; {stack[top++] = 1131;goto st312;} } + goto st1131; +tr779: + { p--; {stack[top++] = 1131;goto st300;} } + goto st1131; +tr782: + { p--; {stack[top++] = 1131;goto st291;} } + goto st1131; +st1131: + if ( ++p == pe ) + goto _test_eof1131; +case 1131: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3673; + case 32: goto tr3673; + case 36: goto tr3674; + case 40: goto tr3675; + case 41: goto tr3676; + case 42: goto tr3677; + case 92: goto tr3677; + case 95: goto tr3677; + case 778: goto tr3678; + case 827: goto tr3679; + case 1034: goto tr3680; + case 1083: goto tr3681; + } + if ( _widec < 64 ) { + if ( 45 <= _widec && _widec <= 57 ) + goto tr3677; + } else if ( _widec > 90 ) { + if ( 97 <= _widec && _widec <= 122 ) + goto tr3677; + } else + goto tr3677; + goto tr3672; +tr3617: + { + s->dname = s->r_owner; + s->r_owner_length = 0; + } + { p--; {stack[top++] = 160;goto st270;} } + goto st160; +tr3677: + { + NOERR; + } + { + s->dname = s->r_owner; + s->r_owner_length = 0; + } + { p--; {stack[top++] = 160;goto st270;} } + goto st160; +st160: + if ( ++p == pe ) + goto _test_eof160; +case 160: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr679; + case 32: goto tr679; + case 40: goto tr680; + case 41: goto tr681; + case 1034: goto tr682; + case 1083: goto tr683; + } + goto tr678; +tr685: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st161; +tr686: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st161; +tr687: + { + s->line_counter++; + } + goto st161; +tr691: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st161; +tr679: + { + s->r_owner_length = s->dname_tmp_length; + } + goto st161; +tr680: + { + s->r_owner_length = s->dname_tmp_length; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st161; +tr681: + { + s->r_owner_length = s->dname_tmp_length; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st161; +tr682: + { + s->r_owner_length = s->dname_tmp_length; + } + { + s->line_counter++; + } + goto st161; +st161: + if ( ++p == pe ) + goto _test_eof161; +case 161: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st161; + case 32: goto st161; + case 40: goto tr685; + case 41: goto tr686; + case 65: goto tr5; + case 67: goto tr6; + case 68: goto tr7; + case 69: goto tr8; + case 72: goto tr9; + case 73: goto tr10; + case 75: goto tr11; + case 76: goto tr12; + case 77: goto tr13; + case 78: goto tr14; + case 80: goto tr15; + case 82: goto tr16; + case 83: goto tr17; + case 84: goto tr18; + case 85: goto tr19; + case 97: goto tr5; + case 99: goto tr6; + case 100: goto tr7; + case 101: goto tr8; + case 104: goto tr9; + case 105: goto tr10; + case 107: goto tr11; + case 108: goto tr12; + case 109: goto tr13; + case 110: goto tr14; + case 112: goto tr15; + case 114: goto tr16; + case 115: goto tr17; + case 116: goto tr18; + case 117: goto tr19; + case 1034: goto tr687; + case 1083: goto tr688; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr4; + goto tr583; +tr688: + { + s->buffer_length = 0; + } + goto st162; +tr689: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st162; +tr683: + { + s->r_owner_length = s->dname_tmp_length; + } + { + s->buffer_length = 0; + } + goto st162; +st162: + if ( ++p == pe ) + goto _test_eof162; +case 162: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr691; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr689; + goto st0; +tr22: + { + s->line_counter++; + } + goto st1132; +tr910: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st1132; +tr756: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + { + if (rdata_tail - s->r_data > UINT16_MAX) { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + s->r_data_length = rdata_tail - s->r_data; + + s->state = ZS_STATE_DATA; + + // Execute the record callback. + if (s->process.automatic) { + if (s->process.record != NULL) { + s->process.record(s); + + // Stop if required from the callback. + if (s->state == ZS_STATE_STOP) { + {p++; cs = 1132; goto _out;} + } + } + } else { + // Return if external processing. + p--; {p++; cs = 1132; goto _out;} + } + } + goto st1132; +tr753: + { + s->line_counter++; + } + { + if (rdata_tail - s->r_data > UINT16_MAX) { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + s->r_data_length = rdata_tail - s->r_data; + + s->state = ZS_STATE_DATA; + + // Execute the record callback. + if (s->process.automatic) { + if (s->process.record != NULL) { + s->process.record(s); + + // Stop if required from the callback. + if (s->state == ZS_STATE_STOP) { + {p++; cs = 1132; goto _out;} + } + } + } else { + // Return if external processing. + p--; {p++; cs = 1132; goto _out;} + } + } + goto st1132; +tr787: + { + s->r_owner_length = s->dname_tmp_length; + } + { + s->line_counter++; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + if (rdata_tail - s->r_data > UINT16_MAX) { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + s->r_data_length = rdata_tail - s->r_data; + + s->state = ZS_STATE_DATA; + + // Execute the record callback. + if (s->process.automatic) { + if (s->process.record != NULL) { + s->process.record(s); + + // Stop if required from the callback. + if (s->state == ZS_STATE_STOP) { + {p++; cs = 1132; goto _out;} + } + } + } else { + // Return if external processing. + p--; {p++; cs = 1132; goto _out;} + } + } + goto st1132; +tr824: + { + s->line_counter++; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + if (rdata_tail - s->r_data > UINT16_MAX) { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + s->r_data_length = rdata_tail - s->r_data; + + s->state = ZS_STATE_DATA; + + // Execute the record callback. + if (s->process.automatic) { + if (s->process.record != NULL) { + s->process.record(s); + + // Stop if required from the callback. + if (s->state == ZS_STATE_STOP) { + {p++; cs = 1132; goto _out;} + } + } + } else { + // Return if external processing. + p--; {p++; cs = 1132; goto _out;} + } + } + goto st1132; +tr827: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + if (rdata_tail - s->r_data > UINT16_MAX) { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + s->r_data_length = rdata_tail - s->r_data; + + s->state = ZS_STATE_DATA; + + // Execute the record callback. + if (s->process.automatic) { + if (s->process.record != NULL) { + s->process.record(s); + + // Stop if required from the callback. + if (s->state == ZS_STATE_STOP) { + {p++; cs = 1132; goto _out;} + } + } + } else { + // Return if external processing. + p--; {p++; cs = 1132; goto _out;} + } + } + goto st1132; +tr3620: + { + if (s->r_owner_length == 0) { + WARN(ZS_BAD_PREVIOUS_OWNER); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st1132; +tr3684: + { + s->line_counter++; + } + { + if (s->r_owner_length == 0) { + WARN(ZS_BAD_PREVIOUS_OWNER); + p--; {goto st268;} + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st1132; +tr3680: + { + NOERR; + } + { + if (s->r_owner_length == 0) { + WARN(ZS_BAD_PREVIOUS_OWNER); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st1132; +tr3689: + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + if (rdata_tail - s->r_data > UINT16_MAX) { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + s->r_data_length = rdata_tail - s->r_data; + + s->state = ZS_STATE_DATA; + + // Execute the record callback. + if (s->process.automatic) { + if (s->process.record != NULL) { + s->process.record(s); + + // Stop if required from the callback. + if (s->state == ZS_STATE_STOP) { + {p++; cs = 1132; goto _out;} + } + } + } else { + // Return if external processing. + p--; {p++; cs = 1132; goto _out;} + } + } + { + s->line_counter++; + } + { + if (s->r_owner_length == 0) { + WARN(ZS_BAD_PREVIOUS_OWNER); + p--; {goto st268;} + } + } + goto st1132; +tr3713: + { + s->line_counter++; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + if (rdata_tail - s->r_data > UINT16_MAX) { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + s->r_data_length = rdata_tail - s->r_data; + + s->state = ZS_STATE_DATA; + + // Execute the record callback. + if (s->process.automatic) { + if (s->process.record != NULL) { + s->process.record(s); + + // Stop if required from the callback. + if (s->state == ZS_STATE_STOP) { + {p++; cs = 1132; goto _out;} + } + } + } else { + // Return if external processing. + p--; {p++; cs = 1132; goto _out;} + } + } + { + if (s->r_owner_length == 0) { + WARN(ZS_BAD_PREVIOUS_OWNER); + p--; {goto st268;} + } + } + goto st1132; +st1132: + if ( ++p == pe ) + goto _test_eof1132; +case 1132: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3613; + case 32: goto tr3613; + case 36: goto st152; + case 40: goto tr3682; + case 41: goto tr3683; + case 42: goto tr3617; + case 65: goto tr3657; + case 67: goto tr3658; + case 68: goto tr3659; + case 69: goto tr3660; + case 72: goto tr3661; + case 73: goto tr3662; + case 75: goto tr3663; + case 76: goto tr3664; + case 77: goto tr3665; + case 78: goto tr3666; + case 80: goto tr3667; + case 82: goto tr3668; + case 83: goto tr3669; + case 84: goto tr3670; + case 85: goto tr3671; + case 92: goto tr3617; + case 95: goto tr3617; + case 97: goto tr3657; + case 99: goto tr3658; + case 100: goto tr3659; + case 101: goto tr3660; + case 104: goto tr3661; + case 105: goto tr3662; + case 107: goto tr3663; + case 108: goto tr3664; + case 109: goto tr3665; + case 110: goto tr3666; + case 112: goto tr3667; + case 114: goto tr3668; + case 115: goto tr3669; + case 116: goto tr3670; + case 117: goto tr3671; + case 778: goto tr3618; + case 827: goto tr3619; + case 1034: goto tr3684; + case 1083: goto tr3685; + } + if ( _widec < 48 ) { + if ( 45 <= _widec && _widec <= 47 ) + goto tr3617; + } else if ( _widec > 57 ) { + if ( _widec > 90 ) { + if ( 98 <= _widec && _widec <= 122 ) + goto tr3617; + } else if ( _widec >= 64 ) + goto tr3617; + } else + goto tr3656; + goto tr3655; +tr3656: + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + { + s->dname = s->r_owner; + s->r_owner_length = 0; + } + { p--; {stack[top++] = 163;goto st270;} } + goto st163; +st163: + if ( ++p == pe ) + goto _test_eof163; +case 163: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr693; + case 32: goto tr693; + case 40: goto tr694; + case 41: goto tr695; + case 68: goto tr29; + case 72: goto tr30; + case 77: goto tr31; + case 83: goto st166; + case 87: goto tr33; + case 100: goto tr29; + case 104: goto tr30; + case 109: goto tr31; + case 115: goto st166; + case 119: goto tr33; + case 1034: goto tr696; + case 1083: goto tr697; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr28; + goto tr692; +tr699: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st164; +tr700: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st164; +tr701: + { + s->line_counter++; + } + goto st164; +tr704: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st164; +tr693: + { + s->r_owner_length = s->dname_tmp_length; + } + { + if (s->number64 <= UINT32_MAX) { + s->r_ttl = (uint32_t)(s->number64); + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st164; +tr694: + { + s->r_owner_length = s->dname_tmp_length; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + { + if (s->number64 <= UINT32_MAX) { + s->r_ttl = (uint32_t)(s->number64); + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st164; +tr695: + { + s->r_owner_length = s->dname_tmp_length; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + { + if (s->number64 <= UINT32_MAX) { + s->r_ttl = (uint32_t)(s->number64); + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st164; +tr696: + { + s->r_owner_length = s->dname_tmp_length; + } + { + s->line_counter++; + } + { + if (s->number64 <= UINT32_MAX) { + s->r_ttl = (uint32_t)(s->number64); + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st164; +st164: + if ( ++p == pe ) + goto _test_eof164; +case 164: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st164; + case 32: goto st164; + case 40: goto tr699; + case 41: goto tr700; + case 65: goto tr5; + case 67: goto tr6; + case 68: goto tr7; + case 69: goto tr8; + case 72: goto tr9; + case 73: goto tr10; + case 75: goto tr11; + case 76: goto tr12; + case 77: goto tr13; + case 78: goto tr14; + case 80: goto tr15; + case 82: goto tr16; + case 83: goto tr17; + case 84: goto tr18; + case 85: goto tr19; + case 97: goto tr5; + case 99: goto tr6; + case 100: goto tr7; + case 101: goto tr8; + case 104: goto tr9; + case 105: goto tr10; + case 107: goto tr11; + case 108: goto tr12; + case 109: goto tr13; + case 110: goto tr14; + case 112: goto tr15; + case 114: goto tr16; + case 115: goto tr17; + case 116: goto tr18; + case 117: goto tr19; + case 1034: goto tr701; + case 1083: goto tr702; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr4; + goto tr583; +tr702: + { + s->buffer_length = 0; + } + goto st165; +tr703: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st165; +tr697: + { + s->r_owner_length = s->dname_tmp_length; + } + { + s->buffer_length = 0; + } + { + if (s->number64 <= UINT32_MAX) { + s->r_ttl = (uint32_t)(s->number64); + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st165; +st165: + if ( ++p == pe ) + goto _test_eof165; +case 165: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr704; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr703; + goto tr36; +tr29: + { if (s->number64 <= (UINT32_MAX / 86400)) { + s->number64 *= 86400; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st166; +tr30: + { if (s->number64 <= (UINT32_MAX / 3600)) { + s->number64 *= 3600; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st166; +tr31: + { if (s->number64 <= (UINT32_MAX / 60)) { + s->number64 *= 60; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st166; +tr33: + { if (s->number64 <= (UINT32_MAX / 604800)) { + s->number64 *= 604800; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st166; +st166: + if ( ++p == pe ) + goto _test_eof166; +case 166: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr25; + case 32: goto tr25; + case 40: goto tr26; + case 41: goto tr27; + case 1034: goto tr34; + case 1083: goto tr35; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr705; + goto tr24; +tr706: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st167; +tr705: + { + s->number64_tmp = s->number64; + } + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st167; +tr715: + { + if (s->number64 + s->number64_tmp < UINT32_MAX) { + s->number64 += s->number64_tmp; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + s->number64_tmp = s->number64; + } + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st167; +st167: + if ( ++p == pe ) + goto _test_eof167; +case 167: + switch( (*p) ) { + case 68: goto tr707; + case 72: goto tr708; + case 77: goto tr709; + case 83: goto st168; + case 87: goto tr711; + case 100: goto tr707; + case 104: goto tr708; + case 109: goto tr709; + case 115: goto st168; + case 119: goto tr711; + } + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr706; + goto tr24; +tr707: + { if (s->number64 <= (UINT32_MAX / 86400)) { + s->number64 *= 86400; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st168; +tr708: + { if (s->number64 <= (UINT32_MAX / 3600)) { + s->number64 *= 3600; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st168; +tr709: + { if (s->number64 <= (UINT32_MAX / 60)) { + s->number64 *= 60; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st168; +tr711: + { if (s->number64 <= (UINT32_MAX / 604800)) { + s->number64 *= 604800; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st168; +st168: + if ( ++p == pe ) + goto _test_eof168; +case 168: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr712; + case 32: goto tr712; + case 40: goto tr713; + case 41: goto tr714; + case 1034: goto tr716; + case 1083: goto tr717; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr715; + goto tr24; +tr56: + { + s->buffer_length = 0; + } + goto st169; +tr35: + { + if (s->number64 <= UINT32_MAX) { + s->r_ttl = (uint32_t)(s->number64); + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st169; +tr718: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st169; +tr717: + { + if (s->number64 + s->number64_tmp < UINT32_MAX) { + s->number64 += s->number64_tmp; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= UINT32_MAX) { + s->r_ttl = (uint32_t)(s->number64); + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st169; +st169: + if ( ++p == pe ) + goto _test_eof169; +case 169: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr719; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr718; + goto tr36; +tr3638: + { + s->r_class = s->default_class; + } + { + s->dname = s->r_owner; + s->r_owner_length = 0; + } + { p--; {stack[top++] = 170;goto st270;} } + goto st170; +tr3657: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + { + s->dname = s->r_owner; + s->r_owner_length = 0; + } + { p--; {stack[top++] = 170;goto st270;} } + goto st170; +st170: + if ( ++p == pe ) + goto _test_eof170; +case 170: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr721; + case 32: goto tr721; + case 40: goto tr722; + case 41: goto tr723; + case 65: goto st230; + case 70: goto st233; + case 80: goto st237; + case 97: goto st230; + case 102: goto st233; + case 112: goto st237; + case 2058: goto tr724; + case 2107: goto tr725; + case 2314: goto tr66; + case 2363: goto tr66; + case 2570: goto tr726; + case 2619: goto tr727; + } + goto tr720; +tr729: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st171; +tr730: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st171; +tr731: + { + s->line_counter++; + } + goto st171; +tr896: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st171; +tr721: + { + s->r_owner_length = s->dname_tmp_length; + } + { s->r_type = KNOT_RRTYPE_A; } + { + rdata_tail = s->r_data; + } + goto st171; +tr722: + { + s->r_owner_length = s->dname_tmp_length; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + { s->r_type = KNOT_RRTYPE_A; } + { + rdata_tail = s->r_data; + } + goto st171; +tr723: + { + s->r_owner_length = s->dname_tmp_length; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + { s->r_type = KNOT_RRTYPE_A; } + { + rdata_tail = s->r_data; + } + goto st171; +tr724: + { + s->r_owner_length = s->dname_tmp_length; + } + { + s->line_counter++; + } + { s->r_type = KNOT_RRTYPE_A; } + { + rdata_tail = s->r_data; + } + goto st171; +st171: + if ( ++p == pe ) + goto _test_eof171; +case 171: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto st171; + case 32: goto st171; + case 40: goto tr729; + case 41: goto tr730; + case 58: goto tr69; + case 65: goto tr118; + case 67: goto tr119; + case 68: goto tr120; + case 69: goto tr121; + case 72: goto tr122; + case 73: goto tr123; + case 75: goto tr124; + case 76: goto tr125; + case 77: goto tr126; + case 78: goto tr127; + case 80: goto tr128; + case 82: goto tr129; + case 83: goto tr130; + case 84: goto tr131; + case 85: goto tr132; + case 92: goto tr74; + case 97: goto tr118; + case 99: goto tr119; + case 100: goto tr120; + case 101: goto tr121; + case 104: goto tr122; + case 105: goto tr123; + case 107: goto tr124; + case 108: goto tr125; + case 109: goto tr126; + case 110: goto tr127; + case 112: goto tr128; + case 114: goto tr129; + case 115: goto tr130; + case 116: goto tr131; + case 117: goto tr132; + case 2058: goto tr731; + case 2107: goto tr732; + case 2314: goto tr69; + case 2363: goto tr69; + case 2570: goto tr733; + case 2619: goto tr734; + } + if ( _widec < 11 ) { + if ( _widec <= 8 ) + goto tr69; + } else if ( _widec > 47 ) { + if ( _widec > 57 ) { + if ( 60 <= _widec ) + goto tr69; + } else if ( _widec >= 48 ) + goto tr117; + } else + goto tr69; + goto tr585; +tr118: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 172;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 172;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 172;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 172;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 172;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 172;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 172;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 172;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 172;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 172;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 172;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 172;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 172;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 172;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 172;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 172;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 172;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 172;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 172;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 172;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 172;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 172;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 172;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 172;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 172;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 172;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 172;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 172;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 172;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st172; +st172: + if ( ++p == pe ) + goto _test_eof172; +case 172: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr736; + case 32: goto tr736; + case 40: goto tr737; + case 41: goto tr738; + case 65: goto st230; + case 70: goto st233; + case 80: goto st237; + case 97: goto st230; + case 102: goto st233; + case 112: goto st237; + case 1802: goto tr83; + case 1851: goto tr84; + case 2058: goto tr739; + case 2107: goto tr740; + case 2314: goto tr741; + case 2363: goto tr742; + case 2570: goto tr743; + case 2619: goto tr744; + } + goto tr735; +tr746: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st173; +tr747: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st173; +tr736: + { s->r_type = KNOT_RRTYPE_A; } + { + rdata_tail = s->r_data; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st173; +tr737: + { s->r_type = KNOT_RRTYPE_A; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st173; +tr738: + { s->r_type = KNOT_RRTYPE_A; } + { + rdata_tail = s->r_data; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st173; +st173: + if ( ++p == pe ) + goto _test_eof173; +case 173: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto st173; + case 32: goto st173; + case 40: goto tr746; + case 41: goto tr747; + case 92: goto tr74; + case 1802: goto tr89; + case 1851: goto tr90; + case 2058: goto tr748; + case 2107: goto tr107; + case 2314: goto tr108; + case 2363: goto tr109; + case 2570: goto tr749; + case 2619: goto tr110; + } + if ( _widec < 11 ) { + if ( _widec <= 8 ) + goto tr69; + } else if ( _widec > 58 ) { + if ( 60 <= _widec ) + goto tr69; + } else + goto tr69; + goto tr79; +tr101: + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 1133;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 1133;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 1133;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 1133;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 1133;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 1133;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 1133;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 1133;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 1133;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 1133;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 1133;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 1133;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 1133;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 1133;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 1133;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 1133;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 1133;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 1133;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 1133;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 1133;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 1133;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 1133;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 1133;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 1133;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 1133;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 1133;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 1133;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 1133;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 1133;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + if (rdata_tail - s->r_data > UINT16_MAX) { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + s->r_data_length = rdata_tail - s->r_data; + + s->state = ZS_STATE_DATA; + + // Execute the record callback. + if (s->process.automatic) { + if (s->process.record != NULL) { + s->process.record(s); + + // Stop if required from the callback. + if (s->state == ZS_STATE_STOP) { + {p++; cs = 1133; goto _out;} + } + } + } else { + // Return if external processing. + p--; {p++; cs = 1133; goto _out;} + } + } + { + s->line_counter++; + } + goto st1133; +tr108: + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 1133;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 1133;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 1133;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 1133;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 1133;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 1133;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 1133;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 1133;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 1133;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 1133;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 1133;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 1133;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 1133;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 1133;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 1133;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 1133;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 1133;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 1133;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 1133;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 1133;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 1133;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 1133;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 1133;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 1133;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 1133;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 1133;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 1133;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 1133;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 1133;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + { + if (rdata_tail - s->r_data > UINT16_MAX) { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + s->r_data_length = rdata_tail - s->r_data; + + s->state = ZS_STATE_DATA; + + // Execute the record callback. + if (s->process.automatic) { + if (s->process.record != NULL) { + s->process.record(s); + + // Stop if required from the callback. + if (s->state == ZS_STATE_STOP) { + {p++; cs = 1133; goto _out;} + } + } + } else { + // Return if external processing. + p--; {p++; cs = 1133; goto _out;} + } + } + { + s->line_counter++; + } + goto st1133; +tr135: + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 1133;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 1133;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 1133;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 1133;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 1133;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 1133;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 1133;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 1133;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 1133;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 1133;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 1133;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 1133;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 1133;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 1133;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 1133;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 1133;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 1133;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 1133;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 1133;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 1133;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 1133;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 1133;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 1133;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 1133;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 1133;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 1133;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 1133;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 1133;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 1133;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + { + s->line_counter++; + } + goto st1133; +tr741: + { s->r_type = KNOT_RRTYPE_A; } + { + rdata_tail = s->r_data; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 1133;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 1133;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 1133;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 1133;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 1133;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 1133;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 1133;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 1133;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 1133;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 1133;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 1133;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 1133;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 1133;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 1133;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 1133;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 1133;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 1133;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 1133;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 1133;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 1133;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 1133;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 1133;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 1133;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 1133;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 1133;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 1133;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 1133;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 1133;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 1133;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + if (rdata_tail - s->r_data > UINT16_MAX) { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + s->r_data_length = rdata_tail - s->r_data; + + s->state = ZS_STATE_DATA; + + // Execute the record callback. + if (s->process.automatic) { + if (s->process.record != NULL) { + s->process.record(s); + + // Stop if required from the callback. + if (s->state == ZS_STATE_STOP) { + {p++; cs = 1133; goto _out;} + } + } + } else { + // Return if external processing. + p--; {p++; cs = 1133; goto _out;} + } + } + { + s->line_counter++; + } + goto st1133; +tr3630: + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 1133;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 1133;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 1133;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 1133;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 1133;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 1133;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 1133;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 1133;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 1133;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 1133;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 1133;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 1133;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 1133;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 1133;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 1133;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 1133;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 1133;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 1133;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 1133;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 1133;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 1133;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 1133;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 1133;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 1133;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 1133;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 1133;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 1133;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 1133;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 1133;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + s->line_counter++; + } + goto st1133; +st1133: + if ( ++p == pe ) + goto _test_eof1133; +case 1133: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3686; + case 32: goto tr3686; + case 36: goto st152; + case 40: goto tr3687; + case 41: goto tr3688; + case 42: goto tr3617; + case 92: goto tr3617; + case 95: goto tr3617; + case 778: goto tr83; + case 827: goto tr84; + case 1034: goto tr3689; + case 1083: goto tr3690; + } + if ( _widec < 64 ) { + if ( 45 <= _widec && _widec <= 57 ) + goto tr3617; + } else if ( _widec > 90 ) { + if ( 97 <= _widec && _widec <= 122 ) + goto tr3617; + } else + goto tr3617; + goto tr783; +tr751: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st174; +tr752: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st174; +tr821: + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st174; +tr822: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st174; +tr823: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st174; +tr784: + { + s->r_owner_length = s->dname_tmp_length; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st174; +tr785: + { + s->r_owner_length = s->dname_tmp_length; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st174; +tr786: + { + s->r_owner_length = s->dname_tmp_length; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st174; +tr3686: + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + if (s->r_owner_length == 0) { + WARN(ZS_BAD_PREVIOUS_OWNER); + p--; {goto st268;} + } + } + goto st174; +tr3687: + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + { + if (s->r_owner_length == 0) { + WARN(ZS_BAD_PREVIOUS_OWNER); + p--; {goto st268;} + } + } + goto st174; +tr3688: + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + { + if (s->r_owner_length == 0) { + WARN(ZS_BAD_PREVIOUS_OWNER); + p--; {goto st268;} + } + } + goto st174; +st174: + if ( ++p == pe ) + goto _test_eof174; +case 174: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st174; + case 32: goto st174; + case 40: goto tr751; + case 41: goto tr752; + case 65: goto tr5; + case 67: goto tr6; + case 68: goto tr7; + case 69: goto tr8; + case 72: goto tr9; + case 73: goto tr10; + case 75: goto tr11; + case 76: goto tr12; + case 77: goto tr13; + case 78: goto tr14; + case 80: goto tr15; + case 82: goto tr16; + case 83: goto tr17; + case 84: goto tr18; + case 85: goto tr19; + case 97: goto tr5; + case 99: goto tr6; + case 100: goto tr7; + case 101: goto tr8; + case 104: goto tr9; + case 105: goto tr10; + case 107: goto tr11; + case 108: goto tr12; + case 109: goto tr13; + case 110: goto tr14; + case 112: goto tr15; + case 114: goto tr16; + case 115: goto tr17; + case 116: goto tr18; + case 117: goto tr19; + case 778: goto tr89; + case 827: goto tr90; + case 1034: goto tr753; + case 1083: goto tr754; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr4; + goto tr0; +tr754: + { + s->buffer_length = 0; + } + goto st175; +tr755: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st175; +tr825: + { + s->buffer_length = 0; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st175; +tr830: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + s->buffer_length = 0; + } + goto st175; +tr834: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + { + s->buffer_length = 0; + } + goto st175; +tr788: + { + s->r_owner_length = s->dname_tmp_length; + } + { + s->buffer_length = 0; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st175; +tr3690: + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + s->buffer_length = 0; + } + { + if (s->r_owner_length == 0) { + WARN(ZS_BAD_PREVIOUS_OWNER); + p--; {goto st268;} + } + } + goto st175; +st175: + if ( ++p == pe ) + goto _test_eof175; +case 175: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 778: goto tr92; + case 1034: goto tr756; + } + if ( _widec > 895 ) { + if ( 896 <= _widec && _widec <= 1151 ) + goto tr755; + } else if ( _widec >= 640 ) + goto tr91; + goto tr85; +tr102: + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 176;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 176;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 176;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 176;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 176;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 176;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 176;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 176;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 176;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 176;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 176;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 176;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 176;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 176;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 176;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 176;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 176;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 176;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 176;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 176;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 176;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 176;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 176;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 176;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 176;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 176;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 176;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 176;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 176;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + s->buffer_length = 0; + } + goto st176; +tr109: + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 176;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 176;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 176;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 176;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 176;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 176;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 176;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 176;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 176;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 176;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 176;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 176;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 176;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 176;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 176;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 176;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 176;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 176;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 176;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 176;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 176;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 176;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 176;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 176;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 176;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 176;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 176;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 176;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 176;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st176; +tr742: + { s->r_type = KNOT_RRTYPE_A; } + { + rdata_tail = s->r_data; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 176;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 176;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 176;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 176;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 176;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 176;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 176;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 176;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 176;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 176;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 176;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 176;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 176;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 176;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 176;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 176;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 176;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 176;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 176;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 176;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 176;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 176;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 176;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 176;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 176;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 176;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 176;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 176;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 176;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + s->buffer_length = 0; + } + goto st176; +st176: + if ( ++p == pe ) + goto _test_eof176; +case 176: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr757; + case 32: goto tr757; + case 40: goto tr758; + case 41: goto tr759; + case 778: goto tr760; + case 827: goto tr761; + case 1034: goto tr760; + case 1083: goto tr761; + } + if ( _widec < 11 ) { + if ( _widec <= 8 ) + goto tr91; + } else if ( _widec > 58 ) { + if ( 60 <= _widec ) + goto tr91; + } else + goto tr91; + goto tr79; +tr762: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st177; +tr757: + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st177; +tr758: + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st177; +tr759: + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st177; +tr763: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st177; +tr764: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st177; +st177: + if ( ++p == pe ) + goto _test_eof177; +case 177: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr762; + case 32: goto tr762; + case 40: goto tr763; + case 41: goto tr764; + case 778: goto tr92; + case 827: goto tr765; + case 1034: goto tr92; + case 1083: goto tr765; + } + if ( _widec < 11 ) { + if ( _widec <= 8 ) + goto tr91; + } else if ( _widec > 58 ) { + if ( 60 <= _widec ) + goto tr91; + } else + goto tr91; + goto tr85; +tr743: + { s->r_type = KNOT_RRTYPE_A; } + { + rdata_tail = s->r_data; + } + { + s->line_counter++; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 1134;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 1134;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 1134;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 1134;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 1134;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 1134;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 1134;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 1134;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 1134;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 1134;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 1134;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 1134;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 1134;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 1134;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 1134;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 1134;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 1134;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 1134;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 1134;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 1134;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 1134;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 1134;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 1134;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 1134;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 1134;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 1134;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 1134;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 1134;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 1134;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + if (rdata_tail - s->r_data > UINT16_MAX) { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + s->r_data_length = rdata_tail - s->r_data; + + s->state = ZS_STATE_DATA; + + // Execute the record callback. + if (s->process.automatic) { + if (s->process.record != NULL) { + s->process.record(s); + + // Stop if required from the callback. + if (s->state == ZS_STATE_STOP) { + {p++; cs = 1134; goto _out;} + } + } + } else { + // Return if external processing. + p--; {p++; cs = 1134; goto _out;} + } + } + goto st1134; +tr749: + { + s->line_counter++; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 1134;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 1134;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 1134;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 1134;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 1134;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 1134;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 1134;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 1134;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 1134;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 1134;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 1134;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 1134;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 1134;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 1134;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 1134;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 1134;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 1134;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 1134;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 1134;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 1134;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 1134;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 1134;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 1134;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 1134;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 1134;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 1134;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 1134;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 1134;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 1134;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + { + if (rdata_tail - s->r_data > UINT16_MAX) { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + s->r_data_length = rdata_tail - s->r_data; + + s->state = ZS_STATE_DATA; + + // Execute the record callback. + if (s->process.automatic) { + if (s->process.record != NULL) { + s->process.record(s); + + // Stop if required from the callback. + if (s->state == ZS_STATE_STOP) { + {p++; cs = 1134; goto _out;} + } + } + } else { + // Return if external processing. + p--; {p++; cs = 1134; goto _out;} + } + } + goto st1134; +st1134: + if ( ++p == pe ) + goto _test_eof1134; +case 1134: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr3691; + case 32: goto tr3691; + case 36: goto tr3623; + case 40: goto tr3692; + case 41: goto tr3693; + case 42: goto tr3626; + case 58: goto tr69; + case 92: goto tr3627; + case 95: goto tr3626; + case 1802: goto tr83; + case 1851: goto tr84; + case 2058: goto tr3689; + case 2107: goto tr3694; + case 2314: goto tr101; + case 2363: goto tr102; + case 2570: goto tr3689; + case 2619: goto tr3695; + } + if ( _widec < 60 ) { + if ( _widec < 11 ) { + if ( _widec <= 8 ) + goto tr69; + } else if ( _widec > 44 ) { + if ( 45 <= _widec && _widec <= 57 ) + goto tr3626; + } else + goto tr69; + } else if ( _widec > 63 ) { + if ( _widec < 91 ) { + if ( 64 <= _widec && _widec <= 90 ) + goto tr3626; + } else if ( _widec > 96 ) { + if ( _widec > 122 ) { + if ( 123 <= _widec ) + goto tr69; + } else if ( _widec >= 97 ) + goto tr3626; + } else + goto tr69; + } else + goto tr69; + goto tr783; +tr767: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st178; +tr768: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st178; +tr897: + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st178; +tr898: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st178; +tr899: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st178; +tr3691: + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + if (s->r_owner_length == 0) { + WARN(ZS_BAD_PREVIOUS_OWNER); + p--; {goto st268;} + } + } + goto st178; +tr3692: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + if (s->r_owner_length == 0) { + WARN(ZS_BAD_PREVIOUS_OWNER); + p--; {goto st268;} + } + } + goto st178; +tr3693: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + if (s->r_owner_length == 0) { + WARN(ZS_BAD_PREVIOUS_OWNER); + p--; {goto st268;} + } + } + goto st178; +st178: + if ( ++p == pe ) + goto _test_eof178; +case 178: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto st178; + case 32: goto st178; + case 40: goto tr767; + case 41: goto tr768; + case 58: goto tr69; + case 65: goto tr118; + case 67: goto tr119; + case 68: goto tr120; + case 69: goto tr121; + case 72: goto tr122; + case 73: goto tr123; + case 75: goto tr124; + case 76: goto tr125; + case 77: goto tr126; + case 78: goto tr127; + case 80: goto tr128; + case 82: goto tr129; + case 83: goto tr130; + case 84: goto tr131; + case 85: goto tr132; + case 92: goto tr74; + case 97: goto tr118; + case 99: goto tr119; + case 100: goto tr120; + case 101: goto tr121; + case 104: goto tr122; + case 105: goto tr123; + case 107: goto tr124; + case 108: goto tr125; + case 109: goto tr126; + case 110: goto tr127; + case 112: goto tr128; + case 114: goto tr129; + case 115: goto tr130; + case 116: goto tr131; + case 117: goto tr132; + case 1802: goto tr89; + case 1851: goto tr90; + case 2058: goto tr753; + case 2107: goto tr769; + case 2314: goto tr108; + case 2363: goto tr109; + case 2570: goto tr753; + case 2619: goto tr770; + } + if ( _widec < 11 ) { + if ( _widec <= 8 ) + goto tr69; + } else if ( _widec > 47 ) { + if ( _widec > 57 ) { + if ( 60 <= _widec ) + goto tr69; + } else if ( _widec >= 48 ) + goto tr117; + } else + goto tr69; + goto tr114; +tr119: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 179;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 179;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 179;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 179;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 179;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 179;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 179;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 179;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 179;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 179;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 179;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 179;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 179;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 179;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 179;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 179;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 179;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 179;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 179;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 179;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 179;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 179;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 179;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 179;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 179;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 179;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 179;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 179;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 179;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st179; +st179: + if ( ++p == pe ) + goto _test_eof179; +case 179: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr80; + case 32: goto tr80; + case 40: goto tr81; + case 41: goto tr82; + case 65: goto st18; + case 68: goto st22; + case 69: goto st29; + case 78: goto st32; + case 97: goto st18; + case 100: goto st22; + case 101: goto st29; + case 110: goto st32; + case 778: goto tr83; + case 827: goto tr84; + case 1034: goto tr83; + case 1083: goto tr84; + } + goto tr735; +tr120: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 180;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 180;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 180;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 180;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 180;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 180;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 180;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 180;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 180;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 180;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 180;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 180;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 180;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 180;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 180;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 180;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 180;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 180;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 180;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 180;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 180;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 180;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 180;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 180;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 180;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 180;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 180;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 180;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 180;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st180; +st180: + if ( ++p == pe ) + goto _test_eof180; +case 180: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr80; + case 32: goto tr80; + case 40: goto tr81; + case 41: goto tr82; + case 72: goto st37; + case 78: goto st41; + case 83: goto st49; + case 104: goto st37; + case 110: goto st41; + case 115: goto st49; + case 778: goto tr83; + case 827: goto tr84; + case 1034: goto tr83; + case 1083: goto tr84; + } + goto tr735; +tr121: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 181;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 181;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 181;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 181;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 181;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 181;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 181;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 181;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 181;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 181;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 181;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 181;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 181;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 181;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 181;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 181;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 181;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 181;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 181;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 181;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 181;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 181;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 181;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 181;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 181;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 181;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 181;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 181;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 181;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st181; +st181: + if ( ++p == pe ) + goto _test_eof181; +case 181: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr80; + case 32: goto tr80; + case 40: goto tr81; + case 41: goto tr82; + case 85: goto st51; + case 117: goto st51; + case 778: goto tr83; + case 827: goto tr84; + case 1034: goto tr83; + case 1083: goto tr84; + } + goto tr735; +tr122: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 182;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 182;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 182;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 182;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 182;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 182;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 182;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 182;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 182;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 182;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 182;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 182;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 182;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 182;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 182;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 182;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 182;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 182;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 182;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 182;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 182;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 182;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 182;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 182;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 182;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 182;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 182;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 182;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 182;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st182; +st182: + if ( ++p == pe ) + goto _test_eof182; +case 182: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr80; + case 32: goto tr80; + case 40: goto tr81; + case 41: goto tr82; + case 73: goto st58; + case 105: goto st58; + case 778: goto tr83; + case 827: goto tr84; + case 1034: goto tr83; + case 1083: goto tr84; + } + goto tr735; +tr123: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 183;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 183;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 183;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 183;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 183;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 183;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 183;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 183;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 183;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 183;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 183;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 183;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 183;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 183;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 183;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 183;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 183;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 183;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 183;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 183;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 183;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 183;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 183;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 183;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 183;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 183;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 183;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 183;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 183;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st183; +st183: + if ( ++p == pe ) + goto _test_eof183; +case 183: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr80; + case 32: goto tr80; + case 40: goto tr81; + case 41: goto tr82; + case 78: goto st144; + case 80: goto st66; + case 110: goto st144; + case 112: goto st66; + case 778: goto tr83; + case 827: goto tr84; + case 1034: goto tr83; + case 1083: goto tr84; + } + goto tr735; +tr124: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 184;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 184;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 184;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 184;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 184;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 184;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 184;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 184;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 184;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 184;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 184;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 184;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 184;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 184;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 184;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 184;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 184;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 184;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 184;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 184;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 184;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 184;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 184;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 184;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 184;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 184;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 184;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 184;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 184;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st184; +st184: + if ( ++p == pe ) + goto _test_eof184; +case 184: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr80; + case 32: goto tr80; + case 40: goto tr81; + case 41: goto tr82; + case 69: goto st74; + case 88: goto st76; + case 101: goto st74; + case 120: goto st76; + case 778: goto tr83; + case 827: goto tr84; + case 1034: goto tr83; + case 1083: goto tr84; + } + goto tr735; +tr125: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 185;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 185;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 185;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 185;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 185;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 185;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 185;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 185;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 185;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 185;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 185;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 185;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 185;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 185;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 185;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 185;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 185;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 185;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 185;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 185;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 185;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 185;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 185;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 185;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 185;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 185;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 185;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 185;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 185;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st185; +st185: + if ( ++p == pe ) + goto _test_eof185; +case 185: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr80; + case 32: goto tr80; + case 40: goto tr81; + case 41: goto tr82; + case 51: goto st78; + case 54: goto st80; + case 79: goto st82; + case 80: goto st84; + case 111: goto st82; + case 112: goto st84; + case 778: goto tr83; + case 827: goto tr84; + case 1034: goto tr83; + case 1083: goto tr84; + } + goto tr735; +tr126: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 186;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 186;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 186;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 186;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 186;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 186;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 186;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 186;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 186;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 186;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 186;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 186;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 186;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 186;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 186;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 186;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 186;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 186;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 186;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 186;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 186;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 186;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 186;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 186;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 186;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 186;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 186;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 186;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 186;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st186; +st186: + if ( ++p == pe ) + goto _test_eof186; +case 186: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr80; + case 32: goto tr80; + case 40: goto tr81; + case 41: goto tr82; + case 73: goto st86; + case 88: goto st90; + case 105: goto st86; + case 120: goto st90; + case 778: goto tr83; + case 827: goto tr84; + case 1034: goto tr83; + case 1083: goto tr84; + } + goto tr735; +tr127: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 187;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 187;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 187;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 187;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 187;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 187;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 187;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 187;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 187;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 187;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 187;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 187;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 187;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 187;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 187;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 187;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 187;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 187;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 187;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 187;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 187;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 187;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 187;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 187;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 187;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 187;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 187;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 187;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 187;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st187; +st187: + if ( ++p == pe ) + goto _test_eof187; +case 187: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr80; + case 32: goto tr80; + case 40: goto tr81; + case 41: goto tr82; + case 65: goto st92; + case 73: goto st96; + case 83: goto st98; + case 97: goto st92; + case 105: goto st96; + case 115: goto st98; + case 778: goto tr83; + case 827: goto tr84; + case 1034: goto tr83; + case 1083: goto tr84; + } + goto tr735; +tr128: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 188;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 188;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 188;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 188;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 188;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 188;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 188;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 188;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 188;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 188;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 188;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 188;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 188;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 188;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 188;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 188;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 188;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 188;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 188;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 188;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 188;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 188;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 188;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 188;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 188;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 188;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 188;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 188;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 188;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st188; +st188: + if ( ++p == pe ) + goto _test_eof188; +case 188: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr80; + case 32: goto tr80; + case 40: goto tr81; + case 41: goto tr82; + case 84: goto st108; + case 116: goto st108; + case 778: goto tr83; + case 827: goto tr84; + case 1034: goto tr83; + case 1083: goto tr84; + } + goto tr735; +tr129: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 189;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 189;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 189;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 189;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 189;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 189;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 189;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 189;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 189;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 189;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 189;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 189;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 189;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 189;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 189;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 189;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 189;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 189;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 189;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 189;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 189;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 189;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 189;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 189;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 189;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 189;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 189;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 189;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 189;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st189; +st189: + if ( ++p == pe ) + goto _test_eof189; +case 189: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr80; + case 32: goto tr80; + case 40: goto tr81; + case 41: goto tr82; + case 80: goto st111; + case 82: goto st112; + case 84: goto st116; + case 112: goto st111; + case 114: goto st112; + case 116: goto st116; + case 778: goto tr83; + case 827: goto tr84; + case 1034: goto tr83; + case 1083: goto tr84; + } + goto tr735; +tr130: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 190;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 190;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 190;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 190;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 190;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 190;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 190;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 190;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 190;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 190;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 190;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 190;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 190;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 190;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 190;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 190;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 190;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 190;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 190;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 190;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 190;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 190;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 190;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 190;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 190;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 190;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 190;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 190;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 190;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st190; +st190: + if ( ++p == pe ) + goto _test_eof190; +case 190: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr80; + case 32: goto tr80; + case 40: goto tr81; + case 41: goto tr82; + case 79: goto st118; + case 80: goto st120; + case 82: goto st122; + case 83: goto st124; + case 111: goto st118; + case 112: goto st120; + case 114: goto st122; + case 115: goto st124; + case 778: goto tr83; + case 827: goto tr84; + case 1034: goto tr83; + case 1083: goto tr84; + } + goto tr735; +tr131: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 191;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 191;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 191;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 191;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 191;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 191;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 191;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 191;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 191;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 191;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 191;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 191;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 191;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 191;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 191;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 191;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 191;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 191;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 191;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 191;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 191;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 191;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 191;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 191;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 191;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 191;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 191;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 191;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 191;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st191; +st191: + if ( ++p == pe ) + goto _test_eof191; +case 191: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr80; + case 32: goto tr80; + case 40: goto tr81; + case 41: goto tr82; + case 76: goto st129; + case 88: goto st132; + case 89: goto st134; + case 108: goto st129; + case 120: goto st132; + case 121: goto st134; + case 778: goto tr83; + case 827: goto tr84; + case 1034: goto tr83; + case 1083: goto tr84; + } + goto tr735; +tr132: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 192;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 192;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 192;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 192;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 192;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 192;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 192;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 192;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 192;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 192;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 192;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 192;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 192;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 192;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 192;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 192;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 192;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 192;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 192;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 192;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 192;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 192;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 192;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 192;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 192;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 192;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 192;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 192;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 192;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st192; +st192: + if ( ++p == pe ) + goto _test_eof192; +case 192: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr80; + case 32: goto tr80; + case 40: goto tr81; + case 41: goto tr82; + case 82: goto st139; + case 114: goto st139; + case 778: goto tr83; + case 827: goto tr84; + case 1034: goto tr83; + case 1083: goto tr84; + } + goto tr735; +tr769: + { + s->buffer_length = 0; + } + goto st193; +tr771: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st193; +tr900: + { + s->buffer_length = 0; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st193; +tr838: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + s->buffer_length = 0; + } + goto st193; +tr842: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + { + s->buffer_length = 0; + } + goto st193; +tr807: + { + s->r_owner_length = s->dname_tmp_length; + } + { + s->buffer_length = 0; + } + { s->r_type = KNOT_RRTYPE_A; } + { + rdata_tail = s->r_data; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st193; +tr3694: + { + s->buffer_length = 0; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + if (s->r_owner_length == 0) { + WARN(ZS_BAD_PREVIOUS_OWNER); + p--; {goto st268;} + } + } + goto st193; +st193: + if ( ++p == pe ) + goto _test_eof193; +case 193: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 778: goto tr92; + case 1034: goto tr772; + } + if ( _widec > 895 ) { + if ( 896 <= _widec && _widec <= 1151 ) + goto tr771; + } else if ( _widec >= 640 ) + goto tr91; + goto tr79; +tr133: + { + s->line_counter++; + } + goto st1135; +tr874: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st1135; +tr772: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + { + if (rdata_tail - s->r_data > UINT16_MAX) { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + s->r_data_length = rdata_tail - s->r_data; + + s->state = ZS_STATE_DATA; + + // Execute the record callback. + if (s->process.automatic) { + if (s->process.record != NULL) { + s->process.record(s); + + // Stop if required from the callback. + if (s->state == ZS_STATE_STOP) { + {p++; cs = 1135; goto _out;} + } + } + } else { + // Return if external processing. + p--; {p++; cs = 1135; goto _out;} + } + } + goto st1135; +tr813: + { + s->line_counter++; + } + { + if (rdata_tail - s->r_data > UINT16_MAX) { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + s->r_data_length = rdata_tail - s->r_data; + + s->state = ZS_STATE_DATA; + + // Execute the record callback. + if (s->process.automatic) { + if (s->process.record != NULL) { + s->process.record(s); + + // Stop if required from the callback. + if (s->state == ZS_STATE_STOP) { + {p++; cs = 1135; goto _out;} + } + } + } else { + // Return if external processing. + p--; {p++; cs = 1135; goto _out;} + } + } + goto st1135; +tr806: + { + s->r_owner_length = s->dname_tmp_length; + } + { + s->line_counter++; + } + { s->r_type = KNOT_RRTYPE_A; } + { + rdata_tail = s->r_data; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + if (rdata_tail - s->r_data > UINT16_MAX) { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + s->r_data_length = rdata_tail - s->r_data; + + s->state = ZS_STATE_DATA; + + // Execute the record callback. + if (s->process.automatic) { + if (s->process.record != NULL) { + s->process.record(s); + + // Stop if required from the callback. + if (s->state == ZS_STATE_STOP) { + {p++; cs = 1135; goto _out;} + } + } + } else { + // Return if external processing. + p--; {p++; cs = 1135; goto _out;} + } + } + goto st1135; +tr3628: + { + s->line_counter++; + } + { + if (s->r_owner_length == 0) { + WARN(ZS_BAD_PREVIOUS_OWNER); + p--; {goto st268;} + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st1135; +st1135: + if ( ++p == pe ) + goto _test_eof1135; +case 1135: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr3622; + case 32: goto tr3622; + case 36: goto tr3623; + case 40: goto tr3624; + case 41: goto tr3625; + case 42: goto tr3626; + case 58: goto tr69; + case 65: goto tr3698; + case 67: goto tr3699; + case 68: goto tr3700; + case 69: goto tr3701; + case 72: goto tr3702; + case 73: goto tr3703; + case 75: goto tr3704; + case 76: goto tr3705; + case 77: goto tr3706; + case 78: goto tr3707; + case 80: goto tr3708; + case 82: goto tr3709; + case 83: goto tr3710; + case 84: goto tr3711; + case 85: goto tr3712; + case 92: goto tr3627; + case 95: goto tr3626; + case 97: goto tr3698; + case 99: goto tr3699; + case 100: goto tr3700; + case 101: goto tr3701; + case 104: goto tr3702; + case 105: goto tr3703; + case 107: goto tr3704; + case 108: goto tr3705; + case 109: goto tr3706; + case 110: goto tr3707; + case 112: goto tr3708; + case 114: goto tr3709; + case 115: goto tr3710; + case 116: goto tr3711; + case 117: goto tr3712; + case 1802: goto tr3618; + case 1851: goto tr3619; + case 2058: goto tr3628; + case 2107: goto tr3629; + case 2314: goto tr3630; + case 2363: goto tr3631; + case 2570: goto tr3632; + case 2619: goto tr3633; + } + if ( _widec < 60 ) { + if ( _widec < 11 ) { + if ( _widec <= 8 ) + goto tr69; + } else if ( _widec > 44 ) { + if ( _widec > 47 ) { + if ( 48 <= _widec && _widec <= 57 ) + goto tr3697; + } else if ( _widec >= 45 ) + goto tr3626; + } else + goto tr69; + } else if ( _widec > 63 ) { + if ( _widec < 91 ) { + if ( 64 <= _widec && _widec <= 90 ) + goto tr3626; + } else if ( _widec > 96 ) { + if ( _widec > 122 ) { + if ( 123 <= _widec ) + goto tr69; + } else if ( _widec >= 98 ) + goto tr3626; + } else + goto tr69; + } else + goto tr69; + goto tr3696; +tr3623: + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 194;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 194;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 194;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 194;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 194;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 194;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 194;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 194;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 194;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 194;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 194;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 194;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 194;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 194;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 194;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 194;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 194;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 194;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 194;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 194;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 194;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 194;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 194;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 194;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 194;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 194;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 194;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 194;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 194;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st194; +st194: + if ( ++p == pe ) + goto _test_eof194; +case 194: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr80; + case 32: goto tr80; + case 40: goto tr81; + case 41: goto tr82; + case 73: goto tr668; + case 79: goto tr669; + case 84: goto tr670; + case 105: goto tr668; + case 111: goto tr669; + case 116: goto tr670; + case 778: goto tr83; + case 827: goto tr84; + case 1034: goto tr83; + case 1083: goto tr84; + } + goto tr773; +tr669: + { + ERR(ZS_OK); + } + goto st195; +st195: + if ( ++p == pe ) + goto _test_eof195; +case 195: + switch( (*p) ) { + case 82: goto st196; + case 114: goto st196; + } + goto tr667; +st196: + if ( ++p == pe ) + goto _test_eof196; +case 196: + switch( (*p) ) { + case 73: goto st197; + case 105: goto st197; + } + goto tr667; +st197: + if ( ++p == pe ) + goto _test_eof197; +case 197: + switch( (*p) ) { + case 71: goto st198; + case 103: goto st198; + } + goto tr667; +st198: + if ( ++p == pe ) + goto _test_eof198; +case 198: + switch( (*p) ) { + case 73: goto st199; + case 105: goto st199; + } + goto tr667; +st199: + if ( ++p == pe ) + goto _test_eof199; +case 199: + switch( (*p) ) { + case 78: goto st200; + case 110: goto st200; + } + goto tr667; +st200: + if ( ++p == pe ) + goto _test_eof200; +case 200: + switch( (*p) ) { + case 32: goto tr779; + case 59: goto tr779; + } + if ( (*p) > 10 ) { + if ( 40 <= (*p) && (*p) <= 41 ) + goto tr779; + } else if ( (*p) >= 9 ) + goto tr779; + goto tr667; +tr670: + { + ERR(ZS_OK); + } + goto st201; +st201: + if ( ++p == pe ) + goto _test_eof201; +case 201: + switch( (*p) ) { + case 84: goto st202; + case 116: goto st202; + } + goto tr667; +st202: + if ( ++p == pe ) + goto _test_eof202; +case 202: + switch( (*p) ) { + case 76: goto st203; + case 108: goto st203; + } + goto tr667; +st203: + if ( ++p == pe ) + goto _test_eof203; +case 203: + switch( (*p) ) { + case 32: goto tr782; + case 59: goto tr782; + } + if ( (*p) > 10 ) { + if ( 40 <= (*p) && (*p) <= 41 ) + goto tr782; + } else if ( (*p) >= 9 ) + goto tr782; + goto tr667; +tr3626: + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 204;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 204;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 204;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 204;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 204;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 204;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 204;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 204;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 204;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 204;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 204;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 204;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 204;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 204;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 204;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 204;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 204;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 204;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 204;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 204;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 204;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 204;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 204;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 204;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 204;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 204;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 204;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 204;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 204;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + { + s->dname = s->r_owner; + s->r_owner_length = 0; + } + { p--; {stack[top++] = 204;goto st270;} } + goto st204; +st204: + if ( ++p == pe ) + goto _test_eof204; +case 204: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr784; + case 32: goto tr784; + case 40: goto tr785; + case 41: goto tr786; + case 778: goto tr83; + case 827: goto tr84; + case 1034: goto tr787; + case 1083: goto tr788; + } + goto tr783; +tr3697: + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 205;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 205;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 205;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 205;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 205;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 205;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 205;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 205;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 205;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 205;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 205;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 205;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 205;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 205;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 205;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 205;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 205;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 205;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 205;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 205;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 205;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 205;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 205;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 205;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 205;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 205;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 205;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 205;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 205;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + { + s->dname = s->r_owner; + s->r_owner_length = 0; + } + { p--; {stack[top++] = 205;goto st270;} } + goto st205; +st205: + if ( ++p == pe ) + goto _test_eof205; +case 205: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr790; + case 32: goto tr790; + case 40: goto tr791; + case 41: goto tr792; + case 68: goto tr29; + case 72: goto tr30; + case 77: goto tr31; + case 83: goto st166; + case 87: goto tr33; + case 100: goto tr29; + case 104: goto tr30; + case 109: goto tr31; + case 115: goto st166; + case 119: goto tr33; + case 778: goto tr83; + case 827: goto tr84; + case 1034: goto tr793; + case 1083: goto tr794; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr28; + goto tr789; +tr796: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st206; +tr797: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st206; +tr790: + { + s->r_owner_length = s->dname_tmp_length; + } + { + if (s->number64 <= UINT32_MAX) { + s->r_ttl = (uint32_t)(s->number64); + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st206; +tr791: + { + s->r_owner_length = s->dname_tmp_length; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + { + if (s->number64 <= UINT32_MAX) { + s->r_ttl = (uint32_t)(s->number64); + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st206; +tr792: + { + s->r_owner_length = s->dname_tmp_length; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + { + if (s->number64 <= UINT32_MAX) { + s->r_ttl = (uint32_t)(s->number64); + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st206; +st206: + if ( ++p == pe ) + goto _test_eof206; +case 206: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st206; + case 32: goto st206; + case 40: goto tr796; + case 41: goto tr797; + case 65: goto tr5; + case 67: goto tr6; + case 68: goto tr7; + case 69: goto tr8; + case 72: goto tr9; + case 73: goto tr10; + case 75: goto tr11; + case 76: goto tr12; + case 77: goto tr13; + case 78: goto tr14; + case 80: goto tr15; + case 82: goto tr16; + case 83: goto tr17; + case 84: goto tr18; + case 85: goto tr19; + case 97: goto tr5; + case 99: goto tr6; + case 100: goto tr7; + case 101: goto tr8; + case 104: goto tr9; + case 105: goto tr10; + case 107: goto tr11; + case 108: goto tr12; + case 109: goto tr13; + case 110: goto tr14; + case 112: goto tr15; + case 114: goto tr16; + case 115: goto tr17; + case 116: goto tr18; + case 117: goto tr19; + case 778: goto tr89; + case 827: goto tr90; + case 1034: goto tr798; + case 1083: goto tr799; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr4; + goto tr0; +tr799: + { + s->buffer_length = 0; + } + goto st207; +tr800: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st207; +tr794: + { + s->r_owner_length = s->dname_tmp_length; + } + { + s->buffer_length = 0; + } + { + if (s->number64 <= UINT32_MAX) { + s->r_ttl = (uint32_t)(s->number64); + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st207; +st207: + if ( ++p == pe ) + goto _test_eof207; +case 207: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 778: goto tr92; + case 1034: goto tr801; + } + if ( _widec > 895 ) { + if ( 896 <= _widec && _widec <= 1151 ) + goto tr800; + } else if ( _widec >= 640 ) + goto tr91; + goto tr145; +tr3698: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 208;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 208;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 208;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 208;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 208;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 208;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 208;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 208;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 208;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 208;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 208;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 208;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 208;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 208;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 208;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 208;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 208;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 208;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 208;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 208;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 208;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 208;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 208;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 208;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 208;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 208;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 208;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 208;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 208;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + { + s->dname = s->r_owner; + s->r_owner_length = 0; + } + { p--; {stack[top++] = 208;goto st270;} } + goto st208; +st208: + if ( ++p == pe ) + goto _test_eof208; +case 208: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr803; + case 32: goto tr803; + case 40: goto tr804; + case 41: goto tr805; + case 65: goto st230; + case 70: goto st233; + case 80: goto st237; + case 97: goto st230; + case 102: goto st233; + case 112: goto st237; + case 1802: goto tr83; + case 1851: goto tr84; + case 2058: goto tr806; + case 2107: goto tr807; + case 2314: goto tr741; + case 2363: goto tr742; + case 2570: goto tr808; + case 2619: goto tr809; + } + goto tr802; +tr811: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st209; +tr812: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st209; +tr803: + { + s->r_owner_length = s->dname_tmp_length; + } + { s->r_type = KNOT_RRTYPE_A; } + { + rdata_tail = s->r_data; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st209; +tr804: + { + s->r_owner_length = s->dname_tmp_length; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + { s->r_type = KNOT_RRTYPE_A; } + { + rdata_tail = s->r_data; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st209; +tr805: + { + s->r_owner_length = s->dname_tmp_length; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + { s->r_type = KNOT_RRTYPE_A; } + { + rdata_tail = s->r_data; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st209; +st209: + if ( ++p == pe ) + goto _test_eof209; +case 209: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto st209; + case 32: goto st209; + case 40: goto tr811; + case 41: goto tr812; + case 58: goto tr69; + case 65: goto tr118; + case 67: goto tr119; + case 68: goto tr120; + case 69: goto tr121; + case 72: goto tr122; + case 73: goto tr123; + case 75: goto tr124; + case 76: goto tr125; + case 77: goto tr126; + case 78: goto tr127; + case 80: goto tr128; + case 82: goto tr129; + case 83: goto tr130; + case 84: goto tr131; + case 85: goto tr132; + case 92: goto tr74; + case 97: goto tr118; + case 99: goto tr119; + case 100: goto tr120; + case 101: goto tr121; + case 104: goto tr122; + case 105: goto tr123; + case 107: goto tr124; + case 108: goto tr125; + case 109: goto tr126; + case 110: goto tr127; + case 112: goto tr128; + case 114: goto tr129; + case 115: goto tr130; + case 116: goto tr131; + case 117: goto tr132; + case 1802: goto tr89; + case 1851: goto tr90; + case 2058: goto tr813; + case 2107: goto tr769; + case 2314: goto tr108; + case 2363: goto tr109; + case 2570: goto tr814; + case 2619: goto tr770; + } + if ( _widec < 11 ) { + if ( _widec <= 8 ) + goto tr69; + } else if ( _widec > 47 ) { + if ( _widec > 57 ) { + if ( 60 <= _widec ) + goto tr69; + } else if ( _widec >= 48 ) + goto tr117; + } else + goto tr69; + goto tr114; +tr137: + { + s->line_counter++; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 1136;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 1136;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 1136;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 1136;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 1136;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 1136;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 1136;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 1136;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 1136;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 1136;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 1136;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 1136;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 1136;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 1136;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 1136;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 1136;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 1136;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 1136;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 1136;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 1136;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 1136;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 1136;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 1136;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 1136;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 1136;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 1136;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 1136;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 1136;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 1136;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st1136; +tr814: + { + s->line_counter++; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 1136;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 1136;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 1136;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 1136;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 1136;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 1136;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 1136;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 1136;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 1136;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 1136;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 1136;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 1136;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 1136;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 1136;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 1136;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 1136;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 1136;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 1136;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 1136;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 1136;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 1136;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 1136;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 1136;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 1136;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 1136;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 1136;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 1136;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 1136;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 1136;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + { + if (rdata_tail - s->r_data > UINT16_MAX) { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + s->r_data_length = rdata_tail - s->r_data; + + s->state = ZS_STATE_DATA; + + // Execute the record callback. + if (s->process.automatic) { + if (s->process.record != NULL) { + s->process.record(s); + + // Stop if required from the callback. + if (s->state == ZS_STATE_STOP) { + {p++; cs = 1136; goto _out;} + } + } + } else { + // Return if external processing. + p--; {p++; cs = 1136; goto _out;} + } + } + goto st1136; +tr808: + { + s->r_owner_length = s->dname_tmp_length; + } + { + s->line_counter++; + } + { s->r_type = KNOT_RRTYPE_A; } + { + rdata_tail = s->r_data; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 1136;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 1136;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 1136;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 1136;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 1136;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 1136;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 1136;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 1136;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 1136;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 1136;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 1136;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 1136;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 1136;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 1136;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 1136;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 1136;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 1136;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 1136;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 1136;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 1136;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 1136;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 1136;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 1136;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 1136;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 1136;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 1136;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 1136;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 1136;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 1136;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + if (rdata_tail - s->r_data > UINT16_MAX) { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + s->r_data_length = rdata_tail - s->r_data; + + s->state = ZS_STATE_DATA; + + // Execute the record callback. + if (s->process.automatic) { + if (s->process.record != NULL) { + s->process.record(s); + + // Stop if required from the callback. + if (s->state == ZS_STATE_STOP) { + {p++; cs = 1136; goto _out;} + } + } + } else { + // Return if external processing. + p--; {p++; cs = 1136; goto _out;} + } + } + goto st1136; +tr3632: + { + s->line_counter++; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 1136;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 1136;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 1136;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 1136;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 1136;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 1136;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 1136;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 1136;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 1136;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 1136;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 1136;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 1136;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 1136;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 1136;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 1136;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 1136;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 1136;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 1136;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 1136;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 1136;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 1136;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 1136;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 1136;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 1136;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 1136;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 1136;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 1136;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 1136;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 1136;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + { + if (s->r_owner_length == 0) { + WARN(ZS_BAD_PREVIOUS_OWNER); + p--; {goto st268;} + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st1136; +st1136: + if ( ++p == pe ) + goto _test_eof1136; +case 1136: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr3691; + case 32: goto tr3691; + case 36: goto tr3623; + case 40: goto tr3692; + case 41: goto tr3693; + case 42: goto tr3626; + case 58: goto tr69; + case 65: goto tr3698; + case 67: goto tr3699; + case 68: goto tr3700; + case 69: goto tr3701; + case 72: goto tr3702; + case 73: goto tr3703; + case 75: goto tr3704; + case 76: goto tr3705; + case 77: goto tr3706; + case 78: goto tr3707; + case 80: goto tr3708; + case 82: goto tr3709; + case 83: goto tr3710; + case 84: goto tr3711; + case 85: goto tr3712; + case 92: goto tr3627; + case 95: goto tr3626; + case 97: goto tr3698; + case 99: goto tr3699; + case 100: goto tr3700; + case 101: goto tr3701; + case 104: goto tr3702; + case 105: goto tr3703; + case 107: goto tr3704; + case 108: goto tr3705; + case 109: goto tr3706; + case 110: goto tr3707; + case 112: goto tr3708; + case 114: goto tr3709; + case 115: goto tr3710; + case 116: goto tr3711; + case 117: goto tr3712; + case 1802: goto tr83; + case 1851: goto tr84; + case 2058: goto tr3713; + case 2107: goto tr3694; + case 2314: goto tr101; + case 2363: goto tr102; + case 2570: goto tr3713; + case 2619: goto tr3695; + } + if ( _widec < 60 ) { + if ( _widec < 11 ) { + if ( _widec <= 8 ) + goto tr69; + } else if ( _widec > 44 ) { + if ( _widec > 47 ) { + if ( 48 <= _widec && _widec <= 57 ) + goto tr3697; + } else if ( _widec >= 45 ) + goto tr3626; + } else + goto tr69; + } else if ( _widec > 63 ) { + if ( _widec < 91 ) { + if ( 64 <= _widec && _widec <= 90 ) + goto tr3626; + } else if ( _widec > 96 ) { + if ( _widec > 122 ) { + if ( 123 <= _widec ) + goto tr69; + } else if ( _widec >= 98 ) + goto tr3626; + } else + goto tr69; + } else + goto tr69; + goto tr3696; +tr3699: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 210;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 210;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 210;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 210;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 210;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 210;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 210;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 210;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 210;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 210;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 210;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 210;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 210;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 210;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 210;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 210;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 210;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 210;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 210;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 210;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 210;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 210;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 210;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 210;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 210;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 210;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 210;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 210;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 210;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + { + s->dname = s->r_owner; + s->r_owner_length = 0; + } + { p--; {stack[top++] = 210;goto st270;} } + goto st210; +st210: + if ( ++p == pe ) + goto _test_eof210; +case 210: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr784; + case 32: goto tr784; + case 40: goto tr785; + case 41: goto tr786; + case 65: goto st18; + case 68: goto st22; + case 69: goto st29; + case 78: goto st32; + case 97: goto st18; + case 100: goto st22; + case 101: goto st29; + case 110: goto st32; + case 778: goto tr83; + case 827: goto tr84; + case 1034: goto tr787; + case 1083: goto tr788; + } + goto tr802; +tr3700: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 211;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 211;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 211;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 211;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 211;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 211;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 211;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 211;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 211;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 211;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 211;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 211;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 211;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 211;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 211;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 211;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 211;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 211;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 211;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 211;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 211;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 211;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 211;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 211;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 211;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 211;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 211;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 211;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 211;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + { + s->dname = s->r_owner; + s->r_owner_length = 0; + } + { p--; {stack[top++] = 211;goto st270;} } + goto st211; +st211: + if ( ++p == pe ) + goto _test_eof211; +case 211: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr784; + case 32: goto tr784; + case 40: goto tr785; + case 41: goto tr786; + case 72: goto st37; + case 78: goto st41; + case 83: goto st49; + case 104: goto st37; + case 110: goto st41; + case 115: goto st49; + case 778: goto tr83; + case 827: goto tr84; + case 1034: goto tr787; + case 1083: goto tr788; + } + goto tr802; +tr3701: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 212;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 212;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 212;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 212;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 212;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 212;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 212;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 212;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 212;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 212;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 212;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 212;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 212;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 212;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 212;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 212;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 212;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 212;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 212;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 212;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 212;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 212;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 212;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 212;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 212;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 212;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 212;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 212;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 212;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + { + s->dname = s->r_owner; + s->r_owner_length = 0; + } + { p--; {stack[top++] = 212;goto st270;} } + goto st212; +st212: + if ( ++p == pe ) + goto _test_eof212; +case 212: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr784; + case 32: goto tr784; + case 40: goto tr785; + case 41: goto tr786; + case 85: goto st51; + case 117: goto st51; + case 778: goto tr83; + case 827: goto tr84; + case 1034: goto tr787; + case 1083: goto tr788; + } + goto tr802; +tr3702: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 213;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 213;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 213;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 213;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 213;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 213;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 213;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 213;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 213;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 213;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 213;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 213;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 213;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 213;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 213;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 213;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 213;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 213;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 213;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 213;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 213;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 213;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 213;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 213;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 213;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 213;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 213;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 213;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 213;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + { + s->dname = s->r_owner; + s->r_owner_length = 0; + } + { p--; {stack[top++] = 213;goto st270;} } + goto st213; +st213: + if ( ++p == pe ) + goto _test_eof213; +case 213: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr784; + case 32: goto tr784; + case 40: goto tr785; + case 41: goto tr786; + case 73: goto st58; + case 105: goto st58; + case 778: goto tr83; + case 827: goto tr84; + case 1034: goto tr787; + case 1083: goto tr788; + } + goto tr802; +tr3703: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 214;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 214;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 214;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 214;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 214;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 214;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 214;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 214;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 214;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 214;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 214;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 214;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 214;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 214;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 214;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 214;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 214;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 214;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 214;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 214;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 214;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 214;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 214;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 214;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 214;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 214;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 214;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 214;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 214;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + { + s->dname = s->r_owner; + s->r_owner_length = 0; + } + { p--; {stack[top++] = 214;goto st270;} } + goto st214; +st214: + if ( ++p == pe ) + goto _test_eof214; +case 214: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr784; + case 32: goto tr784; + case 40: goto tr785; + case 41: goto tr786; + case 78: goto st144; + case 80: goto st66; + case 110: goto st144; + case 112: goto st66; + case 778: goto tr83; + case 827: goto tr84; + case 1034: goto tr787; + case 1083: goto tr788; + } + goto tr802; +tr3704: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 215;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 215;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 215;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 215;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 215;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 215;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 215;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 215;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 215;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 215;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 215;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 215;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 215;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 215;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 215;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 215;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 215;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 215;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 215;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 215;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 215;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 215;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 215;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 215;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 215;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 215;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 215;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 215;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 215;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + { + s->dname = s->r_owner; + s->r_owner_length = 0; + } + { p--; {stack[top++] = 215;goto st270;} } + goto st215; +st215: + if ( ++p == pe ) + goto _test_eof215; +case 215: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr784; + case 32: goto tr784; + case 40: goto tr785; + case 41: goto tr786; + case 69: goto st74; + case 88: goto st76; + case 101: goto st74; + case 120: goto st76; + case 778: goto tr83; + case 827: goto tr84; + case 1034: goto tr787; + case 1083: goto tr788; + } + goto tr802; +tr3705: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 216;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 216;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 216;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 216;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 216;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 216;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 216;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 216;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 216;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 216;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 216;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 216;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 216;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 216;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 216;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 216;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 216;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 216;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 216;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 216;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 216;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 216;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 216;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 216;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 216;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 216;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 216;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 216;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 216;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + { + s->dname = s->r_owner; + s->r_owner_length = 0; + } + { p--; {stack[top++] = 216;goto st270;} } + goto st216; +st216: + if ( ++p == pe ) + goto _test_eof216; +case 216: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr784; + case 32: goto tr784; + case 40: goto tr785; + case 41: goto tr786; + case 51: goto st78; + case 54: goto st80; + case 79: goto st82; + case 80: goto st84; + case 111: goto st82; + case 112: goto st84; + case 778: goto tr83; + case 827: goto tr84; + case 1034: goto tr787; + case 1083: goto tr788; + } + goto tr802; +tr3706: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 217;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 217;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 217;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 217;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 217;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 217;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 217;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 217;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 217;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 217;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 217;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 217;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 217;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 217;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 217;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 217;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 217;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 217;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 217;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 217;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 217;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 217;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 217;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 217;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 217;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 217;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 217;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 217;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 217;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + { + s->dname = s->r_owner; + s->r_owner_length = 0; + } + { p--; {stack[top++] = 217;goto st270;} } + goto st217; +st217: + if ( ++p == pe ) + goto _test_eof217; +case 217: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr784; + case 32: goto tr784; + case 40: goto tr785; + case 41: goto tr786; + case 73: goto st86; + case 88: goto st90; + case 105: goto st86; + case 120: goto st90; + case 778: goto tr83; + case 827: goto tr84; + case 1034: goto tr787; + case 1083: goto tr788; + } + goto tr802; +tr3707: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 218;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 218;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 218;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 218;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 218;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 218;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 218;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 218;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 218;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 218;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 218;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 218;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 218;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 218;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 218;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 218;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 218;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 218;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 218;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 218;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 218;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 218;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 218;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 218;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 218;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 218;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 218;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 218;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 218;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + { + s->dname = s->r_owner; + s->r_owner_length = 0; + } + { p--; {stack[top++] = 218;goto st270;} } + goto st218; +st218: + if ( ++p == pe ) + goto _test_eof218; +case 218: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr784; + case 32: goto tr784; + case 40: goto tr785; + case 41: goto tr786; + case 65: goto st92; + case 73: goto st96; + case 83: goto st98; + case 97: goto st92; + case 105: goto st96; + case 115: goto st98; + case 778: goto tr83; + case 827: goto tr84; + case 1034: goto tr787; + case 1083: goto tr788; + } + goto tr802; +tr3708: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 219;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 219;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 219;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 219;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 219;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 219;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 219;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 219;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 219;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 219;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 219;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 219;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 219;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 219;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 219;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 219;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 219;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 219;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 219;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 219;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 219;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 219;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 219;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 219;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 219;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 219;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 219;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 219;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 219;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + { + s->dname = s->r_owner; + s->r_owner_length = 0; + } + { p--; {stack[top++] = 219;goto st270;} } + goto st219; +st219: + if ( ++p == pe ) + goto _test_eof219; +case 219: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr784; + case 32: goto tr784; + case 40: goto tr785; + case 41: goto tr786; + case 84: goto st108; + case 116: goto st108; + case 778: goto tr83; + case 827: goto tr84; + case 1034: goto tr787; + case 1083: goto tr788; + } + goto tr802; +tr3709: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 220;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 220;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 220;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 220;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 220;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 220;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 220;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 220;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 220;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 220;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 220;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 220;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 220;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 220;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 220;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 220;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 220;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 220;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 220;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 220;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 220;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 220;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 220;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 220;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 220;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 220;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 220;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 220;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 220;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + { + s->dname = s->r_owner; + s->r_owner_length = 0; + } + { p--; {stack[top++] = 220;goto st270;} } + goto st220; +st220: + if ( ++p == pe ) + goto _test_eof220; +case 220: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr784; + case 32: goto tr784; + case 40: goto tr785; + case 41: goto tr786; + case 80: goto st111; + case 82: goto st112; + case 84: goto st116; + case 112: goto st111; + case 114: goto st112; + case 116: goto st116; + case 778: goto tr83; + case 827: goto tr84; + case 1034: goto tr787; + case 1083: goto tr788; + } + goto tr802; +tr3710: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 221;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 221;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 221;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 221;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 221;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 221;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 221;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 221;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 221;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 221;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 221;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 221;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 221;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 221;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 221;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 221;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 221;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 221;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 221;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 221;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 221;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 221;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 221;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 221;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 221;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 221;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 221;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 221;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 221;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + { + s->dname = s->r_owner; + s->r_owner_length = 0; + } + { p--; {stack[top++] = 221;goto st270;} } + goto st221; +st221: + if ( ++p == pe ) + goto _test_eof221; +case 221: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr784; + case 32: goto tr784; + case 40: goto tr785; + case 41: goto tr786; + case 79: goto st118; + case 80: goto st120; + case 82: goto st122; + case 83: goto st124; + case 111: goto st118; + case 112: goto st120; + case 114: goto st122; + case 115: goto st124; + case 778: goto tr83; + case 827: goto tr84; + case 1034: goto tr787; + case 1083: goto tr788; + } + goto tr802; +tr3711: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 222;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 222;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 222;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 222;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 222;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 222;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 222;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 222;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 222;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 222;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 222;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 222;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 222;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 222;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 222;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 222;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 222;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 222;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 222;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 222;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 222;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 222;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 222;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 222;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 222;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 222;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 222;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 222;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 222;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + { + s->dname = s->r_owner; + s->r_owner_length = 0; + } + { p--; {stack[top++] = 222;goto st270;} } + goto st222; +st222: + if ( ++p == pe ) + goto _test_eof222; +case 222: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr784; + case 32: goto tr784; + case 40: goto tr785; + case 41: goto tr786; + case 76: goto st129; + case 88: goto st132; + case 89: goto st134; + case 108: goto st129; + case 120: goto st132; + case 121: goto st134; + case 778: goto tr83; + case 827: goto tr84; + case 1034: goto tr787; + case 1083: goto tr788; + } + goto tr802; +tr3712: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 223;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 223;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 223;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 223;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 223;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 223;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 223;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 223;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 223;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 223;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 223;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 223;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 223;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 223;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 223;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 223;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 223;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 223;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 223;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 223;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 223;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 223;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 223;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 223;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 223;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 223;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 223;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 223;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 223;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + { + s->dname = s->r_owner; + s->r_owner_length = 0; + } + { p--; {stack[top++] = 223;goto st270;} } + goto st223; +st223: + if ( ++p == pe ) + goto _test_eof223; +case 223: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr784; + case 32: goto tr784; + case 40: goto tr785; + case 41: goto tr786; + case 82: goto st139; + case 114: goto st139; + case 778: goto tr83; + case 827: goto tr84; + case 1034: goto tr787; + case 1083: goto tr788; + } + goto tr802; +tr3627: + { + if (pe - p == 1) { + *wrap = WRAP_DETECTED; + } + } + { + s->dname = s->r_owner; + s->r_owner_length = 0; + } + { p--; {stack[top++] = 224;goto st270;} } + goto st224; +st224: + if ( ++p == pe ) + goto _test_eof224; +case 224: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr815; + case 32: goto tr815; + case 35: goto tr94; + case 40: goto tr817; + case 41: goto tr818; + case 778: goto tr93; + case 827: goto tr93; + case 1034: goto tr819; + case 1083: goto tr820; + } + if ( _widec < 11 ) { + if ( _widec <= 8 ) + goto tr93; + } else if ( _widec > 58 ) { + if ( 60 <= _widec ) + goto tr93; + } else + goto tr93; + goto tr816; +tr815: + { + s->r_owner_length = s->dname_tmp_length; + } + { + if (*wrap == WRAP_NONE) { + p--; + } + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 225;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 225;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 225;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 225;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 225;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 225;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 225;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 225;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 225;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 225;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 225;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 225;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 225;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 225;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 225;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 225;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 225;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 225;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 225;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 225;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 225;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 225;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 225;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 225;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 225;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 225;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 225;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 225;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 225;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st225; +tr817: + { + s->r_owner_length = s->dname_tmp_length; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + { + if (*wrap == WRAP_NONE) { + p--; + } + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 225;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 225;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 225;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 225;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 225;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 225;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 225;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 225;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 225;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 225;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 225;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 225;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 225;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 225;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 225;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 225;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 225;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 225;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 225;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 225;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 225;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 225;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 225;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 225;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 225;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 225;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 225;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 225;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 225;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st225; +tr818: + { + s->r_owner_length = s->dname_tmp_length; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + { + if (*wrap == WRAP_NONE) { + p--; + } + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 225;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 225;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 225;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 225;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 225;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 225;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 225;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 225;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 225;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 225;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 225;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 225;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 225;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 225;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 225;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 225;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 225;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 225;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 225;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 225;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 225;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 225;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 225;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 225;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 225;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 225;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 225;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 225;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 225;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st225; +tr819: + { + s->r_owner_length = s->dname_tmp_length; + } + { + s->line_counter++; + } + { + if (*wrap == WRAP_NONE) { + p--; + } + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 225;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 225;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 225;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 225;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 225;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 225;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 225;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 225;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 225;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 225;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 225;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 225;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 225;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 225;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 225;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 225;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 225;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 225;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 225;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 225;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 225;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 225;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 225;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 225;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 225;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 225;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 225;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 225;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 225;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st225; +st225: + if ( ++p == pe ) + goto _test_eof225; +case 225: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr821; + case 32: goto tr821; + case 40: goto tr822; + case 41: goto tr823; + case 65: goto tr5; + case 67: goto tr6; + case 68: goto tr7; + case 69: goto tr8; + case 72: goto tr9; + case 73: goto tr10; + case 75: goto tr11; + case 76: goto tr12; + case 77: goto tr13; + case 78: goto tr14; + case 80: goto tr15; + case 82: goto tr16; + case 83: goto tr17; + case 84: goto tr18; + case 85: goto tr19; + case 97: goto tr5; + case 99: goto tr6; + case 100: goto tr7; + case 101: goto tr8; + case 104: goto tr9; + case 105: goto tr10; + case 107: goto tr11; + case 108: goto tr12; + case 109: goto tr13; + case 110: goto tr14; + case 112: goto tr15; + case 114: goto tr16; + case 115: goto tr17; + case 116: goto tr18; + case 117: goto tr19; + case 778: goto tr83; + case 827: goto tr84; + case 1034: goto tr824; + case 1083: goto tr825; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr4; + goto tr114; +tr820: + { + s->r_owner_length = s->dname_tmp_length; + } + { + s->buffer_length = 0; + } + { + if (*wrap == WRAP_NONE) { + p--; + } + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 226;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 226;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 226;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 226;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 226;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 226;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 226;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 226;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 226;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 226;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 226;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 226;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 226;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 226;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 226;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 226;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 226;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 226;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 226;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 226;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 226;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 226;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 226;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 226;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 226;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 226;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 226;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 226;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 226;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st226; +st226: + if ( ++p == pe ) + goto _test_eof226; +case 226: + _widec = (*p); + if ( (*p) < 11 ) { + if ( (*p) > 9 ) { + if ( 10 <= (*p) && (*p) <= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 58 ) { + if ( (*p) > 59 ) { + if ( 60 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 777: goto tr80; + case 778: goto tr83; + case 800: goto tr80; + case 808: goto tr81; + case 809: goto tr82; + case 827: goto tr84; + case 1033: goto tr826; + case 1034: goto tr827; + case 1056: goto tr826; + case 1064: goto tr828; + case 1065: goto tr829; + case 1083: goto tr830; + } + if ( 896 <= _widec && _widec <= 1151 ) + goto tr689; + goto tr79; +tr831: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st227; +tr826: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st227; +tr828: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st227; +tr829: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st227; +tr832: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st227; +tr833: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st227; +st227: + if ( ++p == pe ) + goto _test_eof227; +case 227: + _widec = (*p); + if ( (*p) < 11 ) { + if ( (*p) > 9 ) { + if ( 10 <= (*p) && (*p) <= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 58 ) { + if ( (*p) > 59 ) { + if ( 60 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 777: goto st7; + case 778: goto tr89; + case 800: goto st7; + case 808: goto tr87; + case 809: goto tr88; + case 827: goto tr90; + case 1033: goto tr831; + case 1034: goto tr756; + case 1056: goto tr831; + case 1064: goto tr832; + case 1065: goto tr833; + case 1083: goto tr834; + } + if ( 896 <= _widec && _widec <= 1151 ) + goto tr689; + goto tr85; +tr770: + { + s->buffer_length = 0; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 228;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 228;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 228;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 228;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 228;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 228;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 228;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 228;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 228;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 228;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 228;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 228;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 228;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 228;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 228;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 228;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 228;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 228;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 228;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 228;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 228;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 228;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 228;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 228;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 228;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 228;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 228;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 228;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 228;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st228; +tr901: + { + s->buffer_length = 0; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 228;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 228;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 228;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 228;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 228;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 228;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 228;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 228;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 228;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 228;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 228;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 228;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 228;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 228;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 228;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 228;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 228;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 228;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 228;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 228;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 228;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 228;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 228;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 228;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 228;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 228;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 228;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 228;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 228;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st228; +tr809: + { + s->r_owner_length = s->dname_tmp_length; + } + { + s->buffer_length = 0; + } + { s->r_type = KNOT_RRTYPE_A; } + { + rdata_tail = s->r_data; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 228;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 228;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 228;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 228;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 228;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 228;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 228;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 228;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 228;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 228;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 228;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 228;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 228;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 228;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 228;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 228;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 228;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 228;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 228;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 228;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 228;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 228;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 228;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 228;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 228;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 228;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 228;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 228;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 228;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st228; +tr3695: + { + s->buffer_length = 0; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 228;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 228;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 228;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 228;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 228;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 228;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 228;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 228;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 228;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 228;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 228;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 228;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 228;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 228;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 228;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 228;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 228;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 228;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 228;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 228;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 228;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 228;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 228;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 228;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 228;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 228;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 228;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 228;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 228;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + if (s->r_owner_length == 0) { + WARN(ZS_BAD_PREVIOUS_OWNER); + p--; {goto st268;} + } + } + goto st228; +st228: + if ( ++p == pe ) + goto _test_eof228; +case 228: + _widec = (*p); + if ( (*p) < 11 ) { + if ( (*p) > 9 ) { + if ( 10 <= (*p) && (*p) <= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 58 ) { + if ( (*p) > 59 ) { + if ( 60 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 777: goto tr757; + case 778: goto tr760; + case 800: goto tr757; + case 808: goto tr758; + case 809: goto tr759; + case 827: goto tr761; + case 1033: goto tr835; + case 1034: goto tr827; + case 1056: goto tr835; + case 1064: goto tr836; + case 1065: goto tr837; + case 1083: goto tr838; + } + if ( _widec > 895 ) { + if ( 896 <= _widec && _widec <= 1151 ) + goto tr771; + } else if ( _widec >= 640 ) + goto tr91; + goto tr79; +tr839: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st229; +tr835: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st229; +tr836: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st229; +tr837: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st229; +tr840: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st229; +tr841: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st229; +st229: + if ( ++p == pe ) + goto _test_eof229; +case 229: + _widec = (*p); + if ( (*p) < 11 ) { + if ( (*p) > 9 ) { + if ( 10 <= (*p) && (*p) <= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 58 ) { + if ( (*p) > 59 ) { + if ( 60 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 777: goto tr762; + case 778: goto tr92; + case 800: goto tr762; + case 808: goto tr763; + case 809: goto tr764; + case 827: goto tr765; + case 1033: goto tr839; + case 1034: goto tr756; + case 1056: goto tr839; + case 1064: goto tr840; + case 1065: goto tr841; + case 1083: goto tr842; + } + if ( _widec > 895 ) { + if ( 896 <= _widec && _widec <= 1151 ) + goto tr771; + } else if ( _widec >= 640 ) + goto tr91; + goto tr79; +st230: + if ( ++p == pe ) + goto _test_eof230; +case 230: + switch( (*p) ) { + case 65: goto st231; + case 97: goto st231; + } + goto tr36; +st231: + if ( ++p == pe ) + goto _test_eof231; +case 231: + switch( (*p) ) { + case 65: goto st232; + case 97: goto st232; + } + goto tr36; +st232: + if ( ++p == pe ) + goto _test_eof232; +case 232: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr845; + case 32: goto tr845; + case 40: goto tr846; + case 41: goto tr847; + case 2058: goto tr848; + case 2107: goto tr849; + case 2314: goto tr850; + case 2363: goto tr850; + case 2570: goto tr851; + case 2619: goto tr852; + } + goto tr57; +st233: + if ( ++p == pe ) + goto _test_eof233; +case 233: + switch( (*p) ) { + case 83: goto st234; + case 115: goto st234; + } + goto tr36; +st234: + if ( ++p == pe ) + goto _test_eof234; +case 234: + switch( (*p) ) { + case 68: goto st235; + case 100: goto st235; + } + goto tr36; +st235: + if ( ++p == pe ) + goto _test_eof235; +case 235: + switch( (*p) ) { + case 66: goto st236; + case 98: goto st236; + } + goto tr36; +st236: + if ( ++p == pe ) + goto _test_eof236; +case 236: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr856; + case 32: goto tr856; + case 40: goto tr857; + case 41: goto tr858; + case 2058: goto tr859; + case 2107: goto tr860; + case 2314: goto tr861; + case 2363: goto tr861; + case 2570: goto tr862; + case 2619: goto tr863; + } + goto tr57; +st237: + if ( ++p == pe ) + goto _test_eof237; +case 237: + switch( (*p) ) { + case 76: goto st238; + case 108: goto st238; + } + goto tr36; +st238: + if ( ++p == pe ) + goto _test_eof238; +case 238: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr865; + case 32: goto tr865; + case 40: goto tr866; + case 41: goto tr867; + case 2058: goto tr868; + case 2107: goto tr869; + case 2314: goto tr870; + case 2363: goto tr870; + case 2570: goto tr871; + case 2619: goto tr872; + } + goto tr57; +tr134: + { + s->buffer_length = 0; + } + goto st239; +tr873: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st239; +tr3629: + { + s->buffer_length = 0; + } + { + if (s->r_owner_length == 0) { + WARN(ZS_BAD_PREVIOUS_OWNER); + p--; {goto st268;} + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st239; +st239: + if ( ++p == pe ) + goto _test_eof239; +case 239: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 778: goto tr666; + case 1034: goto tr874; + } + if ( _widec > 895 ) { + if ( 896 <= _widec && _widec <= 1151 ) + goto tr873; + } else if ( _widec >= 640 ) + goto tr665; + goto tr79; +tr3631: + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 240;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 240;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 240;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 240;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 240;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 240;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 240;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 240;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 240;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 240;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 240;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 240;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 240;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 240;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 240;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 240;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 240;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 240;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 240;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 240;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 240;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 240;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 240;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 240;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 240;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 240;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 240;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 240;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 240;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + s->buffer_length = 0; + } + goto st240; +tr136: + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 240;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 240;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 240;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 240;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 240;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 240;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 240;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 240;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 240;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 240;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 240;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 240;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 240;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 240;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 240;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 240;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 240;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 240;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 240;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 240;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 240;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 240;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 240;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 240;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 240;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 240;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 240;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 240;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 240;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st240; +st240: + if ( ++p == pe ) + goto _test_eof240; +case 240: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr875; + case 32: goto tr875; + case 40: goto tr876; + case 41: goto tr877; + case 778: goto tr878; + case 827: goto tr761; + case 1034: goto tr878; + case 1083: goto tr761; + } + if ( _widec < 11 ) { + if ( _widec <= 8 ) + goto tr665; + } else if ( _widec > 58 ) { + if ( 60 <= _widec ) + goto tr665; + } else + goto tr665; + goto tr79; +tr879: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st241; +tr875: + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st241; +tr876: + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st241; +tr877: + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st241; +tr880: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st241; +tr881: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st241; +st241: + if ( ++p == pe ) + goto _test_eof241; +case 241: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr879; + case 32: goto tr879; + case 40: goto tr880; + case 41: goto tr881; + case 778: goto tr882; + case 827: goto tr765; + case 1034: goto tr882; + case 1083: goto tr765; + } + if ( _widec < 11 ) { + if ( _widec <= 8 ) + goto tr665; + } else if ( _widec > 58 ) { + if ( 60 <= _widec ) + goto tr665; + } else + goto tr665; + goto tr85; +tr138: + { + s->buffer_length = 0; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 242;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 242;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 242;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 242;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 242;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 242;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 242;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 242;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 242;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 242;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 242;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 242;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 242;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 242;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 242;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 242;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 242;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 242;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 242;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 242;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 242;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 242;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 242;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 242;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 242;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 242;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 242;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 242;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 242;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st242; +tr3633: + { + s->buffer_length = 0; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 242;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 242;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 242;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 242;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 242;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 242;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 242;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 242;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 242;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 242;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 242;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 242;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 242;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 242;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 242;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 242;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 242;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 242;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 242;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 242;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 242;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 242;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 242;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 242;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 242;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 242;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 242;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 242;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 242;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + { + if (s->r_owner_length == 0) { + WARN(ZS_BAD_PREVIOUS_OWNER); + p--; {goto st268;} + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st242; +st242: + if ( ++p == pe ) + goto _test_eof242; +case 242: + _widec = (*p); + if ( (*p) < 11 ) { + if ( (*p) > 9 ) { + if ( 10 <= (*p) && (*p) <= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 58 ) { + if ( (*p) > 59 ) { + if ( 60 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 777: goto tr875; + case 778: goto tr878; + case 800: goto tr875; + case 808: goto tr876; + case 809: goto tr877; + case 827: goto tr761; + case 1033: goto tr883; + case 1034: goto tr827; + case 1056: goto tr883; + case 1064: goto tr884; + case 1065: goto tr885; + case 1083: goto tr838; + } + if ( _widec > 895 ) { + if ( 896 <= _widec && _widec <= 1151 ) + goto tr873; + } else if ( _widec >= 640 ) + goto tr665; + goto tr79; +tr886: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st243; +tr883: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st243; +tr884: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st243; +tr885: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st243; +tr887: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st243; +tr888: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st243; +st243: + if ( ++p == pe ) + goto _test_eof243; +case 243: + _widec = (*p); + if ( (*p) < 11 ) { + if ( (*p) > 9 ) { + if ( 10 <= (*p) && (*p) <= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 58 ) { + if ( (*p) > 59 ) { + if ( 60 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 777: goto tr879; + case 778: goto tr882; + case 800: goto tr879; + case 808: goto tr880; + case 809: goto tr881; + case 827: goto tr765; + case 1033: goto tr886; + case 1034: goto tr756; + case 1056: goto tr886; + case 1064: goto tr887; + case 1065: goto tr888; + case 1083: goto tr842; + } + if ( _widec > 895 ) { + if ( 896 <= _widec && _widec <= 1151 ) + goto tr873; + } else if ( _widec >= 640 ) + goto tr665; + goto tr79; +tr110: + { + s->buffer_length = 0; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 244;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 244;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 244;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 244;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 244;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 244;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 244;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 244;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 244;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 244;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 244;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 244;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 244;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 244;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 244;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 244;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 244;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 244;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 244;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 244;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 244;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 244;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 244;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 244;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 244;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 244;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 244;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 244;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 244;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st244; +tr103: + { + s->buffer_length = 0; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 244;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 244;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 244;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 244;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 244;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 244;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 244;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 244;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 244;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 244;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 244;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 244;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 244;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 244;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 244;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 244;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 244;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 244;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 244;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 244;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 244;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 244;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 244;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 244;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 244;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 244;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 244;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 244;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 244;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st244; +tr744: + { s->r_type = KNOT_RRTYPE_A; } + { + rdata_tail = s->r_data; + } + { + s->buffer_length = 0; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 244;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 244;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 244;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 244;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 244;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 244;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 244;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 244;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 244;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 244;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 244;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 244;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 244;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 244;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 244;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 244;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 244;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 244;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 244;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 244;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 244;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 244;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 244;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 244;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 244;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 244;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 244;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 244;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 244;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st244; +st244: + if ( ++p == pe ) + goto _test_eof244; +case 244: + _widec = (*p); + if ( (*p) < 11 ) { + if ( (*p) > 9 ) { + if ( 10 <= (*p) && (*p) <= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 58 ) { + if ( (*p) > 59 ) { + if ( 60 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 777: goto tr757; + case 778: goto tr760; + case 800: goto tr757; + case 808: goto tr758; + case 809: goto tr759; + case 827: goto tr761; + case 1033: goto tr889; + case 1034: goto tr760; + case 1056: goto tr889; + case 1064: goto tr890; + case 1065: goto tr891; + case 1083: goto tr167; + } + if ( _widec > 895 ) { + if ( 896 <= _widec && _widec <= 1151 ) + goto tr111; + } else if ( _widec >= 640 ) + goto tr91; + goto tr79; +tr892: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st245; +tr889: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st245; +tr890: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st245; +tr891: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st245; +tr893: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st245; +tr894: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st245; +st245: + if ( ++p == pe ) + goto _test_eof245; +case 245: + _widec = (*p); + if ( (*p) < 11 ) { + if ( (*p) > 9 ) { + if ( 10 <= (*p) && (*p) <= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 58 ) { + if ( (*p) > 59 ) { + if ( 60 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 777: goto tr762; + case 778: goto tr92; + case 800: goto tr762; + case 808: goto tr763; + case 809: goto tr764; + case 827: goto tr765; + case 1033: goto tr892; + case 1034: goto tr92; + case 1056: goto tr892; + case 1064: goto tr893; + case 1065: goto tr894; + case 1083: goto tr171; + } + if ( _widec > 895 ) { + if ( 896 <= _widec && _widec <= 1151 ) + goto tr111; + } else if ( _widec >= 640 ) + goto tr91; + goto tr79; +tr732: + { + s->buffer_length = 0; + } + goto st246; +tr895: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st246; +tr725: + { + s->r_owner_length = s->dname_tmp_length; + } + { + s->buffer_length = 0; + } + { s->r_type = KNOT_RRTYPE_A; } + { + rdata_tail = s->r_data; + } + goto st246; +st246: + if ( ++p == pe ) + goto _test_eof246; +case 246: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr896; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr895; + goto tr71; +tr733: + { + s->line_counter++; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 247;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 247;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 247;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 247;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 247;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 247;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 247;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 247;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 247;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 247;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 247;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 247;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 247;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 247;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 247;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 247;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 247;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 247;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 247;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 247;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 247;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 247;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 247;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 247;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 247;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 247;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 247;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 247;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 247;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st247; +tr726: + { + s->r_owner_length = s->dname_tmp_length; + } + { + s->line_counter++; + } + { s->r_type = KNOT_RRTYPE_A; } + { + rdata_tail = s->r_data; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 247;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 247;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 247;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 247;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 247;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 247;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 247;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 247;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 247;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 247;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 247;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 247;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 247;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 247;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 247;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 247;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 247;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 247;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 247;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 247;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 247;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 247;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 247;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 247;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 247;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 247;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 247;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 247;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 247;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st247; +st247: + if ( ++p == pe ) + goto _test_eof247; +case 247: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr897; + case 32: goto tr897; + case 40: goto tr898; + case 41: goto tr899; + case 58: goto tr69; + case 65: goto tr118; + case 67: goto tr119; + case 68: goto tr120; + case 69: goto tr121; + case 72: goto tr122; + case 73: goto tr123; + case 75: goto tr124; + case 76: goto tr125; + case 77: goto tr126; + case 78: goto tr127; + case 80: goto tr128; + case 82: goto tr129; + case 83: goto tr130; + case 84: goto tr131; + case 85: goto tr132; + case 92: goto tr74; + case 97: goto tr118; + case 99: goto tr119; + case 100: goto tr120; + case 101: goto tr121; + case 104: goto tr122; + case 105: goto tr123; + case 107: goto tr124; + case 108: goto tr125; + case 109: goto tr126; + case 110: goto tr127; + case 112: goto tr128; + case 114: goto tr129; + case 115: goto tr130; + case 116: goto tr131; + case 117: goto tr132; + case 1802: goto tr83; + case 1851: goto tr84; + case 2058: goto tr824; + case 2107: goto tr900; + case 2314: goto tr101; + case 2363: goto tr102; + case 2570: goto tr824; + case 2619: goto tr901; + } + if ( _widec < 11 ) { + if ( _widec <= 8 ) + goto tr69; + } else if ( _widec > 47 ) { + if ( _widec > 57 ) { + if ( 60 <= _widec ) + goto tr69; + } else if ( _widec >= 48 ) + goto tr117; + } else + goto tr69; + goto tr114; +tr734: + { + s->buffer_length = 0; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 248;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 248;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 248;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 248;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 248;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 248;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 248;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 248;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 248;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 248;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 248;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 248;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 248;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 248;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 248;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 248;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 248;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 248;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 248;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 248;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 248;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 248;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 248;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 248;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 248;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 248;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 248;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 248;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 248;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st248; +tr727: + { + s->r_owner_length = s->dname_tmp_length; + } + { + s->buffer_length = 0; + } + { s->r_type = KNOT_RRTYPE_A; } + { + rdata_tail = s->r_data; + } + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = 248;goto st632;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = 248;goto st634;} + case KNOT_RRTYPE_SOA: + {stack[top++] = 248;goto st636;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = 248;goto st668;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = 248;goto st673;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = 248;goto st678;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = 248;goto st683;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = 248;goto st687;} + case KNOT_RRTYPE_LOC: + {stack[top++] = 248;goto st689;} + case KNOT_RRTYPE_SRV: + {stack[top++] = 248;goto st744;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = 248;goto st755;} + case KNOT_RRTYPE_CERT: + {stack[top++] = 248;goto st772;} + case KNOT_RRTYPE_APL: + {stack[top++] = 248;goto st783;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = 248;goto st794;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = 248;goto st807;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = 248;goto st817;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = 248;goto st856;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = 248;goto st1010;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = 248;goto st1013;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = 248;goto st1024;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = 248;goto st1026;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = 248;goto st1055;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = 248;goto st1068;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = 248;goto st1086;} + case KNOT_RRTYPE_L32: + {stack[top++] = 248;goto st1081;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = 248;goto st1099;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = 248;goto st1105;} + case KNOT_RRTYPE_URI: + {stack[top++] = 248;goto st1111;} + case KNOT_RRTYPE_CAA: + {stack[top++] = 248;goto st1119;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {goto st268;} + } + } + goto st248; +st248: + if ( ++p == pe ) + goto _test_eof248; +case 248: + _widec = (*p); + if ( (*p) < 11 ) { + if ( (*p) > 9 ) { + if ( 10 <= (*p) && (*p) <= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 58 ) { + if ( (*p) > 59 ) { + if ( 60 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 777: goto tr80; + case 778: goto tr83; + case 800: goto tr80; + case 808: goto tr81; + case 809: goto tr82; + case 827: goto tr84; + case 1033: goto tr902; + case 1034: goto tr827; + case 1056: goto tr902; + case 1064: goto tr903; + case 1065: goto tr904; + case 1083: goto tr838; + } + if ( 896 <= _widec && _widec <= 1151 ) + goto tr895; + goto tr79; +tr905: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st249; +tr902: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st249; +tr903: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st249; +tr904: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st249; +tr906: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st249; +tr907: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st249; +st249: + if ( ++p == pe ) + goto _test_eof249; +case 249: + _widec = (*p); + if ( (*p) < 11 ) { + if ( (*p) > 9 ) { + if ( 10 <= (*p) && (*p) <= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 58 ) { + if ( (*p) > 59 ) { + if ( 60 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 777: goto st7; + case 778: goto tr89; + case 800: goto st7; + case 808: goto tr87; + case 809: goto tr88; + case 827: goto tr90; + case 1033: goto tr905; + case 1034: goto tr756; + case 1056: goto tr905; + case 1064: goto tr906; + case 1065: goto tr907; + case 1083: goto tr842; + } + if ( 896 <= _widec && _widec <= 1151 ) + goto tr895; + goto tr79; +tr3639: + { + s->r_class = s->default_class; + } + { + s->dname = s->r_owner; + s->r_owner_length = 0; + } + { p--; {stack[top++] = 250;goto st270;} } + goto st250; +tr3658: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + { + s->dname = s->r_owner; + s->r_owner_length = 0; + } + { p--; {stack[top++] = 250;goto st270;} } + goto st250; +st250: + if ( ++p == pe ) + goto _test_eof250; +case 250: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr679; + case 32: goto tr679; + case 40: goto tr680; + case 41: goto tr681; + case 65: goto st18; + case 68: goto st22; + case 69: goto st29; + case 78: goto st32; + case 97: goto st18; + case 100: goto st22; + case 101: goto st29; + case 110: goto st32; + case 1034: goto tr682; + case 1083: goto tr683; + } + goto tr908; +tr3640: + { + s->r_class = s->default_class; + } + { + s->dname = s->r_owner; + s->r_owner_length = 0; + } + { p--; {stack[top++] = 251;goto st270;} } + goto st251; +tr3659: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + { + s->dname = s->r_owner; + s->r_owner_length = 0; + } + { p--; {stack[top++] = 251;goto st270;} } + goto st251; +st251: + if ( ++p == pe ) + goto _test_eof251; +case 251: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr679; + case 32: goto tr679; + case 40: goto tr680; + case 41: goto tr681; + case 72: goto st37; + case 78: goto st41; + case 83: goto st49; + case 104: goto st37; + case 110: goto st41; + case 115: goto st49; + case 1034: goto tr682; + case 1083: goto tr683; + } + goto tr908; +tr3641: + { + s->r_class = s->default_class; + } + { + s->dname = s->r_owner; + s->r_owner_length = 0; + } + { p--; {stack[top++] = 252;goto st270;} } + goto st252; +tr3660: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + { + s->dname = s->r_owner; + s->r_owner_length = 0; + } + { p--; {stack[top++] = 252;goto st270;} } + goto st252; +st252: + if ( ++p == pe ) + goto _test_eof252; +case 252: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr679; + case 32: goto tr679; + case 40: goto tr680; + case 41: goto tr681; + case 85: goto st51; + case 117: goto st51; + case 1034: goto tr682; + case 1083: goto tr683; + } + goto tr908; +tr3642: + { + s->r_class = s->default_class; + } + { + s->dname = s->r_owner; + s->r_owner_length = 0; + } + { p--; {stack[top++] = 253;goto st270;} } + goto st253; +tr3661: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + { + s->dname = s->r_owner; + s->r_owner_length = 0; + } + { p--; {stack[top++] = 253;goto st270;} } + goto st253; +st253: + if ( ++p == pe ) + goto _test_eof253; +case 253: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr679; + case 32: goto tr679; + case 40: goto tr680; + case 41: goto tr681; + case 73: goto st58; + case 105: goto st58; + case 1034: goto tr682; + case 1083: goto tr683; + } + goto tr908; +tr3662: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + { + s->dname = s->r_owner; + s->r_owner_length = 0; + } + { p--; {stack[top++] = 254;goto st270;} } + goto st254; +st254: + if ( ++p == pe ) + goto _test_eof254; +case 254: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr679; + case 32: goto tr679; + case 40: goto tr680; + case 41: goto tr681; + case 78: goto st144; + case 80: goto st66; + case 110: goto st144; + case 112: goto st66; + case 1034: goto tr682; + case 1083: goto tr683; + } + goto tr908; +tr3644: + { + s->r_class = s->default_class; + } + { + s->dname = s->r_owner; + s->r_owner_length = 0; + } + { p--; {stack[top++] = 255;goto st270;} } + goto st255; +tr3663: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + { + s->dname = s->r_owner; + s->r_owner_length = 0; + } + { p--; {stack[top++] = 255;goto st270;} } + goto st255; +st255: + if ( ++p == pe ) + goto _test_eof255; +case 255: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr679; + case 32: goto tr679; + case 40: goto tr680; + case 41: goto tr681; + case 69: goto st74; + case 88: goto st76; + case 101: goto st74; + case 120: goto st76; + case 1034: goto tr682; + case 1083: goto tr683; + } + goto tr908; +tr3645: + { + s->r_class = s->default_class; + } + { + s->dname = s->r_owner; + s->r_owner_length = 0; + } + { p--; {stack[top++] = 256;goto st270;} } + goto st256; +tr3664: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + { + s->dname = s->r_owner; + s->r_owner_length = 0; + } + { p--; {stack[top++] = 256;goto st270;} } + goto st256; +st256: + if ( ++p == pe ) + goto _test_eof256; +case 256: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr679; + case 32: goto tr679; + case 40: goto tr680; + case 41: goto tr681; + case 51: goto st78; + case 54: goto st80; + case 79: goto st82; + case 80: goto st84; + case 111: goto st82; + case 112: goto st84; + case 1034: goto tr682; + case 1083: goto tr683; + } + goto tr908; +tr3646: + { + s->r_class = s->default_class; + } + { + s->dname = s->r_owner; + s->r_owner_length = 0; + } + { p--; {stack[top++] = 257;goto st270;} } + goto st257; +tr3665: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + { + s->dname = s->r_owner; + s->r_owner_length = 0; + } + { p--; {stack[top++] = 257;goto st270;} } + goto st257; +st257: + if ( ++p == pe ) + goto _test_eof257; +case 257: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr679; + case 32: goto tr679; + case 40: goto tr680; + case 41: goto tr681; + case 73: goto st86; + case 88: goto st90; + case 105: goto st86; + case 120: goto st90; + case 1034: goto tr682; + case 1083: goto tr683; + } + goto tr908; +tr3647: + { + s->r_class = s->default_class; + } + { + s->dname = s->r_owner; + s->r_owner_length = 0; + } + { p--; {stack[top++] = 258;goto st270;} } + goto st258; +tr3666: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + { + s->dname = s->r_owner; + s->r_owner_length = 0; + } + { p--; {stack[top++] = 258;goto st270;} } + goto st258; +st258: + if ( ++p == pe ) + goto _test_eof258; +case 258: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr679; + case 32: goto tr679; + case 40: goto tr680; + case 41: goto tr681; + case 65: goto st92; + case 73: goto st96; + case 83: goto st98; + case 97: goto st92; + case 105: goto st96; + case 115: goto st98; + case 1034: goto tr682; + case 1083: goto tr683; + } + goto tr908; +tr3648: + { + s->r_class = s->default_class; + } + { + s->dname = s->r_owner; + s->r_owner_length = 0; + } + { p--; {stack[top++] = 259;goto st270;} } + goto st259; +tr3667: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + { + s->dname = s->r_owner; + s->r_owner_length = 0; + } + { p--; {stack[top++] = 259;goto st270;} } + goto st259; +st259: + if ( ++p == pe ) + goto _test_eof259; +case 259: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr679; + case 32: goto tr679; + case 40: goto tr680; + case 41: goto tr681; + case 84: goto st108; + case 116: goto st108; + case 1034: goto tr682; + case 1083: goto tr683; + } + goto tr908; +tr3649: + { + s->r_class = s->default_class; + } + { + s->dname = s->r_owner; + s->r_owner_length = 0; + } + { p--; {stack[top++] = 260;goto st270;} } + goto st260; +tr3668: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + { + s->dname = s->r_owner; + s->r_owner_length = 0; + } + { p--; {stack[top++] = 260;goto st270;} } + goto st260; +st260: + if ( ++p == pe ) + goto _test_eof260; +case 260: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr679; + case 32: goto tr679; + case 40: goto tr680; + case 41: goto tr681; + case 80: goto st111; + case 82: goto st112; + case 84: goto st116; + case 112: goto st111; + case 114: goto st112; + case 116: goto st116; + case 1034: goto tr682; + case 1083: goto tr683; + } + goto tr908; +tr3650: + { + s->r_class = s->default_class; + } + { + s->dname = s->r_owner; + s->r_owner_length = 0; + } + { p--; {stack[top++] = 261;goto st270;} } + goto st261; +tr3669: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + { + s->dname = s->r_owner; + s->r_owner_length = 0; + } + { p--; {stack[top++] = 261;goto st270;} } + goto st261; +st261: + if ( ++p == pe ) + goto _test_eof261; +case 261: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr679; + case 32: goto tr679; + case 40: goto tr680; + case 41: goto tr681; + case 79: goto st118; + case 80: goto st120; + case 82: goto st122; + case 83: goto st124; + case 111: goto st118; + case 112: goto st120; + case 114: goto st122; + case 115: goto st124; + case 1034: goto tr682; + case 1083: goto tr683; + } + goto tr908; +tr3651: + { + s->r_class = s->default_class; + } + { + s->dname = s->r_owner; + s->r_owner_length = 0; + } + { p--; {stack[top++] = 262;goto st270;} } + goto st262; +tr3670: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + { + s->dname = s->r_owner; + s->r_owner_length = 0; + } + { p--; {stack[top++] = 262;goto st270;} } + goto st262; +st262: + if ( ++p == pe ) + goto _test_eof262; +case 262: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr679; + case 32: goto tr679; + case 40: goto tr680; + case 41: goto tr681; + case 76: goto st129; + case 88: goto st132; + case 89: goto st134; + case 108: goto st129; + case 120: goto st132; + case 121: goto st134; + case 1034: goto tr682; + case 1083: goto tr683; + } + goto tr908; +tr3652: + { + s->r_class = s->default_class; + } + { + s->dname = s->r_owner; + s->r_owner_length = 0; + } + { p--; {stack[top++] = 263;goto st270;} } + goto st263; +tr3671: + { + s->r_class = s->default_class; + } + { + s->r_ttl = s->default_ttl; + } + { + s->dname = s->r_owner; + s->r_owner_length = 0; + } + { p--; {stack[top++] = 263;goto st270;} } + goto st263; +st263: + if ( ++p == pe ) + goto _test_eof263; +case 263: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr679; + case 32: goto tr679; + case 40: goto tr680; + case 41: goto tr681; + case 82: goto st139; + case 114: goto st139; + case 1034: goto tr682; + case 1083: goto tr683; + } + goto tr908; +tr23: + { + s->buffer_length = 0; + } + goto st264; +tr909: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st264; +tr3621: + { + if (s->r_owner_length == 0) { + WARN(ZS_BAD_PREVIOUS_OWNER); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st264; +tr3685: + { + s->buffer_length = 0; + } + { + if (s->r_owner_length == 0) { + WARN(ZS_BAD_PREVIOUS_OWNER); + p--; {goto st268;} + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st264; +tr3681: + { + NOERR; + } + { + if (s->r_owner_length == 0) { + WARN(ZS_BAD_PREVIOUS_OWNER); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st264; +st264: + if ( ++p == pe ) + goto _test_eof264; +case 264: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 778: goto tr666; + case 1034: goto tr910; + } + if ( _widec > 895 ) { + if ( 896 <= _widec && _widec <= 1151 ) + goto tr909; + } else if ( _widec >= 640 ) + goto tr665; + goto tr85; +tr611: + { + s->buffer_length = 0; + } + goto st265; +tr911: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st265; +tr3654: + { + s->buffer_length = 0; + } + { + if (s->r_owner_length == 0) { + WARN(ZS_BAD_PREVIOUS_OWNER); + p--; {goto st268;} + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st265; +st265: + if ( ++p == pe ) + goto _test_eof265; +case 265: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 778: goto tr666; + case 1034: goto tr912; + } + if ( _widec > 895 ) { + if ( 896 <= _widec && _widec <= 1151 ) + goto tr911; + } else if ( _widec >= 640 ) + goto tr665; + goto tr145; +tr3643: + { + s->r_class = s->default_class; + } + { + s->dname = s->r_owner; + s->r_owner_length = 0; + } + { p--; {stack[top++] = 266;goto st270;} } + goto st266; +st266: + if ( ++p == pe ) + goto _test_eof266; +case 266: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr679; + case 32: goto tr679; + case 40: goto tr680; + case 41: goto tr681; + case 78: goto st63; + case 80: goto st66; + case 110: goto st63; + case 112: goto st66; + case 1034: goto tr682; + case 1083: goto tr683; + } + goto tr908; +tr150: + { + s->buffer_length = 0; + } + goto st267; +tr913: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st267; +tr144: + { + if (s->number64 <= UINT32_MAX) { + s->r_ttl = (uint32_t)(s->number64); + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st267; +st267: + if ( ++p == pe ) + goto _test_eof267; +case 267: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 778: goto tr92; + case 1034: goto tr914; + } + if ( _widec > 895 ) { + if ( 896 <= _widec && _widec <= 1151 ) + goto tr913; + } else if ( _widec >= 640 ) + goto tr91; + goto tr145; +st268: + if ( ++p == pe ) + goto _test_eof268; +case 268: + if ( (*p) == 10 ) + goto tr916; + goto tr915; +tr915: + { + s->buffer_length = 0; + } + { + if ((*p) == '\r') { + ERR(ZS_DOS_NEWLINE); + } + + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st269; +tr917: + { + if ((*p) == '\r') { + ERR(ZS_DOS_NEWLINE); + } + + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st269; +st269: + if ( ++p == pe ) + goto _test_eof269; +case 269: + if ( (*p) == 10 ) + goto tr918; + goto tr917; +tr916: + cs = 1137; + { + s->buffer_length = 0; + } + { + // Terminate the error context string. + s->buffer[s->buffer_length++] = 0; + + // Error counter incrementation. + s->error.counter++; + + // Initialize the fcall stack. + top = 0; + + // Reset the multiline context. + s->multiline = false; + + s->state = ZS_STATE_ERROR; + + // Execute the error callback. + if (s->process.automatic) { + if (s->process.error != NULL) { + s->process.error(s); + + // Stop if required from the callback. + if (s->state == ZS_STATE_STOP) { + {p++; goto _out;} + } + } + + // Stop the scanner if fatal error. + if (s->error.fatal) { + {p++; goto _out;} + } + {goto st1127;} + } else { + // Return if external processing. + p--; cs = 1127; {p++; goto _out;} + } + } + { + s->line_counter++; + } + goto _again; +tr918: + cs = 1137; + { + // Terminate the error context string. + s->buffer[s->buffer_length++] = 0; + + // Error counter incrementation. + s->error.counter++; + + // Initialize the fcall stack. + top = 0; + + // Reset the multiline context. + s->multiline = false; + + s->state = ZS_STATE_ERROR; + + // Execute the error callback. + if (s->process.automatic) { + if (s->process.error != NULL) { + s->process.error(s); + + // Stop if required from the callback. + if (s->state == ZS_STATE_STOP) { + {p++; goto _out;} + } + } + + // Stop the scanner if fatal error. + if (s->error.fatal) { + {p++; goto _out;} + } + {goto st1127;} + } else { + // Return if external processing. + p--; cs = 1127; {p++; goto _out;} + } + } + { + s->line_counter++; + } + goto _again; +st1137: + if ( ++p == pe ) + goto _test_eof1137; +case 1137: + goto st0; +st270: + if ( ++p == pe ) + goto _test_eof270; +case 270: + switch( (*p) ) { + case 42: goto tr920; + case 46: goto tr921; + case 64: goto st278; + case 92: goto tr923; + case 95: goto tr920; + } + if ( (*p) < 65 ) { + if ( 45 <= (*p) && (*p) <= 57 ) + goto tr920; + } else if ( (*p) > 90 ) { + if ( 97 <= (*p) && (*p) <= 122 ) + goto tr920; + } else + goto tr920; + goto tr919; +tr920: + { + s->item_length_position = 0; + s->dname_tmp_length = 0; + } + { + s->item_length = 0; + s->item_length_position = s->dname_tmp_length++; + } + { + // Check for maximum dname label length. + if (s->item_length < ZS_MAX_LABEL_LENGTH) { + (s->dname)[s->dname_tmp_length++] = (*p); + s->item_length++; + } else { + WARN(ZS_LABEL_OVERFLOW); + p--; {goto st268;} + } + } + goto st271; +tr925: + { + // Check for maximum dname label length. + if (s->item_length < ZS_MAX_LABEL_LENGTH) { + (s->dname)[s->dname_tmp_length++] = (*p); + s->item_length++; + } else { + WARN(ZS_LABEL_OVERFLOW); + p--; {goto st268;} + } + } + goto st271; +tr929: + { + s->item_length = 0; + s->item_length_position = s->dname_tmp_length++; + } + { + // Check for maximum dname label length. + if (s->item_length < ZS_MAX_LABEL_LENGTH) { + (s->dname)[s->dname_tmp_length++] = (*p); + s->item_length++; + } else { + WARN(ZS_LABEL_OVERFLOW); + p--; {goto st268;} + } + } + goto st271; +tr936: + { + s->dname_tmp_length++; + } + { + // Check for maximum dname label length. + if (s->item_length < ZS_MAX_LABEL_LENGTH) { + (s->dname)[s->dname_tmp_length++] = (*p); + s->item_length++; + } else { + WARN(ZS_LABEL_OVERFLOW); + p--; {goto st268;} + } + } + goto st271; +st271: + if ( ++p == pe ) + goto _test_eof271; +case 271: + switch( (*p) ) { + case 32: goto tr924; + case 42: goto tr925; + case 46: goto tr926; + case 59: goto tr924; + case 92: goto st273; + case 95: goto tr925; + } + if ( (*p) < 45 ) { + if ( (*p) > 10 ) { + if ( 40 <= (*p) && (*p) <= 41 ) + goto tr924; + } else if ( (*p) >= 9 ) + goto tr924; + } else if ( (*p) > 57 ) { + if ( (*p) > 90 ) { + if ( 97 <= (*p) && (*p) <= 122 ) + goto tr925; + } else if ( (*p) >= 65 ) + goto tr925; + } else + goto tr925; + goto tr919; +tr924: + { + // Check for maximum dname length overflow after each label. + // (at least the next label length must follow). + if (s->dname_tmp_length < ZS_MAX_DNAME_LENGTH) { + (s->dname)[s->item_length_position] = + (uint8_t)(s->item_length); + } else { + WARN(ZS_DNAME_OVERFLOW); + p--; {goto st268;} + } + } + { + // Check for (relative + origin) dname length overflow. + if (s->dname_tmp_length + s->zone_origin_length <= ZS_MAX_DNAME_LENGTH) { + memcpy(s->dname + s->dname_tmp_length, + s->zone_origin, + s->zone_origin_length); + + s->dname_tmp_length += s->zone_origin_length; + } else { + WARN(ZS_DNAME_OVERFLOW); + p--; {goto st268;} + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1138; +tr928: + { + // Enough room for the terminal label is guaranteed (_label_exit). + (s->dname)[s->dname_tmp_length++] = 0; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1138; +tr935: + { + s->dname_tmp_length++; + } + { + // Check for maximum dname length overflow after each label. + // (at least the next label length must follow). + if (s->dname_tmp_length < ZS_MAX_DNAME_LENGTH) { + (s->dname)[s->item_length_position] = + (uint8_t)(s->item_length); + } else { + WARN(ZS_DNAME_OVERFLOW); + p--; {goto st268;} + } + } + { + // Check for (relative + origin) dname length overflow. + if (s->dname_tmp_length + s->zone_origin_length <= ZS_MAX_DNAME_LENGTH) { + memcpy(s->dname + s->dname_tmp_length, + s->zone_origin, + s->zone_origin_length); + + s->dname_tmp_length += s->zone_origin_length; + } else { + WARN(ZS_DNAME_OVERFLOW); + p--; {goto st268;} + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1138; +tr939: + { + // Copy already verified zone origin. + memcpy(s->dname, + s->zone_origin, + s->zone_origin_length); + + s->dname_tmp_length = s->zone_origin_length; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1138; +st1138: + if ( ++p == pe ) + goto _test_eof1138; +case 1138: + goto st0; +tr926: + { + // Check for maximum dname length overflow after each label. + // (at least the next label length must follow). + if (s->dname_tmp_length < ZS_MAX_DNAME_LENGTH) { + (s->dname)[s->item_length_position] = + (uint8_t)(s->item_length); + } else { + WARN(ZS_DNAME_OVERFLOW); + p--; {goto st268;} + } + } + goto st272; +tr937: + { + s->dname_tmp_length++; + } + { + // Check for maximum dname length overflow after each label. + // (at least the next label length must follow). + if (s->dname_tmp_length < ZS_MAX_DNAME_LENGTH) { + (s->dname)[s->item_length_position] = + (uint8_t)(s->item_length); + } else { + WARN(ZS_DNAME_OVERFLOW); + p--; {goto st268;} + } + } + goto st272; +st272: + if ( ++p == pe ) + goto _test_eof272; +case 272: + switch( (*p) ) { + case 32: goto tr928; + case 42: goto tr929; + case 45: goto tr929; + case 59: goto tr928; + case 92: goto tr930; + case 95: goto tr929; + } + if ( (*p) < 47 ) { + if ( (*p) > 10 ) { + if ( 40 <= (*p) && (*p) <= 41 ) + goto tr928; + } else if ( (*p) >= 9 ) + goto tr928; + } else if ( (*p) > 57 ) { + if ( (*p) > 90 ) { + if ( 97 <= (*p) && (*p) <= 122 ) + goto tr929; + } else if ( (*p) >= 65 ) + goto tr929; + } else + goto tr929; + goto tr919; +tr923: + { + s->item_length_position = 0; + s->dname_tmp_length = 0; + } + { + s->item_length = 0; + s->item_length_position = s->dname_tmp_length++; + } + goto st273; +tr930: + { + s->item_length = 0; + s->item_length_position = s->dname_tmp_length++; + } + goto st273; +tr938: + { + s->dname_tmp_length++; + } + goto st273; +st273: + if ( ++p == pe ) + goto _test_eof273; +case 273: + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr931; + goto tr925; +tr931: + { + if (s->item_length < ZS_MAX_LABEL_LENGTH) { + (s->dname)[s->dname_tmp_length] = 0; + s->item_length++; + } else { + WARN(ZS_LABEL_OVERFLOW); + p--; {goto st268;} + } + } + { + (s->dname)[s->dname_tmp_length] *= 10; + (s->dname)[s->dname_tmp_length] += digit_to_num[(uint8_t)(*p)]; + } + goto st274; +st274: + if ( ++p == pe ) + goto _test_eof274; +case 274: + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr933; + goto tr932; +tr933: + { + (s->dname)[s->dname_tmp_length] *= 10; + (s->dname)[s->dname_tmp_length] += digit_to_num[(uint8_t)(*p)]; + } + goto st275; +st275: + if ( ++p == pe ) + goto _test_eof275; +case 275: + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr934; + goto tr932; +tr934: + { + (s->dname)[s->dname_tmp_length] *= 10; + (s->dname)[s->dname_tmp_length] += digit_to_num[(uint8_t)(*p)]; + } + goto st276; +st276: + if ( ++p == pe ) + goto _test_eof276; +case 276: + switch( (*p) ) { + case 32: goto tr935; + case 42: goto tr936; + case 46: goto tr937; + case 59: goto tr935; + case 92: goto tr938; + case 95: goto tr936; + } + if ( (*p) < 45 ) { + if ( (*p) > 10 ) { + if ( 40 <= (*p) && (*p) <= 41 ) + goto tr935; + } else if ( (*p) >= 9 ) + goto tr935; + } else if ( (*p) > 57 ) { + if ( (*p) > 90 ) { + if ( 97 <= (*p) && (*p) <= 122 ) + goto tr936; + } else if ( (*p) >= 65 ) + goto tr936; + } else + goto tr936; + goto tr932; +tr921: + { + s->item_length_position = 0; + s->dname_tmp_length = 0; + } + goto st277; +st277: + if ( ++p == pe ) + goto _test_eof277; +case 277: + switch( (*p) ) { + case 32: goto tr928; + case 59: goto tr928; + } + if ( (*p) > 10 ) { + if ( 40 <= (*p) && (*p) <= 41 ) + goto tr928; + } else if ( (*p) >= 9 ) + goto tr928; + goto tr919; +st278: + if ( ++p == pe ) + goto _test_eof278; +case 278: + switch( (*p) ) { + case 32: goto tr939; + case 59: goto tr939; + } + if ( (*p) > 10 ) { + if ( 40 <= (*p) && (*p) <= 41 ) + goto tr939; + } else if ( (*p) >= 9 ) + goto tr939; + goto tr919; +st279: + if ( ++p == pe ) + goto _test_eof279; +case 279: + switch( (*p) ) { + case 34: goto st285; + case 92: goto st281; + } + if ( (*p) > 58 ) { + if ( 60 <= (*p) && (*p) <= 126 ) + goto tr941; + } else if ( (*p) >= 33 ) + goto tr941; + goto tr940; +tr941: + { + if (rdata_tail <= rdata_stop) { + // Split long string. + if (s->long_string && + rdata_tail - s->item_length_location == 1 + MAX_ITEM_LENGTH) { + // _item_length_exit equivalent. + *(s->item_length_location) = MAX_ITEM_LENGTH; + // _item_length_init equivalent. + s->item_length_location = rdata_tail++; + + if (rdata_tail > rdata_stop) { + WARN(ZS_TEXT_OVERFLOW); + p--; {goto st268;} + } + } + + *(rdata_tail++) = (*p); + } else { + WARN(ZS_TEXT_OVERFLOW); + p--; {goto st268;} + } + } + goto st280; +tr951: + { + rdata_tail++; + } + { + if (rdata_tail <= rdata_stop) { + // Split long string. + if (s->long_string && + rdata_tail - s->item_length_location == 1 + MAX_ITEM_LENGTH) { + // _item_length_exit equivalent. + *(s->item_length_location) = MAX_ITEM_LENGTH; + // _item_length_init equivalent. + s->item_length_location = rdata_tail++; + + if (rdata_tail > rdata_stop) { + WARN(ZS_TEXT_OVERFLOW); + p--; {goto st268;} + } + } + + *(rdata_tail++) = (*p); + } else { + WARN(ZS_TEXT_OVERFLOW); + p--; {goto st268;} + } + } + goto st280; +st280: + if ( ++p == pe ) + goto _test_eof280; +case 280: + switch( (*p) ) { + case 32: goto tr944; + case 33: goto tr941; + case 59: goto tr944; + case 92: goto st281; + } + if ( (*p) < 35 ) { + if ( 9 <= (*p) && (*p) <= 10 ) + goto tr944; + } else if ( (*p) > 39 ) { + if ( (*p) > 41 ) { + if ( 42 <= (*p) && (*p) <= 126 ) + goto tr941; + } else if ( (*p) >= 40 ) + goto tr945; + } else + goto tr941; + goto tr940; +tr944: + { + p--; {cs = stack[--top];goto _again;} + } + goto st1139; +tr950: + { + rdata_tail++; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1139; +st1139: + if ( ++p == pe ) + goto _test_eof1139; +case 1139: + goto st0; +tr945: + { + if (rdata_tail <= rdata_stop) { + // Split long string. + if (s->long_string && + rdata_tail - s->item_length_location == 1 + MAX_ITEM_LENGTH) { + // _item_length_exit equivalent. + *(s->item_length_location) = MAX_ITEM_LENGTH; + // _item_length_init equivalent. + s->item_length_location = rdata_tail++; + + if (rdata_tail > rdata_stop) { + WARN(ZS_TEXT_OVERFLOW); + p--; {goto st268;} + } + } + + *(rdata_tail++) = (*p); + } else { + WARN(ZS_TEXT_OVERFLOW); + p--; {goto st268;} + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1140; +tr952: + { + rdata_tail++; + } + { + if (rdata_tail <= rdata_stop) { + // Split long string. + if (s->long_string && + rdata_tail - s->item_length_location == 1 + MAX_ITEM_LENGTH) { + // _item_length_exit equivalent. + *(s->item_length_location) = MAX_ITEM_LENGTH; + // _item_length_init equivalent. + s->item_length_location = rdata_tail++; + + if (rdata_tail > rdata_stop) { + WARN(ZS_TEXT_OVERFLOW); + p--; {goto st268;} + } + } + + *(rdata_tail++) = (*p); + } else { + WARN(ZS_TEXT_OVERFLOW); + p--; {goto st268;} + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1140; +st1140: + if ( ++p == pe ) + goto _test_eof1140; +case 1140: + switch( (*p) ) { + case 32: goto tr944; + case 33: goto tr941; + case 59: goto tr944; + case 92: goto st281; + } + if ( (*p) < 35 ) { + if ( 9 <= (*p) && (*p) <= 10 ) + goto tr944; + } else if ( (*p) > 39 ) { + if ( (*p) > 41 ) { + if ( 42 <= (*p) && (*p) <= 126 ) + goto tr941; + } else if ( (*p) >= 40 ) + goto tr945; + } else + goto tr941; + goto tr940; +tr953: + { + rdata_tail++; + } + goto st281; +st281: + if ( ++p == pe ) + goto _test_eof281; +case 281: + if ( (*p) < 48 ) { + if ( 32 <= (*p) && (*p) <= 47 ) + goto tr941; + } else if ( (*p) > 57 ) { + if ( 58 <= (*p) && (*p) <= 126 ) + goto tr941; + } else + goto tr947; + goto tr946; +tr947: + { + if (rdata_tail <= rdata_stop) { + // Split long string. + if (s->long_string && + rdata_tail - s->item_length_location == 1 + MAX_ITEM_LENGTH) { + // _item_length_exit equivalent. + *(s->item_length_location) = MAX_ITEM_LENGTH; + // _item_length_init equivalent. + s->item_length_location = rdata_tail++; + + if (rdata_tail > rdata_stop) { + WARN(ZS_TEXT_OVERFLOW); + p--; {goto st268;} + } + } + + *rdata_tail = 0; + s->item_length++; + } else { + WARN(ZS_TEXT_OVERFLOW); + p--; {goto st268;} + } + } + { + if ((*rdata_tail < (UINT8_MAX / 10)) || // Dominant fast check. + ((*rdata_tail == (UINT8_MAX / 10)) && // Marginal case. + ((*p) <= (UINT8_MAX % 10) + '0') + ) + ) { + *rdata_tail *= 10; + *rdata_tail += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + goto st282; +st282: + if ( ++p == pe ) + goto _test_eof282; +case 282: + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr948; + goto tr946; +tr948: + { + if ((*rdata_tail < (UINT8_MAX / 10)) || // Dominant fast check. + ((*rdata_tail == (UINT8_MAX / 10)) && // Marginal case. + ((*p) <= (UINT8_MAX % 10) + '0') + ) + ) { + *rdata_tail *= 10; + *rdata_tail += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + goto st283; +st283: + if ( ++p == pe ) + goto _test_eof283; +case 283: + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr949; + goto tr946; +tr949: + { + if ((*rdata_tail < (UINT8_MAX / 10)) || // Dominant fast check. + ((*rdata_tail == (UINT8_MAX / 10)) && // Marginal case. + ((*p) <= (UINT8_MAX % 10) + '0') + ) + ) { + *rdata_tail *= 10; + *rdata_tail += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + goto st284; +st284: + if ( ++p == pe ) + goto _test_eof284; +case 284: + switch( (*p) ) { + case 32: goto tr950; + case 33: goto tr951; + case 59: goto tr950; + case 92: goto tr953; + } + if ( (*p) < 35 ) { + if ( 9 <= (*p) && (*p) <= 10 ) + goto tr950; + } else if ( (*p) > 39 ) { + if ( (*p) > 41 ) { + if ( 42 <= (*p) && (*p) <= 126 ) + goto tr951; + } else if ( (*p) >= 40 ) + goto tr952; + } else + goto tr951; + goto tr946; +tr954: + { + if (rdata_tail <= rdata_stop) { + // Split long string. + if (s->long_string && + rdata_tail - s->item_length_location == 1 + MAX_ITEM_LENGTH) { + // _item_length_exit equivalent. + *(s->item_length_location) = MAX_ITEM_LENGTH; + // _item_length_init equivalent. + s->item_length_location = rdata_tail++; + + if (rdata_tail > rdata_stop) { + WARN(ZS_TEXT_OVERFLOW); + p--; {goto st268;} + } + } + + *(rdata_tail++) = (*p); + } else { + WARN(ZS_TEXT_OVERFLOW); + p--; {goto st268;} + } + } + goto st285; +tr961: + { + rdata_tail++; + } + { + if (rdata_tail <= rdata_stop) { + // Split long string. + if (s->long_string && + rdata_tail - s->item_length_location == 1 + MAX_ITEM_LENGTH) { + // _item_length_exit equivalent. + *(s->item_length_location) = MAX_ITEM_LENGTH; + // _item_length_init equivalent. + s->item_length_location = rdata_tail++; + + if (rdata_tail > rdata_stop) { + WARN(ZS_TEXT_OVERFLOW); + p--; {goto st268;} + } + } + + *(rdata_tail++) = (*p); + } else { + WARN(ZS_TEXT_OVERFLOW); + p--; {goto st268;} + } + } + goto st285; +st285: + if ( ++p == pe ) + goto _test_eof285; +case 285: + _widec = (*p); + if ( 10 <= (*p) && (*p) <= 10 ) { + _widec = (short)(128 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr954; + case 34: goto st286; + case 92: goto st287; + case 522: goto tr954; + } + if ( 32 <= _widec && _widec <= 126 ) + goto tr954; + goto tr940; +tr962: + { + rdata_tail++; + } + goto st286; +st286: + if ( ++p == pe ) + goto _test_eof286; +case 286: + switch( (*p) ) { + case 32: goto tr944; + case 59: goto tr944; + } + if ( (*p) > 10 ) { + if ( 40 <= (*p) && (*p) <= 41 ) + goto tr944; + } else if ( (*p) >= 9 ) + goto tr944; + goto tr957; +tr963: + { + rdata_tail++; + } + goto st287; +st287: + if ( ++p == pe ) + goto _test_eof287; +case 287: + if ( (*p) < 48 ) { + if ( 32 <= (*p) && (*p) <= 47 ) + goto tr954; + } else if ( (*p) > 57 ) { + if ( 58 <= (*p) && (*p) <= 126 ) + goto tr954; + } else + goto tr958; + goto tr946; +tr958: + { + if (rdata_tail <= rdata_stop) { + // Split long string. + if (s->long_string && + rdata_tail - s->item_length_location == 1 + MAX_ITEM_LENGTH) { + // _item_length_exit equivalent. + *(s->item_length_location) = MAX_ITEM_LENGTH; + // _item_length_init equivalent. + s->item_length_location = rdata_tail++; + + if (rdata_tail > rdata_stop) { + WARN(ZS_TEXT_OVERFLOW); + p--; {goto st268;} + } + } + + *rdata_tail = 0; + s->item_length++; + } else { + WARN(ZS_TEXT_OVERFLOW); + p--; {goto st268;} + } + } + { + if ((*rdata_tail < (UINT8_MAX / 10)) || // Dominant fast check. + ((*rdata_tail == (UINT8_MAX / 10)) && // Marginal case. + ((*p) <= (UINT8_MAX % 10) + '0') + ) + ) { + *rdata_tail *= 10; + *rdata_tail += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + goto st288; +st288: + if ( ++p == pe ) + goto _test_eof288; +case 288: + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr959; + goto tr946; +tr959: + { + if ((*rdata_tail < (UINT8_MAX / 10)) || // Dominant fast check. + ((*rdata_tail == (UINT8_MAX / 10)) && // Marginal case. + ((*p) <= (UINT8_MAX % 10) + '0') + ) + ) { + *rdata_tail *= 10; + *rdata_tail += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + goto st289; +st289: + if ( ++p == pe ) + goto _test_eof289; +case 289: + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr960; + goto tr946; +tr960: + { + if ((*rdata_tail < (UINT8_MAX / 10)) || // Dominant fast check. + ((*rdata_tail == (UINT8_MAX / 10)) && // Marginal case. + ((*p) <= (UINT8_MAX % 10) + '0') + ) + ) { + *rdata_tail *= 10; + *rdata_tail += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + goto st290; +st290: + if ( ++p == pe ) + goto _test_eof290; +case 290: + _widec = (*p); + if ( 10 <= (*p) && (*p) <= 10 ) { + _widec = (short)(128 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr961; + case 34: goto tr962; + case 92: goto tr963; + case 522: goto tr961; + } + if ( 32 <= _widec && _widec <= 126 ) + goto tr961; + goto tr946; +st291: + if ( ++p == pe ) + goto _test_eof291; +case 291: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st292; + case 32: goto st292; + case 40: goto tr966; + case 41: goto tr967; + case 1034: goto tr968; + case 1083: goto tr969; + } + goto tr964; +tr966: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st292; +tr967: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st292; +tr968: + { + s->line_counter++; + } + goto st292; +tr1007: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st292; +st292: + if ( ++p == pe ) + goto _test_eof292; +case 292: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st292; + case 32: goto st292; + case 40: goto tr966; + case 41: goto tr967; + case 1034: goto tr968; + case 1083: goto tr969; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr971; + goto tr970; +tr971: + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st293; +tr976: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st293; +st293: + if ( ++p == pe ) + goto _test_eof293; +case 293: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr973; + case 32: goto tr973; + case 40: goto tr974; + case 41: goto tr975; + case 68: goto tr977; + case 72: goto tr978; + case 77: goto tr979; + case 83: goto st296; + case 87: goto tr981; + case 100: goto tr977; + case 104: goto tr978; + case 109: goto tr979; + case 115: goto st296; + case 119: goto tr981; + case 778: goto tr982; + case 827: goto tr983; + case 1034: goto tr982; + case 1083: goto tr983; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr976; + goto tr972; +tr986: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st294; +tr987: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st294; +tr973: + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st294; +tr974: + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st294; +tr975: + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st294; +tr1000: + { + if (s->number64 + s->number64_tmp < UINT32_MAX) { + s->number64 += s->number64_tmp; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st294; +tr1001: + { + if (s->number64 + s->number64_tmp < UINT32_MAX) { + s->number64 += s->number64_tmp; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st294; +tr1002: + { + if (s->number64 + s->number64_tmp < UINT32_MAX) { + s->number64 += s->number64_tmp; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st294; +st294: + if ( ++p == pe ) + goto _test_eof294; +case 294: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st294; + case 32: goto st294; + case 40: goto tr986; + case 41: goto tr987; + case 778: goto tr988; + case 827: goto tr989; + case 1034: goto tr988; + case 1083: goto tr989; + } + goto tr984; +tr982: + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + if (s->number64 <= UINT32_MAX) { + s->default_ttl = (uint32_t)(s->number64); + } else { + ERR(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + p--; {cs = stack[--top];goto _again;} + } + { + s->line_counter++; + } + goto st1141; +tr988: + { + if (s->number64 <= UINT32_MAX) { + s->default_ttl = (uint32_t)(s->number64); + } else { + ERR(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + p--; {cs = stack[--top];goto _again;} + } + { + s->line_counter++; + } + goto st1141; +tr991: + { + s->buffer[s->buffer_length++] = 0; + } + { + if (s->number64 <= UINT32_MAX) { + s->default_ttl = (uint32_t)(s->number64); + } else { + ERR(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + p--; {cs = stack[--top];goto _again;} + } + { + s->line_counter++; + } + goto st1141; +tr1004: + { + if (s->number64 + s->number64_tmp < UINT32_MAX) { + s->number64 += s->number64_tmp; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + if (s->number64 <= UINT32_MAX) { + s->default_ttl = (uint32_t)(s->number64); + } else { + ERR(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + p--; {cs = stack[--top];goto _again;} + } + { + s->line_counter++; + } + goto st1141; +st1141: + if ( ++p == pe ) + goto _test_eof1141; +case 1141: + goto st0; +tr989: + { + s->buffer_length = 0; + } + goto st295; +tr983: + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + s->buffer_length = 0; + } + goto st295; +tr990: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st295; +tr1005: + { + if (s->number64 + s->number64_tmp < UINT32_MAX) { + s->number64 += s->number64_tmp; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + s->buffer_length = 0; + } + goto st295; +st295: + if ( ++p == pe ) + goto _test_eof295; +case 295: + if ( (*p) == 10 ) + goto tr991; + goto tr990; +tr977: + { if (s->number64 <= (UINT32_MAX / 86400)) { + s->number64 *= 86400; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st296; +tr978: + { if (s->number64 <= (UINT32_MAX / 3600)) { + s->number64 *= 3600; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st296; +tr979: + { if (s->number64 <= (UINT32_MAX / 60)) { + s->number64 *= 60; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st296; +tr981: + { if (s->number64 <= (UINT32_MAX / 604800)) { + s->number64 *= 604800; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st296; +st296: + if ( ++p == pe ) + goto _test_eof296; +case 296: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr973; + case 32: goto tr973; + case 40: goto tr974; + case 41: goto tr975; + case 778: goto tr982; + case 827: goto tr983; + case 1034: goto tr982; + case 1083: goto tr983; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr992; + goto tr972; +tr994: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st297; +tr992: + { + s->number64_tmp = s->number64; + } + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st297; +tr1003: + { + if (s->number64 + s->number64_tmp < UINT32_MAX) { + s->number64 += s->number64_tmp; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + s->number64_tmp = s->number64; + } + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st297; +st297: + if ( ++p == pe ) + goto _test_eof297; +case 297: + switch( (*p) ) { + case 68: goto tr995; + case 72: goto tr996; + case 77: goto tr997; + case 83: goto st298; + case 87: goto tr999; + case 100: goto tr995; + case 104: goto tr996; + case 109: goto tr997; + case 115: goto st298; + case 119: goto tr999; + } + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr994; + goto tr993; +tr995: + { if (s->number64 <= (UINT32_MAX / 86400)) { + s->number64 *= 86400; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st298; +tr996: + { if (s->number64 <= (UINT32_MAX / 3600)) { + s->number64 *= 3600; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st298; +tr997: + { if (s->number64 <= (UINT32_MAX / 60)) { + s->number64 *= 60; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st298; +tr999: + { if (s->number64 <= (UINT32_MAX / 604800)) { + s->number64 *= 604800; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st298; +st298: + if ( ++p == pe ) + goto _test_eof298; +case 298: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr1000; + case 32: goto tr1000; + case 40: goto tr1001; + case 41: goto tr1002; + case 778: goto tr1004; + case 827: goto tr1005; + case 1034: goto tr1004; + case 1083: goto tr1005; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr1003; + goto tr972; +tr969: + { + s->buffer_length = 0; + } + goto st299; +tr1006: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st299; +st299: + if ( ++p == pe ) + goto _test_eof299; +case 299: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr1007; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr1006; + goto tr964; +st300: + if ( ++p == pe ) + goto _test_eof300; +case 300: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st301; + case 32: goto st301; + case 40: goto tr1010; + case 41: goto tr1011; + case 1034: goto tr1012; + case 1083: goto tr1013; + } + goto tr1008; +tr1010: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st301; +tr1011: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st301; +tr1012: + { + s->line_counter++; + } + goto st301; +tr1043: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st301; +st301: + if ( ++p == pe ) + goto _test_eof301; +case 301: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st301; + case 32: goto st301; + case 40: goto tr1010; + case 41: goto tr1011; + case 42: goto tr1014; + case 46: goto tr1015; + case 92: goto tr1016; + case 95: goto tr1014; + case 1034: goto tr1012; + case 1083: goto tr1013; + } + if ( _widec < 65 ) { + if ( 45 <= _widec && _widec <= 57 ) + goto tr1014; + } else if ( _widec > 90 ) { + if ( 97 <= _widec && _widec <= 122 ) + goto tr1014; + } else + goto tr1014; + goto tr1008; +tr1017: + { + // Check for maximum dname label length. + if (s->item_length < ZS_MAX_LABEL_LENGTH) { + (s->dname)[s->dname_tmp_length++] = (*p); + s->item_length++; + } else { + WARN(ZS_LABEL_OVERFLOW); + p--; {goto st268;} + } + } + goto st302; +tr1024: + { + s->item_length = 0; + s->item_length_position = s->dname_tmp_length++; + } + { + // Check for maximum dname label length. + if (s->item_length < ZS_MAX_LABEL_LENGTH) { + (s->dname)[s->dname_tmp_length++] = (*p); + s->item_length++; + } else { + WARN(ZS_LABEL_OVERFLOW); + p--; {goto st268;} + } + } + goto st302; +tr1039: + { + s->dname_tmp_length++; + } + { + // Check for maximum dname label length. + if (s->item_length < ZS_MAX_LABEL_LENGTH) { + (s->dname)[s->dname_tmp_length++] = (*p); + s->item_length++; + } else { + WARN(ZS_LABEL_OVERFLOW); + p--; {goto st268;} + } + } + goto st302; +tr1014: + { + s->dname = s->zone_origin; + } + { + s->item_length_position = 0; + s->dname_tmp_length = 0; + } + { + s->item_length = 0; + s->item_length_position = s->dname_tmp_length++; + } + { + // Check for maximum dname label length. + if (s->item_length < ZS_MAX_LABEL_LENGTH) { + (s->dname)[s->dname_tmp_length++] = (*p); + s->item_length++; + } else { + WARN(ZS_LABEL_OVERFLOW); + p--; {goto st268;} + } + } + goto st302; +st302: + if ( ++p == pe ) + goto _test_eof302; +case 302: + switch( (*p) ) { + case 42: goto tr1017; + case 46: goto tr1018; + case 92: goto st306; + case 95: goto tr1017; + } + if ( (*p) < 65 ) { + if ( 45 <= (*p) && (*p) <= 57 ) + goto tr1017; + } else if ( (*p) > 90 ) { + if ( 97 <= (*p) && (*p) <= 122 ) + goto tr1017; + } else + goto tr1017; + goto tr1008; +tr1018: + { + // Check for maximum dname length overflow after each label. + // (at least the next label length must follow). + if (s->dname_tmp_length < ZS_MAX_DNAME_LENGTH) { + (s->dname)[s->item_length_position] = + (uint8_t)(s->item_length); + } else { + WARN(ZS_DNAME_OVERFLOW); + p--; {goto st268;} + } + } + goto st303; +tr1040: + { + s->dname_tmp_length++; + } + { + // Check for maximum dname length overflow after each label. + // (at least the next label length must follow). + if (s->dname_tmp_length < ZS_MAX_DNAME_LENGTH) { + (s->dname)[s->item_length_position] = + (uint8_t)(s->item_length); + } else { + WARN(ZS_DNAME_OVERFLOW); + p--; {goto st268;} + } + } + goto st303; +st303: + if ( ++p == pe ) + goto _test_eof303; +case 303: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr1021; + case 32: goto tr1021; + case 40: goto tr1022; + case 41: goto tr1023; + case 42: goto tr1024; + case 45: goto tr1024; + case 92: goto tr1025; + case 95: goto tr1024; + case 778: goto tr1026; + case 827: goto tr1027; + case 1034: goto tr1026; + case 1083: goto tr1027; + } + if ( _widec < 65 ) { + if ( 47 <= _widec && _widec <= 57 ) + goto tr1024; + } else if ( _widec > 90 ) { + if ( 97 <= _widec && _widec <= 122 ) + goto tr1024; + } else + goto tr1024; + goto tr1020; +tr1029: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st304; +tr1030: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st304; +tr1021: + { + // Enough room for the terminal label is guaranteed (_label_exit). + (s->dname)[s->dname_tmp_length++] = 0; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st304; +tr1022: + { + // Enough room for the terminal label is guaranteed (_label_exit). + (s->dname)[s->dname_tmp_length++] = 0; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st304; +tr1023: + { + // Enough room for the terminal label is guaranteed (_label_exit). + (s->dname)[s->dname_tmp_length++] = 0; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st304; +st304: + if ( ++p == pe ) + goto _test_eof304; +case 304: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st304; + case 32: goto st304; + case 40: goto tr1029; + case 41: goto tr1030; + case 778: goto tr1031; + case 827: goto tr1032; + case 1034: goto tr1031; + case 1083: goto tr1032; + } + goto tr1020; +tr1026: + { + // Enough room for the terminal label is guaranteed (_label_exit). + (s->dname)[s->dname_tmp_length++] = 0; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + s->zone_origin_length = s->dname_tmp_length; + } + { + p--; {cs = stack[--top];goto _again;} + } + { + s->line_counter++; + } + goto st1142; +tr1031: + { + s->zone_origin_length = s->dname_tmp_length; + } + { + p--; {cs = stack[--top];goto _again;} + } + { + s->line_counter++; + } + goto st1142; +tr1034: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->zone_origin_length = s->dname_tmp_length; + } + { + p--; {cs = stack[--top];goto _again;} + } + { + s->line_counter++; + } + goto st1142; +st1142: + if ( ++p == pe ) + goto _test_eof1142; +case 1142: + goto st0; +tr1032: + { + s->buffer_length = 0; + } + goto st305; +tr1033: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st305; +tr1027: + { + // Enough room for the terminal label is guaranteed (_label_exit). + (s->dname)[s->dname_tmp_length++] = 0; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + s->buffer_length = 0; + } + goto st305; +st305: + if ( ++p == pe ) + goto _test_eof305; +case 305: + if ( (*p) == 10 ) + goto tr1034; + goto tr1033; +tr1025: + { + s->item_length = 0; + s->item_length_position = s->dname_tmp_length++; + } + goto st306; +tr1041: + { + s->dname_tmp_length++; + } + goto st306; +tr1016: + { + s->dname = s->zone_origin; + } + { + s->item_length_position = 0; + s->dname_tmp_length = 0; + } + { + s->item_length = 0; + s->item_length_position = s->dname_tmp_length++; + } + goto st306; +st306: + if ( ++p == pe ) + goto _test_eof306; +case 306: + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr1035; + goto tr1017; +tr1035: + { + if (s->item_length < ZS_MAX_LABEL_LENGTH) { + (s->dname)[s->dname_tmp_length] = 0; + s->item_length++; + } else { + WARN(ZS_LABEL_OVERFLOW); + p--; {goto st268;} + } + } + { + (s->dname)[s->dname_tmp_length] *= 10; + (s->dname)[s->dname_tmp_length] += digit_to_num[(uint8_t)(*p)]; + } + goto st307; +st307: + if ( ++p == pe ) + goto _test_eof307; +case 307: + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr1037; + goto tr1036; +tr1037: + { + (s->dname)[s->dname_tmp_length] *= 10; + (s->dname)[s->dname_tmp_length] += digit_to_num[(uint8_t)(*p)]; + } + goto st308; +st308: + if ( ++p == pe ) + goto _test_eof308; +case 308: + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr1038; + goto tr1036; +tr1038: + { + (s->dname)[s->dname_tmp_length] *= 10; + (s->dname)[s->dname_tmp_length] += digit_to_num[(uint8_t)(*p)]; + } + goto st309; +st309: + if ( ++p == pe ) + goto _test_eof309; +case 309: + switch( (*p) ) { + case 42: goto tr1039; + case 46: goto tr1040; + case 92: goto tr1041; + case 95: goto tr1039; + } + if ( (*p) < 65 ) { + if ( 45 <= (*p) && (*p) <= 57 ) + goto tr1039; + } else if ( (*p) > 90 ) { + if ( 97 <= (*p) && (*p) <= 122 ) + goto tr1039; + } else + goto tr1039; + goto tr1036; +tr1015: + { + s->dname = s->zone_origin; + } + { + s->item_length_position = 0; + s->dname_tmp_length = 0; + } + goto st310; +st310: + if ( ++p == pe ) + goto _test_eof310; +case 310: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr1021; + case 32: goto tr1021; + case 40: goto tr1022; + case 41: goto tr1023; + case 778: goto tr1026; + case 827: goto tr1027; + case 1034: goto tr1026; + case 1083: goto tr1027; + } + goto tr1020; +tr1013: + { + s->buffer_length = 0; + } + goto st311; +tr1042: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st311; +st311: + if ( ++p == pe ) + goto _test_eof311; +case 311: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr1043; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr1042; + goto tr1008; +st312: + if ( ++p == pe ) + goto _test_eof312; +case 312: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st313; + case 32: goto st313; + case 40: goto tr1045; + case 41: goto tr1046; + case 1034: goto tr1047; + case 1083: goto tr1048; + } + goto st0; +tr1045: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st313; +tr1046: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st313; +tr1047: + { + s->line_counter++; + } + goto st313; +tr1101: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st313; +st313: + if ( ++p == pe ) + goto _test_eof313; +case 313: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st313; + case 32: goto st313; + case 40: goto tr1045; + case 41: goto tr1046; + case 1034: goto tr1047; + case 1083: goto tr1048; + } + if ( _widec < 11 ) { + if ( _widec <= 8 ) + goto tr1049; + } else if ( _widec > 58 ) { + if ( 60 <= _widec ) + goto tr1049; + } else + goto tr1049; + goto tr1050; +tr1049: + { + rdata_tail = s->r_data; + } + { p--; {stack[top++] = 314;goto st279;} } + goto st314; +st314: + if ( ++p == pe ) + goto _test_eof314; +case 314: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr1052; + case 32: goto tr1052; + case 40: goto tr1053; + case 41: goto tr1054; + case 778: goto tr1055; + case 827: goto tr1056; + case 1034: goto tr1055; + case 1083: goto tr1057; + } + goto tr1051; +tr1060: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st315; +tr1061: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st315; +tr1052: + { + size_t len = rdata_tail - s->r_data; + if (len >= sizeof(s->include_filename)) { + ERR(ZS_BAD_INCLUDE_FILENAME); + p--; {goto st268;} + } + + // Store zero terminated include filename. + memcpy(s->include_filename, s->r_data, len); + s->include_filename[len] = '\0'; + + // For detection whether origin is not present. + s->dname = NULL; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st315; +tr1053: + { + size_t len = rdata_tail - s->r_data; + if (len >= sizeof(s->include_filename)) { + ERR(ZS_BAD_INCLUDE_FILENAME); + p--; {goto st268;} + } + + // Store zero terminated include filename. + memcpy(s->include_filename, s->r_data, len); + s->include_filename[len] = '\0'; + + // For detection whether origin is not present. + s->dname = NULL; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st315; +tr1054: + { + size_t len = rdata_tail - s->r_data; + if (len >= sizeof(s->include_filename)) { + ERR(ZS_BAD_INCLUDE_FILENAME); + p--; {goto st268;} + } + + // Store zero terminated include filename. + memcpy(s->include_filename, s->r_data, len); + s->include_filename[len] = '\0'; + + // For detection whether origin is not present. + s->dname = NULL; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st315; +st315: + if ( ++p == pe ) + goto _test_eof315; +case 315: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st315; + case 32: goto st315; + case 40: goto tr1060; + case 41: goto tr1061; + case 42: goto tr1062; + case 46: goto tr1063; + case 92: goto tr1064; + case 95: goto tr1062; + case 778: goto tr1065; + case 827: goto tr1066; + case 1034: goto tr1065; + case 1083: goto tr1067; + } + if ( _widec < 65 ) { + if ( 45 <= _widec && _widec <= 57 ) + goto tr1062; + } else if ( _widec > 90 ) { + if ( 97 <= _widec && _widec <= 122 ) + goto tr1062; + } else + goto tr1062; + goto tr1058; +tr1069: + { + // Check for maximum dname label length. + if (s->item_length < ZS_MAX_LABEL_LENGTH) { + (s->dname)[s->dname_tmp_length++] = (*p); + s->item_length++; + } else { + WARN(ZS_LABEL_OVERFLOW); + p--; {goto st268;} + } + } + goto st316; +tr1075: + { + s->item_length = 0; + s->item_length_position = s->dname_tmp_length++; + } + { + // Check for maximum dname label length. + if (s->item_length < ZS_MAX_LABEL_LENGTH) { + (s->dname)[s->dname_tmp_length++] = (*p); + s->item_length++; + } else { + WARN(ZS_LABEL_OVERFLOW); + p--; {goto st268;} + } + } + goto st316; +tr1088: + { + s->dname_tmp_length++; + } + { + // Check for maximum dname label length. + if (s->item_length < ZS_MAX_LABEL_LENGTH) { + (s->dname)[s->dname_tmp_length++] = (*p); + s->item_length++; + } else { + WARN(ZS_LABEL_OVERFLOW); + p--; {goto st268;} + } + } + goto st316; +tr1062: + { + s->dname = s->r_data; + } + { + s->item_length_position = 0; + s->dname_tmp_length = 0; + } + { + s->item_length = 0; + s->item_length_position = s->dname_tmp_length++; + } + { + // Check for maximum dname label length. + if (s->item_length < ZS_MAX_LABEL_LENGTH) { + (s->dname)[s->dname_tmp_length++] = (*p); + s->item_length++; + } else { + WARN(ZS_LABEL_OVERFLOW); + p--; {goto st268;} + } + } + goto st316; +st316: + if ( ++p == pe ) + goto _test_eof316; +case 316: + switch( (*p) ) { + case 42: goto tr1069; + case 46: goto tr1070; + case 92: goto st320; + case 95: goto tr1069; + } + if ( (*p) < 65 ) { + if ( 45 <= (*p) && (*p) <= 57 ) + goto tr1069; + } else if ( (*p) > 90 ) { + if ( 97 <= (*p) && (*p) <= 122 ) + goto tr1069; + } else + goto tr1069; + goto tr1068; +tr1070: + { + // Check for maximum dname length overflow after each label. + // (at least the next label length must follow). + if (s->dname_tmp_length < ZS_MAX_DNAME_LENGTH) { + (s->dname)[s->item_length_position] = + (uint8_t)(s->item_length); + } else { + WARN(ZS_DNAME_OVERFLOW); + p--; {goto st268;} + } + } + goto st317; +tr1089: + { + s->dname_tmp_length++; + } + { + // Check for maximum dname length overflow after each label. + // (at least the next label length must follow). + if (s->dname_tmp_length < ZS_MAX_DNAME_LENGTH) { + (s->dname)[s->item_length_position] = + (uint8_t)(s->item_length); + } else { + WARN(ZS_DNAME_OVERFLOW); + p--; {goto st268;} + } + } + goto st317; +st317: + if ( ++p == pe ) + goto _test_eof317; +case 317: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr1072; + case 32: goto tr1072; + case 40: goto tr1073; + case 41: goto tr1074; + case 42: goto tr1075; + case 45: goto tr1075; + case 92: goto tr1076; + case 95: goto tr1075; + case 778: goto tr1077; + case 827: goto tr1078; + case 1034: goto tr1077; + case 1083: goto tr1078; + } + if ( _widec < 65 ) { + if ( 47 <= _widec && _widec <= 57 ) + goto tr1075; + } else if ( _widec > 90 ) { + if ( 97 <= _widec && _widec <= 122 ) + goto tr1075; + } else + goto tr1075; + goto tr1058; +tr1080: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st318; +tr1081: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st318; +tr1072: + { + // Enough room for the terminal label is guaranteed (_label_exit). + (s->dname)[s->dname_tmp_length++] = 0; + } + { + s->r_data_length = s->dname_tmp_length; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st318; +tr1073: + { + // Enough room for the terminal label is guaranteed (_label_exit). + (s->dname)[s->dname_tmp_length++] = 0; + } + { + s->r_data_length = s->dname_tmp_length; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st318; +tr1074: + { + // Enough room for the terminal label is guaranteed (_label_exit). + (s->dname)[s->dname_tmp_length++] = 0; + } + { + s->r_data_length = s->dname_tmp_length; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st318; +st318: + if ( ++p == pe ) + goto _test_eof318; +case 318: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st318; + case 32: goto st318; + case 40: goto tr1080; + case 41: goto tr1081; + case 778: goto tr1065; + case 827: goto tr1066; + case 1034: goto tr1065; + case 1083: goto tr1066; + } + goto tr85; +tr1055: + cs = 1143; + { + size_t len = rdata_tail - s->r_data; + if (len >= sizeof(s->include_filename)) { + ERR(ZS_BAD_INCLUDE_FILENAME); + p--; {goto st268;} + } + + // Store zero terminated include filename. + memcpy(s->include_filename, s->r_data, len); + s->include_filename[len] = '\0'; + + // For detection whether origin is not present. + s->dname = NULL; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + // Extend relative file path. + if (s->include_filename[0] != '/') { + int ret = snprintf((char *)(s->buffer), sizeof(s->buffer), + "%s/%s", s->path, s->include_filename); + if (ret <= 0 || ret > sizeof(s->buffer)) { + ERR(ZS_BAD_INCLUDE_FILENAME); + p--; {goto st268;} + } + memcpy(s->include_filename, s->buffer, ret); + } + + // Origin conversion from wire to text form in \DDD notation. + if (s->dname == NULL) { // Use current origin. + wire_dname_to_str(s->zone_origin, + s->zone_origin_length, + (char *)s->buffer); + } else { // Use specified origin. + wire_dname_to_str(s->r_data, + s->r_data_length, + (char *)s->buffer); + } + + // Let the caller to solve the include. + if (s->process.automatic) { + // Create new scanner for included zone file. + zs_scanner_t *ss = malloc(sizeof(zs_scanner_t)); + if (ss == NULL) { + ERR(ZS_UNPROCESSED_INCLUDE); + p--; {goto st268;} + } + + // Parse included zone file. + if (zs_init(ss, (char *)s->buffer, s->default_class, + s->default_ttl) != 0 || + zs_set_input_file(ss, (char *)(s->include_filename)) != 0 || + zs_set_processing(ss, s->process.record, s->process.error, + s->process.data) != 0 || + zs_parse_all(ss) != 0) { + // File internal errors are handled by error callback. + if (ss->error.counter > 0) { + s->error.counter += ss->error.counter; + ERR(ZS_UNPROCESSED_INCLUDE); + // General include file error. + } else { + ERR(ss->error.code); + } + zs_deinit(ss); + free(ss); + p--; {goto st268;} + } + zs_deinit(ss); + free(ss); + } else { + s->state = ZS_STATE_INCLUDE; + p--; cs = 1127; {p++; goto _out;} + } + } + { + p--; {cs = stack[--top];goto _again;} + } + { + s->line_counter++; + } + goto _again; +tr1065: + cs = 1143; + { + // Extend relative file path. + if (s->include_filename[0] != '/') { + int ret = snprintf((char *)(s->buffer), sizeof(s->buffer), + "%s/%s", s->path, s->include_filename); + if (ret <= 0 || ret > sizeof(s->buffer)) { + ERR(ZS_BAD_INCLUDE_FILENAME); + p--; {goto st268;} + } + memcpy(s->include_filename, s->buffer, ret); + } + + // Origin conversion from wire to text form in \DDD notation. + if (s->dname == NULL) { // Use current origin. + wire_dname_to_str(s->zone_origin, + s->zone_origin_length, + (char *)s->buffer); + } else { // Use specified origin. + wire_dname_to_str(s->r_data, + s->r_data_length, + (char *)s->buffer); + } + + // Let the caller to solve the include. + if (s->process.automatic) { + // Create new scanner for included zone file. + zs_scanner_t *ss = malloc(sizeof(zs_scanner_t)); + if (ss == NULL) { + ERR(ZS_UNPROCESSED_INCLUDE); + p--; {goto st268;} + } + + // Parse included zone file. + if (zs_init(ss, (char *)s->buffer, s->default_class, + s->default_ttl) != 0 || + zs_set_input_file(ss, (char *)(s->include_filename)) != 0 || + zs_set_processing(ss, s->process.record, s->process.error, + s->process.data) != 0 || + zs_parse_all(ss) != 0) { + // File internal errors are handled by error callback. + if (ss->error.counter > 0) { + s->error.counter += ss->error.counter; + ERR(ZS_UNPROCESSED_INCLUDE); + // General include file error. + } else { + ERR(ss->error.code); + } + zs_deinit(ss); + free(ss); + p--; {goto st268;} + } + zs_deinit(ss); + free(ss); + } else { + s->state = ZS_STATE_INCLUDE; + p--; cs = 1127; {p++; goto _out;} + } + } + { + p--; {cs = stack[--top];goto _again;} + } + { + s->line_counter++; + } + goto _again; +tr1077: + cs = 1143; + { + // Enough room for the terminal label is guaranteed (_label_exit). + (s->dname)[s->dname_tmp_length++] = 0; + } + { + s->r_data_length = s->dname_tmp_length; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + // Extend relative file path. + if (s->include_filename[0] != '/') { + int ret = snprintf((char *)(s->buffer), sizeof(s->buffer), + "%s/%s", s->path, s->include_filename); + if (ret <= 0 || ret > sizeof(s->buffer)) { + ERR(ZS_BAD_INCLUDE_FILENAME); + p--; {goto st268;} + } + memcpy(s->include_filename, s->buffer, ret); + } + + // Origin conversion from wire to text form in \DDD notation. + if (s->dname == NULL) { // Use current origin. + wire_dname_to_str(s->zone_origin, + s->zone_origin_length, + (char *)s->buffer); + } else { // Use specified origin. + wire_dname_to_str(s->r_data, + s->r_data_length, + (char *)s->buffer); + } + + // Let the caller to solve the include. + if (s->process.automatic) { + // Create new scanner for included zone file. + zs_scanner_t *ss = malloc(sizeof(zs_scanner_t)); + if (ss == NULL) { + ERR(ZS_UNPROCESSED_INCLUDE); + p--; {goto st268;} + } + + // Parse included zone file. + if (zs_init(ss, (char *)s->buffer, s->default_class, + s->default_ttl) != 0 || + zs_set_input_file(ss, (char *)(s->include_filename)) != 0 || + zs_set_processing(ss, s->process.record, s->process.error, + s->process.data) != 0 || + zs_parse_all(ss) != 0) { + // File internal errors are handled by error callback. + if (ss->error.counter > 0) { + s->error.counter += ss->error.counter; + ERR(ZS_UNPROCESSED_INCLUDE); + // General include file error. + } else { + ERR(ss->error.code); + } + zs_deinit(ss); + free(ss); + p--; {goto st268;} + } + zs_deinit(ss); + free(ss); + } else { + s->state = ZS_STATE_INCLUDE; + p--; cs = 1127; {p++; goto _out;} + } + } + { + p--; {cs = stack[--top];goto _again;} + } + { + s->line_counter++; + } + goto _again; +tr1083: + cs = 1143; + { + s->buffer[s->buffer_length++] = 0; + } + { + // Extend relative file path. + if (s->include_filename[0] != '/') { + int ret = snprintf((char *)(s->buffer), sizeof(s->buffer), + "%s/%s", s->path, s->include_filename); + if (ret <= 0 || ret > sizeof(s->buffer)) { + ERR(ZS_BAD_INCLUDE_FILENAME); + p--; {goto st268;} + } + memcpy(s->include_filename, s->buffer, ret); + } + + // Origin conversion from wire to text form in \DDD notation. + if (s->dname == NULL) { // Use current origin. + wire_dname_to_str(s->zone_origin, + s->zone_origin_length, + (char *)s->buffer); + } else { // Use specified origin. + wire_dname_to_str(s->r_data, + s->r_data_length, + (char *)s->buffer); + } + + // Let the caller to solve the include. + if (s->process.automatic) { + // Create new scanner for included zone file. + zs_scanner_t *ss = malloc(sizeof(zs_scanner_t)); + if (ss == NULL) { + ERR(ZS_UNPROCESSED_INCLUDE); + p--; {goto st268;} + } + + // Parse included zone file. + if (zs_init(ss, (char *)s->buffer, s->default_class, + s->default_ttl) != 0 || + zs_set_input_file(ss, (char *)(s->include_filename)) != 0 || + zs_set_processing(ss, s->process.record, s->process.error, + s->process.data) != 0 || + zs_parse_all(ss) != 0) { + // File internal errors are handled by error callback. + if (ss->error.counter > 0) { + s->error.counter += ss->error.counter; + ERR(ZS_UNPROCESSED_INCLUDE); + // General include file error. + } else { + ERR(ss->error.code); + } + zs_deinit(ss); + free(ss); + p--; {goto st268;} + } + zs_deinit(ss); + free(ss); + } else { + s->state = ZS_STATE_INCLUDE; + p--; cs = 1127; {p++; goto _out;} + } + } + { + p--; {cs = stack[--top];goto _again;} + } + { + s->line_counter++; + } + goto _again; +st1143: + if ( ++p == pe ) + goto _test_eof1143; +case 1143: + goto st0; +tr1066: + { + s->buffer_length = 0; + } + goto st319; +tr1082: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st319; +tr1056: + { + size_t len = rdata_tail - s->r_data; + if (len >= sizeof(s->include_filename)) { + ERR(ZS_BAD_INCLUDE_FILENAME); + p--; {goto st268;} + } + + // Store zero terminated include filename. + memcpy(s->include_filename, s->r_data, len); + s->include_filename[len] = '\0'; + + // For detection whether origin is not present. + s->dname = NULL; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + s->buffer_length = 0; + } + goto st319; +tr1078: + { + // Enough room for the terminal label is guaranteed (_label_exit). + (s->dname)[s->dname_tmp_length++] = 0; + } + { + s->r_data_length = s->dname_tmp_length; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + { + s->buffer_length = 0; + } + goto st319; +st319: + if ( ++p == pe ) + goto _test_eof319; +case 319: + if ( (*p) == 10 ) + goto tr1083; + goto tr1082; +tr1076: + { + s->item_length = 0; + s->item_length_position = s->dname_tmp_length++; + } + goto st320; +tr1090: + { + s->dname_tmp_length++; + } + goto st320; +tr1064: + { + s->dname = s->r_data; + } + { + s->item_length_position = 0; + s->dname_tmp_length = 0; + } + { + s->item_length = 0; + s->item_length_position = s->dname_tmp_length++; + } + goto st320; +st320: + if ( ++p == pe ) + goto _test_eof320; +case 320: + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr1084; + goto tr1069; +tr1084: + { + if (s->item_length < ZS_MAX_LABEL_LENGTH) { + (s->dname)[s->dname_tmp_length] = 0; + s->item_length++; + } else { + WARN(ZS_LABEL_OVERFLOW); + p--; {goto st268;} + } + } + { + (s->dname)[s->dname_tmp_length] *= 10; + (s->dname)[s->dname_tmp_length] += digit_to_num[(uint8_t)(*p)]; + } + goto st321; +st321: + if ( ++p == pe ) + goto _test_eof321; +case 321: + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr1086; + goto tr1085; +tr1086: + { + (s->dname)[s->dname_tmp_length] *= 10; + (s->dname)[s->dname_tmp_length] += digit_to_num[(uint8_t)(*p)]; + } + goto st322; +st322: + if ( ++p == pe ) + goto _test_eof322; +case 322: + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr1087; + goto tr1085; +tr1087: + { + (s->dname)[s->dname_tmp_length] *= 10; + (s->dname)[s->dname_tmp_length] += digit_to_num[(uint8_t)(*p)]; + } + goto st323; +st323: + if ( ++p == pe ) + goto _test_eof323; +case 323: + switch( (*p) ) { + case 42: goto tr1088; + case 46: goto tr1089; + case 92: goto tr1090; + case 95: goto tr1088; + } + if ( (*p) < 65 ) { + if ( 45 <= (*p) && (*p) <= 57 ) + goto tr1088; + } else if ( (*p) > 90 ) { + if ( 97 <= (*p) && (*p) <= 122 ) + goto tr1088; + } else + goto tr1088; + goto tr1085; +tr1063: + { + s->dname = s->r_data; + } + { + s->item_length_position = 0; + s->dname_tmp_length = 0; + } + goto st324; +st324: + if ( ++p == pe ) + goto _test_eof324; +case 324: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr1072; + case 32: goto tr1072; + case 40: goto tr1073; + case 41: goto tr1074; + case 778: goto tr1077; + case 827: goto tr1078; + case 1034: goto tr1077; + case 1083: goto tr1078; + } + goto tr1058; +tr1067: + { + s->buffer_length = 0; + } + goto st325; +tr1091: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st325; +tr1057: + { + size_t len = rdata_tail - s->r_data; + if (len >= sizeof(s->include_filename)) { + ERR(ZS_BAD_INCLUDE_FILENAME); + p--; {goto st268;} + } + + // Store zero terminated include filename. + memcpy(s->include_filename, s->r_data, len); + s->include_filename[len] = '\0'; + + // For detection whether origin is not present. + s->dname = NULL; + } + { + s->buffer_length = 0; + } + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + goto st325; +st325: + if ( ++p == pe ) + goto _test_eof325; +case 325: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 778: goto tr1083; + case 1034: goto tr1092; + } + if ( _widec > 895 ) { + if ( 896 <= _widec && _widec <= 1151 ) + goto tr1091; + } else if ( _widec >= 640 ) + goto tr1082; + goto tr85; +tr1092: + cs = 1144; + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + { + // Extend relative file path. + if (s->include_filename[0] != '/') { + int ret = snprintf((char *)(s->buffer), sizeof(s->buffer), + "%s/%s", s->path, s->include_filename); + if (ret <= 0 || ret > sizeof(s->buffer)) { + ERR(ZS_BAD_INCLUDE_FILENAME); + p--; {goto st268;} + } + memcpy(s->include_filename, s->buffer, ret); + } + + // Origin conversion from wire to text form in \DDD notation. + if (s->dname == NULL) { // Use current origin. + wire_dname_to_str(s->zone_origin, + s->zone_origin_length, + (char *)s->buffer); + } else { // Use specified origin. + wire_dname_to_str(s->r_data, + s->r_data_length, + (char *)s->buffer); + } + + // Let the caller to solve the include. + if (s->process.automatic) { + // Create new scanner for included zone file. + zs_scanner_t *ss = malloc(sizeof(zs_scanner_t)); + if (ss == NULL) { + ERR(ZS_UNPROCESSED_INCLUDE); + p--; {goto st268;} + } + + // Parse included zone file. + if (zs_init(ss, (char *)s->buffer, s->default_class, + s->default_ttl) != 0 || + zs_set_input_file(ss, (char *)(s->include_filename)) != 0 || + zs_set_processing(ss, s->process.record, s->process.error, + s->process.data) != 0 || + zs_parse_all(ss) != 0) { + // File internal errors are handled by error callback. + if (ss->error.counter > 0) { + s->error.counter += ss->error.counter; + ERR(ZS_UNPROCESSED_INCLUDE); + // General include file error. + } else { + ERR(ss->error.code); + } + zs_deinit(ss); + free(ss); + p--; {goto st268;} + } + zs_deinit(ss); + free(ss); + } else { + s->state = ZS_STATE_INCLUDE; + p--; cs = 1127; {p++; goto _out;} + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto _again; +st1144: + if ( ++p == pe ) + goto _test_eof1144; +case 1144: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st326; + case 32: goto st326; + case 40: goto tr1094; + case 41: goto tr1095; + case 42: goto tr1062; + case 46: goto tr1063; + case 92: goto tr1064; + case 95: goto tr1062; + case 1034: goto tr1096; + case 1083: goto tr1097; + } + if ( _widec < 65 ) { + if ( 45 <= _widec && _widec <= 57 ) + goto tr1062; + } else if ( _widec > 90 ) { + if ( 97 <= _widec && _widec <= 122 ) + goto tr1062; + } else + goto tr1062; + goto tr1068; +tr1094: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st326; +tr1095: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st326; +tr1096: + { + s->line_counter++; + } + goto st326; +tr1099: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st326; +st326: + if ( ++p == pe ) + goto _test_eof326; +case 326: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st326; + case 32: goto st326; + case 40: goto tr1094; + case 41: goto tr1095; + case 42: goto tr1062; + case 46: goto tr1063; + case 92: goto tr1064; + case 95: goto tr1062; + case 1034: goto tr1096; + case 1083: goto tr1097; + } + if ( _widec < 65 ) { + if ( 45 <= _widec && _widec <= 57 ) + goto tr1062; + } else if ( _widec > 90 ) { + if ( 97 <= _widec && _widec <= 122 ) + goto tr1062; + } else + goto tr1062; + goto tr1068; +tr1097: + { + s->buffer_length = 0; + } + goto st327; +tr1098: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st327; +st327: + if ( ++p == pe ) + goto _test_eof327; +case 327: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr1099; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr1098; + goto st0; +tr1048: + { + s->buffer_length = 0; + } + goto st328; +tr1100: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st328; +st328: + if ( ++p == pe ) + goto _test_eof328; +case 328: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr1101; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr1100; + goto st0; +st329: + if ( ++p == pe ) + goto _test_eof329; +case 329: + if ( (*p) == 43 ) + goto tr1103; + if ( (*p) < 65 ) { + if ( 47 <= (*p) && (*p) <= 57 ) + goto tr1103; + } else if ( (*p) > 90 ) { + if ( 97 <= (*p) && (*p) <= 122 ) + goto tr1103; + } else + goto tr1103; + goto tr1102; +tr1103: + { + if (rdata_tail <= rdata_stop) { + *rdata_tail = first_base64_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + goto st330; +st330: + if ( ++p == pe ) + goto _test_eof330; +case 330: + if ( (*p) == 43 ) + goto tr1104; + if ( (*p) < 65 ) { + if ( 47 <= (*p) && (*p) <= 57 ) + goto tr1104; + } else if ( (*p) > 90 ) { + if ( 97 <= (*p) && (*p) <= 122 ) + goto tr1104; + } else + goto tr1104; + goto tr1102; +tr1104: + { + *(rdata_tail++) += second_left_base64_to_num[(uint8_t)(*p)]; + + if (rdata_tail <= rdata_stop) { + *rdata_tail = second_right_base64_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + goto st331; +st331: + if ( ++p == pe ) + goto _test_eof331; +case 331: + switch( (*p) ) { + case 43: goto tr1105; + case 61: goto st335; + } + if ( (*p) < 65 ) { + if ( 47 <= (*p) && (*p) <= 57 ) + goto tr1105; + } else if ( (*p) > 90 ) { + if ( 97 <= (*p) && (*p) <= 122 ) + goto tr1105; + } else + goto tr1105; + goto tr1102; +tr1105: + { + *(rdata_tail++) += third_left_base64_to_num[(uint8_t)(*p)]; + + if (rdata_tail <= rdata_stop) { + *rdata_tail = third_right_base64_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + goto st332; +st332: + if ( ++p == pe ) + goto _test_eof332; +case 332: + switch( (*p) ) { + case 43: goto tr1107; + case 61: goto st333; + } + if ( (*p) < 65 ) { + if ( 47 <= (*p) && (*p) <= 57 ) + goto tr1107; + } else if ( (*p) > 90 ) { + if ( 97 <= (*p) && (*p) <= 122 ) + goto tr1107; + } else + goto tr1107; + goto tr1102; +tr1109: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st333; +tr1110: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st333; +tr1111: + { + s->line_counter++; + } + goto st333; +tr1117: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st333; +tr1107: + { + *(rdata_tail++) += fourth_base64_to_num[(uint8_t)(*p)]; + } + goto st333; +st333: + if ( ++p == pe ) + goto _test_eof333; +case 333: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto st333; + case 32: goto st333; + case 40: goto tr1109; + case 41: goto tr1110; + case 43: goto tr1103; + case 2058: goto tr1111; + case 2107: goto tr1112; + case 2314: goto tr1113; + case 2363: goto tr1113; + case 2570: goto tr1114; + case 2619: goto tr1115; + } + if ( _widec < 65 ) { + if ( 47 <= _widec && _widec <= 57 ) + goto tr1103; + } else if ( _widec > 90 ) { + if ( 97 <= _widec && _widec <= 122 ) + goto tr1103; + } else + goto tr1103; + goto tr1102; +tr1112: + { + s->buffer_length = 0; + } + goto st334; +tr1116: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st334; +st334: + if ( ++p == pe ) + goto _test_eof334; +case 334: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr1117; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr1116; + goto tr1102; +tr1113: + { + p--; {cs = stack[--top];goto _again;} + } + goto st1145; +st1145: + if ( ++p == pe ) + goto _test_eof1145; +case 1145: + goto st0; +tr1114: + { + s->line_counter++; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1146; +st1146: + if ( ++p == pe ) + goto _test_eof1146; +case 1146: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto st333; + case 32: goto st333; + case 40: goto tr1109; + case 41: goto tr1110; + case 43: goto tr1103; + case 2058: goto tr1111; + case 2107: goto tr1112; + case 2314: goto tr1113; + case 2363: goto tr1113; + case 2570: goto tr1114; + case 2619: goto tr1115; + } + if ( _widec < 65 ) { + if ( 47 <= _widec && _widec <= 57 ) + goto tr1103; + } else if ( _widec > 90 ) { + if ( 97 <= _widec && _widec <= 122 ) + goto tr1103; + } else + goto tr1103; + goto tr1102; +tr1115: + { + s->buffer_length = 0; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1147; +st1147: + if ( ++p == pe ) + goto _test_eof1147; +case 1147: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr1117; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr1116; + goto tr1102; +st335: + if ( ++p == pe ) + goto _test_eof335; +case 335: + if ( (*p) == 61 ) + goto st333; + goto tr1102; +st336: + if ( ++p == pe ) + goto _test_eof336; +case 336: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr1119; + case 32: goto tr1119; + case 40: goto tr1120; + case 41: goto tr1121; + case 2058: goto tr1122; + case 2107: goto tr1123; + case 2314: goto tr1124; + case 2363: goto tr1124; + case 2570: goto tr1125; + case 2619: goto tr1126; + } + goto tr1118; +tr1128: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st337; +tr1129: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st337; +tr1145: + { + s->line_counter++; + } + goto st337; +tr1172: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st337; +tr1119: + { + memset(s->windows, 0, sizeof(s->windows)); + s->last_window = -1; + } + goto st337; +tr1120: + { + memset(s->windows, 0, sizeof(s->windows)); + s->last_window = -1; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st337; +tr1121: + { + memset(s->windows, 0, sizeof(s->windows)); + s->last_window = -1; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st337; +tr1122: + { + memset(s->windows, 0, sizeof(s->windows)); + s->last_window = -1; + } + { + s->line_counter++; + } + goto st337; +tr1150: + { window_add_bit(KNOT_RRTYPE_A, s); } + goto st337; +tr1151: + { window_add_bit(KNOT_RRTYPE_A, s); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st337; +tr1152: + { window_add_bit(KNOT_RRTYPE_A, s); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st337; +tr1156: + { window_add_bit(KNOT_RRTYPE_A, s); } + { + s->line_counter++; + } + goto st337; +tr1163: + { window_add_bit(KNOT_RRTYPE_AAAA, s); } + goto st337; +tr1164: + { window_add_bit(KNOT_RRTYPE_AAAA, s); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st337; +tr1165: + { window_add_bit(KNOT_RRTYPE_AAAA, s); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st337; +tr1166: + { window_add_bit(KNOT_RRTYPE_AAAA, s); } + { + s->line_counter++; + } + goto st337; +tr1178: + { window_add_bit(KNOT_RRTYPE_CAA, s); } + goto st337; +tr1179: + { window_add_bit(KNOT_RRTYPE_CAA, s); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st337; +tr1180: + { window_add_bit(KNOT_RRTYPE_CAA, s); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st337; +tr1181: + { window_add_bit(KNOT_RRTYPE_CAA, s); } + { + s->line_counter++; + } + goto st337; +tr1192: + { window_add_bit(KNOT_RRTYPE_CDNSKEY, s); } + goto st337; +tr1193: + { window_add_bit(KNOT_RRTYPE_CDNSKEY, s); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st337; +tr1194: + { window_add_bit(KNOT_RRTYPE_CDNSKEY, s); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st337; +tr1195: + { window_add_bit(KNOT_RRTYPE_CDNSKEY, s); } + { + s->line_counter++; + } + goto st337; +tr1200: + { window_add_bit(KNOT_RRTYPE_CDS, s); } + goto st337; +tr1201: + { window_add_bit(KNOT_RRTYPE_CDS, s); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st337; +tr1202: + { window_add_bit(KNOT_RRTYPE_CDS, s); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st337; +tr1203: + { window_add_bit(KNOT_RRTYPE_CDS, s); } + { + s->line_counter++; + } + goto st337; +tr1210: + { window_add_bit(KNOT_RRTYPE_CERT, s); } + goto st337; +tr1211: + { window_add_bit(KNOT_RRTYPE_CERT, s); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st337; +tr1212: + { window_add_bit(KNOT_RRTYPE_CERT, s); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st337; +tr1213: + { window_add_bit(KNOT_RRTYPE_CERT, s); } + { + s->line_counter++; + } + goto st337; +tr1221: + { window_add_bit(KNOT_RRTYPE_CNAME, s); } + goto st337; +tr1222: + { window_add_bit(KNOT_RRTYPE_CNAME, s); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st337; +tr1223: + { window_add_bit(KNOT_RRTYPE_CNAME, s); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st337; +tr1224: + { window_add_bit(KNOT_RRTYPE_CNAME, s); } + { + s->line_counter++; + } + goto st337; +tr1235: + { window_add_bit(KNOT_RRTYPE_DHCID, s); } + goto st337; +tr1236: + { window_add_bit(KNOT_RRTYPE_DHCID, s); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st337; +tr1237: + { window_add_bit(KNOT_RRTYPE_DHCID, s); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st337; +tr1238: + { window_add_bit(KNOT_RRTYPE_DHCID, s); } + { + s->line_counter++; + } + goto st337; +tr1247: + { window_add_bit(KNOT_RRTYPE_DNAME, s); } + goto st337; +tr1248: + { window_add_bit(KNOT_RRTYPE_DNAME, s); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st337; +tr1249: + { window_add_bit(KNOT_RRTYPE_DNAME, s); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st337; +tr1250: + { window_add_bit(KNOT_RRTYPE_DNAME, s); } + { + s->line_counter++; + } + goto st337; +tr1258: + { window_add_bit(KNOT_RRTYPE_DNSKEY, s); } + goto st337; +tr1259: + { window_add_bit(KNOT_RRTYPE_DNSKEY, s); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st337; +tr1260: + { window_add_bit(KNOT_RRTYPE_DNSKEY, s); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st337; +tr1261: + { window_add_bit(KNOT_RRTYPE_DNSKEY, s); } + { + s->line_counter++; + } + goto st337; +tr1266: + { window_add_bit(KNOT_RRTYPE_DS, s); } + goto st337; +tr1267: + { window_add_bit(KNOT_RRTYPE_DS, s); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st337; +tr1268: + { window_add_bit(KNOT_RRTYPE_DS, s); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st337; +tr1269: + { window_add_bit(KNOT_RRTYPE_DS, s); } + { + s->line_counter++; + } + goto st337; +tr1279: + { window_add_bit(KNOT_RRTYPE_EUI48, s); } + goto st337; +tr1280: + { window_add_bit(KNOT_RRTYPE_EUI48, s); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st337; +tr1281: + { window_add_bit(KNOT_RRTYPE_EUI48, s); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st337; +tr1282: + { window_add_bit(KNOT_RRTYPE_EUI48, s); } + { + s->line_counter++; + } + goto st337; +tr1288: + { window_add_bit(KNOT_RRTYPE_EUI64, s); } + goto st337; +tr1289: + { window_add_bit(KNOT_RRTYPE_EUI64, s); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st337; +tr1290: + { window_add_bit(KNOT_RRTYPE_EUI64, s); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st337; +tr1291: + { window_add_bit(KNOT_RRTYPE_EUI64, s); } + { + s->line_counter++; + } + goto st337; +tr1300: + { window_add_bit(KNOT_RRTYPE_HINFO, s); } + goto st337; +tr1301: + { window_add_bit(KNOT_RRTYPE_HINFO, s); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st337; +tr1302: + { window_add_bit(KNOT_RRTYPE_HINFO, s); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st337; +tr1303: + { window_add_bit(KNOT_RRTYPE_HINFO, s); } + { + s->line_counter++; + } + goto st337; +tr1315: + { window_add_bit(KNOT_RRTYPE_IPSECKEY, s); } + goto st337; +tr1316: + { window_add_bit(KNOT_RRTYPE_IPSECKEY, s); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st337; +tr1317: + { window_add_bit(KNOT_RRTYPE_IPSECKEY, s); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st337; +tr1318: + { window_add_bit(KNOT_RRTYPE_IPSECKEY, s); } + { + s->line_counter++; + } + goto st337; +tr1326: + { window_add_bit(KNOT_RRTYPE_KEY, s); } + goto st337; +tr1327: + { window_add_bit(KNOT_RRTYPE_KEY, s); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st337; +tr1328: + { window_add_bit(KNOT_RRTYPE_KEY, s); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st337; +tr1329: + { window_add_bit(KNOT_RRTYPE_KEY, s); } + { + s->line_counter++; + } + goto st337; +tr1334: + { window_add_bit(KNOT_RRTYPE_KX, s); } + goto st337; +tr1335: + { window_add_bit(KNOT_RRTYPE_KX, s); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st337; +tr1336: + { window_add_bit(KNOT_RRTYPE_KX, s); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st337; +tr1337: + { window_add_bit(KNOT_RRTYPE_KX, s); } + { + s->line_counter++; + } + goto st337; +tr1347: + { window_add_bit(KNOT_RRTYPE_L32, s); } + goto st337; +tr1348: + { window_add_bit(KNOT_RRTYPE_L32, s); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st337; +tr1349: + { window_add_bit(KNOT_RRTYPE_L32, s); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st337; +tr1350: + { window_add_bit(KNOT_RRTYPE_L32, s); } + { + s->line_counter++; + } + goto st337; +tr1356: + { window_add_bit(KNOT_RRTYPE_L64, s); } + goto st337; +tr1357: + { window_add_bit(KNOT_RRTYPE_L64, s); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st337; +tr1358: + { window_add_bit(KNOT_RRTYPE_L64, s); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st337; +tr1359: + { window_add_bit(KNOT_RRTYPE_L64, s); } + { + s->line_counter++; + } + goto st337; +tr1365: + { window_add_bit(KNOT_RRTYPE_LOC, s); } + goto st337; +tr1366: + { window_add_bit(KNOT_RRTYPE_LOC, s); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st337; +tr1367: + { window_add_bit(KNOT_RRTYPE_LOC, s); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st337; +tr1368: + { window_add_bit(KNOT_RRTYPE_LOC, s); } + { + s->line_counter++; + } + goto st337; +tr1373: + { window_add_bit(KNOT_RRTYPE_LP, s); } + goto st337; +tr1374: + { window_add_bit(KNOT_RRTYPE_LP, s); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st337; +tr1375: + { window_add_bit(KNOT_RRTYPE_LP, s); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st337; +tr1376: + { window_add_bit(KNOT_RRTYPE_LP, s); } + { + s->line_counter++; + } + goto st337; +tr1386: + { window_add_bit(KNOT_RRTYPE_MINFO, s); } + goto st337; +tr1387: + { window_add_bit(KNOT_RRTYPE_MINFO, s); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st337; +tr1388: + { window_add_bit(KNOT_RRTYPE_MINFO, s); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st337; +tr1389: + { window_add_bit(KNOT_RRTYPE_MINFO, s); } + { + s->line_counter++; + } + goto st337; +tr1394: + { window_add_bit(KNOT_RRTYPE_MX, s); } + goto st337; +tr1395: + { window_add_bit(KNOT_RRTYPE_MX, s); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st337; +tr1396: + { window_add_bit(KNOT_RRTYPE_MX, s); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st337; +tr1397: + { window_add_bit(KNOT_RRTYPE_MX, s); } + { + s->line_counter++; + } + goto st337; +tr1408: + { window_add_bit(KNOT_RRTYPE_NAPTR, s); } + goto st337; +tr1409: + { window_add_bit(KNOT_RRTYPE_NAPTR, s); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st337; +tr1410: + { window_add_bit(KNOT_RRTYPE_NAPTR, s); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st337; +tr1411: + { window_add_bit(KNOT_RRTYPE_NAPTR, s); } + { + s->line_counter++; + } + goto st337; +tr1417: + { window_add_bit(KNOT_RRTYPE_NID, s); } + goto st337; +tr1418: + { window_add_bit(KNOT_RRTYPE_NID, s); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st337; +tr1419: + { window_add_bit(KNOT_RRTYPE_NID, s); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st337; +tr1420: + { window_add_bit(KNOT_RRTYPE_NID, s); } + { + s->line_counter++; + } + goto st337; +tr1425: + { window_add_bit(KNOT_RRTYPE_NS, s); } + goto st337; +tr1426: + { window_add_bit(KNOT_RRTYPE_NS, s); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st337; +tr1427: + { window_add_bit(KNOT_RRTYPE_NS, s); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st337; +tr1429: + { window_add_bit(KNOT_RRTYPE_NS, s); } + { + s->line_counter++; + } + goto st337; +tr1435: + { window_add_bit(KNOT_RRTYPE_NSEC, s); } + goto st337; +tr1436: + { window_add_bit(KNOT_RRTYPE_NSEC, s); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st337; +tr1437: + { window_add_bit(KNOT_RRTYPE_NSEC, s); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st337; +tr1439: + { window_add_bit(KNOT_RRTYPE_NSEC, s); } + { + s->line_counter++; + } + goto st337; +tr1444: + { window_add_bit(KNOT_RRTYPE_NSEC3, s); } + goto st337; +tr1445: + { window_add_bit(KNOT_RRTYPE_NSEC3, s); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st337; +tr1446: + { window_add_bit(KNOT_RRTYPE_NSEC3, s); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st337; +tr1448: + { window_add_bit(KNOT_RRTYPE_NSEC3, s); } + { + s->line_counter++; + } + goto st337; +tr1457: + { window_add_bit(KNOT_RRTYPE_NSEC3PARAM, s); } + goto st337; +tr1458: + { window_add_bit(KNOT_RRTYPE_NSEC3PARAM, s); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st337; +tr1459: + { window_add_bit(KNOT_RRTYPE_NSEC3PARAM, s); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st337; +tr1460: + { window_add_bit(KNOT_RRTYPE_NSEC3PARAM, s); } + { + s->line_counter++; + } + goto st337; +tr1467: + { window_add_bit(KNOT_RRTYPE_PTR, s); } + goto st337; +tr1468: + { window_add_bit(KNOT_RRTYPE_PTR, s); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st337; +tr1469: + { window_add_bit(KNOT_RRTYPE_PTR, s); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st337; +tr1470: + { window_add_bit(KNOT_RRTYPE_PTR, s); } + { + s->line_counter++; + } + goto st337; +tr1478: + { window_add_bit(KNOT_RRTYPE_RP, s); } + goto st337; +tr1479: + { window_add_bit(KNOT_RRTYPE_RP, s); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st337; +tr1480: + { window_add_bit(KNOT_RRTYPE_RP, s); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st337; +tr1481: + { window_add_bit(KNOT_RRTYPE_RP, s); } + { + s->line_counter++; + } + goto st337; +tr1489: + { window_add_bit(KNOT_RRTYPE_RRSIG, s); } + goto st337; +tr1490: + { window_add_bit(KNOT_RRTYPE_RRSIG, s); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st337; +tr1491: + { window_add_bit(KNOT_RRTYPE_RRSIG, s); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st337; +tr1492: + { window_add_bit(KNOT_RRTYPE_RRSIG, s); } + { + s->line_counter++; + } + goto st337; +tr1497: + { window_add_bit(KNOT_RRTYPE_RT, s); } + goto st337; +tr1498: + { window_add_bit(KNOT_RRTYPE_RT, s); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st337; +tr1499: + { window_add_bit(KNOT_RRTYPE_RT, s); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st337; +tr1500: + { window_add_bit(KNOT_RRTYPE_RT, s); } + { + s->line_counter++; + } + goto st337; +tr1510: + { window_add_bit(KNOT_RRTYPE_SOA, s); } + goto st337; +tr1511: + { window_add_bit(KNOT_RRTYPE_SOA, s); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st337; +tr1512: + { window_add_bit(KNOT_RRTYPE_SOA, s); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st337; +tr1513: + { window_add_bit(KNOT_RRTYPE_SOA, s); } + { + s->line_counter++; + } + goto st337; +tr1519: + { window_add_bit(KNOT_RRTYPE_SPF, s); } + goto st337; +tr1520: + { window_add_bit(KNOT_RRTYPE_SPF, s); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st337; +tr1521: + { window_add_bit(KNOT_RRTYPE_SPF, s); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st337; +tr1522: + { window_add_bit(KNOT_RRTYPE_SPF, s); } + { + s->line_counter++; + } + goto st337; +tr1528: + { window_add_bit(KNOT_RRTYPE_SRV, s); } + goto st337; +tr1529: + { window_add_bit(KNOT_RRTYPE_SRV, s); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st337; +tr1530: + { window_add_bit(KNOT_RRTYPE_SRV, s); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st337; +tr1531: + { window_add_bit(KNOT_RRTYPE_SRV, s); } + { + s->line_counter++; + } + goto st337; +tr1539: + { window_add_bit(KNOT_RRTYPE_SSHFP, s); } + goto st337; +tr1540: + { window_add_bit(KNOT_RRTYPE_SSHFP, s); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st337; +tr1541: + { window_add_bit(KNOT_RRTYPE_SSHFP, s); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st337; +tr1542: + { window_add_bit(KNOT_RRTYPE_SSHFP, s); } + { + s->line_counter++; + } + goto st337; +tr1552: + { window_add_bit(KNOT_RRTYPE_TLSA, s); } + goto st337; +tr1553: + { window_add_bit(KNOT_RRTYPE_TLSA, s); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st337; +tr1554: + { window_add_bit(KNOT_RRTYPE_TLSA, s); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st337; +tr1555: + { window_add_bit(KNOT_RRTYPE_TLSA, s); } + { + s->line_counter++; + } + goto st337; +tr1561: + { window_add_bit(KNOT_RRTYPE_TXT, s); } + goto st337; +tr1562: + { window_add_bit(KNOT_RRTYPE_TXT, s); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st337; +tr1563: + { window_add_bit(KNOT_RRTYPE_TXT, s); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st337; +tr1564: + { window_add_bit(KNOT_RRTYPE_TXT, s); } + { + s->line_counter++; + } + goto st337; +tr1572: + { + if (s->number64 <= UINT16_MAX) { + window_add_bit(s->number64, s); + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + goto st337; +tr1573: + { + if (s->number64 <= UINT16_MAX) { + window_add_bit(s->number64, s); + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st337; +tr1574: + { + if (s->number64 <= UINT16_MAX) { + window_add_bit(s->number64, s); + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st337; +tr1576: + { + if (s->number64 <= UINT16_MAX) { + window_add_bit(s->number64, s); + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st337; +tr1583: + { window_add_bit(KNOT_RRTYPE_URI, s); } + goto st337; +tr1584: + { window_add_bit(KNOT_RRTYPE_URI, s); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st337; +tr1585: + { window_add_bit(KNOT_RRTYPE_URI, s); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st337; +tr1586: + { window_add_bit(KNOT_RRTYPE_URI, s); } + { + s->line_counter++; + } + goto st337; +tr1594: + { window_add_bit(KNOT_RRTYPE_AFSDB, s); } + goto st337; +tr1595: + { window_add_bit(KNOT_RRTYPE_AFSDB, s); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st337; +tr1596: + { window_add_bit(KNOT_RRTYPE_AFSDB, s); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st337; +tr1597: + { window_add_bit(KNOT_RRTYPE_AFSDB, s); } + { + s->line_counter++; + } + goto st337; +tr1603: + { window_add_bit(KNOT_RRTYPE_APL, s); } + goto st337; +tr1604: + { window_add_bit(KNOT_RRTYPE_APL, s); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st337; +tr1605: + { window_add_bit(KNOT_RRTYPE_APL, s); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st337; +tr1606: + { window_add_bit(KNOT_RRTYPE_APL, s); } + { + s->line_counter++; + } + goto st337; +st337: + if ( ++p == pe ) + goto _test_eof337; +case 337: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto st337; + case 32: goto st337; + case 40: goto tr1128; + case 41: goto tr1129; + case 65: goto st338; + case 67: goto st343; + case 68: goto st360; + case 69: goto st374; + case 72: goto st381; + case 73: goto st386; + case 75: goto st394; + case 76: goto st398; + case 77: goto st406; + case 78: goto st412; + case 80: goto st428; + case 82: goto st431; + case 83: goto st438; + case 84: goto st449; + case 85: goto st459; + case 97: goto st338; + case 99: goto st343; + case 100: goto st360; + case 101: goto st374; + case 104: goto st381; + case 105: goto st386; + case 107: goto st394; + case 108: goto st398; + case 109: goto st406; + case 110: goto st412; + case 112: goto st428; + case 114: goto st431; + case 115: goto st438; + case 116: goto st449; + case 117: goto st459; + case 2058: goto tr1145; + case 2107: goto tr1146; + case 2314: goto tr1147; + case 2363: goto tr1147; + case 2570: goto tr1148; + case 2619: goto tr1149; + } + goto tr1118; +st338: + if ( ++p == pe ) + goto _test_eof338; +case 338: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr1150; + case 32: goto tr1150; + case 40: goto tr1151; + case 41: goto tr1152; + case 65: goto st339; + case 70: goto st462; + case 80: goto st466; + case 97: goto st339; + case 102: goto st462; + case 112: goto st466; + case 2058: goto tr1156; + case 2107: goto tr1157; + case 2314: goto tr1158; + case 2363: goto tr1158; + case 2570: goto tr1159; + case 2619: goto tr1160; + } + goto tr1118; +st339: + if ( ++p == pe ) + goto _test_eof339; +case 339: + switch( (*p) ) { + case 65: goto st340; + case 97: goto st340; + } + goto tr1118; +st340: + if ( ++p == pe ) + goto _test_eof340; +case 340: + switch( (*p) ) { + case 65: goto st341; + case 97: goto st341; + } + goto tr1118; +st341: + if ( ++p == pe ) + goto _test_eof341; +case 341: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr1163; + case 32: goto tr1163; + case 40: goto tr1164; + case 41: goto tr1165; + case 2058: goto tr1166; + case 2107: goto tr1167; + case 2314: goto tr1168; + case 2363: goto tr1168; + case 2570: goto tr1169; + case 2619: goto tr1170; + } + goto tr1118; +tr1146: + { + s->buffer_length = 0; + } + goto st342; +tr1171: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st342; +tr1123: + { + memset(s->windows, 0, sizeof(s->windows)); + s->last_window = -1; + } + { + s->buffer_length = 0; + } + goto st342; +tr1157: + { window_add_bit(KNOT_RRTYPE_A, s); } + { + s->buffer_length = 0; + } + goto st342; +tr1167: + { window_add_bit(KNOT_RRTYPE_AAAA, s); } + { + s->buffer_length = 0; + } + goto st342; +tr1182: + { window_add_bit(KNOT_RRTYPE_CAA, s); } + { + s->buffer_length = 0; + } + goto st342; +tr1196: + { window_add_bit(KNOT_RRTYPE_CDNSKEY, s); } + { + s->buffer_length = 0; + } + goto st342; +tr1204: + { window_add_bit(KNOT_RRTYPE_CDS, s); } + { + s->buffer_length = 0; + } + goto st342; +tr1214: + { window_add_bit(KNOT_RRTYPE_CERT, s); } + { + s->buffer_length = 0; + } + goto st342; +tr1225: + { window_add_bit(KNOT_RRTYPE_CNAME, s); } + { + s->buffer_length = 0; + } + goto st342; +tr1239: + { window_add_bit(KNOT_RRTYPE_DHCID, s); } + { + s->buffer_length = 0; + } + goto st342; +tr1251: + { window_add_bit(KNOT_RRTYPE_DNAME, s); } + { + s->buffer_length = 0; + } + goto st342; +tr1262: + { window_add_bit(KNOT_RRTYPE_DNSKEY, s); } + { + s->buffer_length = 0; + } + goto st342; +tr1270: + { window_add_bit(KNOT_RRTYPE_DS, s); } + { + s->buffer_length = 0; + } + goto st342; +tr1283: + { window_add_bit(KNOT_RRTYPE_EUI48, s); } + { + s->buffer_length = 0; + } + goto st342; +tr1292: + { window_add_bit(KNOT_RRTYPE_EUI64, s); } + { + s->buffer_length = 0; + } + goto st342; +tr1304: + { window_add_bit(KNOT_RRTYPE_HINFO, s); } + { + s->buffer_length = 0; + } + goto st342; +tr1319: + { window_add_bit(KNOT_RRTYPE_IPSECKEY, s); } + { + s->buffer_length = 0; + } + goto st342; +tr1330: + { window_add_bit(KNOT_RRTYPE_KEY, s); } + { + s->buffer_length = 0; + } + goto st342; +tr1338: + { window_add_bit(KNOT_RRTYPE_KX, s); } + { + s->buffer_length = 0; + } + goto st342; +tr1351: + { window_add_bit(KNOT_RRTYPE_L32, s); } + { + s->buffer_length = 0; + } + goto st342; +tr1360: + { window_add_bit(KNOT_RRTYPE_L64, s); } + { + s->buffer_length = 0; + } + goto st342; +tr1369: + { window_add_bit(KNOT_RRTYPE_LOC, s); } + { + s->buffer_length = 0; + } + goto st342; +tr1377: + { window_add_bit(KNOT_RRTYPE_LP, s); } + { + s->buffer_length = 0; + } + goto st342; +tr1390: + { window_add_bit(KNOT_RRTYPE_MINFO, s); } + { + s->buffer_length = 0; + } + goto st342; +tr1398: + { window_add_bit(KNOT_RRTYPE_MX, s); } + { + s->buffer_length = 0; + } + goto st342; +tr1412: + { window_add_bit(KNOT_RRTYPE_NAPTR, s); } + { + s->buffer_length = 0; + } + goto st342; +tr1421: + { window_add_bit(KNOT_RRTYPE_NID, s); } + { + s->buffer_length = 0; + } + goto st342; +tr1430: + { window_add_bit(KNOT_RRTYPE_NS, s); } + { + s->buffer_length = 0; + } + goto st342; +tr1440: + { window_add_bit(KNOT_RRTYPE_NSEC, s); } + { + s->buffer_length = 0; + } + goto st342; +tr1449: + { window_add_bit(KNOT_RRTYPE_NSEC3, s); } + { + s->buffer_length = 0; + } + goto st342; +tr1461: + { window_add_bit(KNOT_RRTYPE_NSEC3PARAM, s); } + { + s->buffer_length = 0; + } + goto st342; +tr1471: + { window_add_bit(KNOT_RRTYPE_PTR, s); } + { + s->buffer_length = 0; + } + goto st342; +tr1482: + { window_add_bit(KNOT_RRTYPE_RP, s); } + { + s->buffer_length = 0; + } + goto st342; +tr1493: + { window_add_bit(KNOT_RRTYPE_RRSIG, s); } + { + s->buffer_length = 0; + } + goto st342; +tr1501: + { window_add_bit(KNOT_RRTYPE_RT, s); } + { + s->buffer_length = 0; + } + goto st342; +tr1514: + { window_add_bit(KNOT_RRTYPE_SOA, s); } + { + s->buffer_length = 0; + } + goto st342; +tr1523: + { window_add_bit(KNOT_RRTYPE_SPF, s); } + { + s->buffer_length = 0; + } + goto st342; +tr1532: + { window_add_bit(KNOT_RRTYPE_SRV, s); } + { + s->buffer_length = 0; + } + goto st342; +tr1543: + { window_add_bit(KNOT_RRTYPE_SSHFP, s); } + { + s->buffer_length = 0; + } + goto st342; +tr1556: + { window_add_bit(KNOT_RRTYPE_TLSA, s); } + { + s->buffer_length = 0; + } + goto st342; +tr1565: + { window_add_bit(KNOT_RRTYPE_TXT, s); } + { + s->buffer_length = 0; + } + goto st342; +tr1577: + { + if (s->number64 <= UINT16_MAX) { + window_add_bit(s->number64, s); + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st342; +tr1587: + { window_add_bit(KNOT_RRTYPE_URI, s); } + { + s->buffer_length = 0; + } + goto st342; +tr1598: + { window_add_bit(KNOT_RRTYPE_AFSDB, s); } + { + s->buffer_length = 0; + } + goto st342; +tr1607: + { window_add_bit(KNOT_RRTYPE_APL, s); } + { + s->buffer_length = 0; + } + goto st342; +st342: + if ( ++p == pe ) + goto _test_eof342; +case 342: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr1172; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr1171; + goto tr1118; +tr1124: + { + memset(s->windows, 0, sizeof(s->windows)); + s->last_window = -1; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1148; +tr1147: + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1148; +tr1158: + { window_add_bit(KNOT_RRTYPE_A, s); } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1148; +tr1168: + { window_add_bit(KNOT_RRTYPE_AAAA, s); } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1148; +tr1183: + { window_add_bit(KNOT_RRTYPE_CAA, s); } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1148; +tr1197: + { window_add_bit(KNOT_RRTYPE_CDNSKEY, s); } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1148; +tr1205: + { window_add_bit(KNOT_RRTYPE_CDS, s); } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1148; +tr1215: + { window_add_bit(KNOT_RRTYPE_CERT, s); } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1148; +tr1226: + { window_add_bit(KNOT_RRTYPE_CNAME, s); } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1148; +tr1240: + { window_add_bit(KNOT_RRTYPE_DHCID, s); } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1148; +tr1252: + { window_add_bit(KNOT_RRTYPE_DNAME, s); } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1148; +tr1263: + { window_add_bit(KNOT_RRTYPE_DNSKEY, s); } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1148; +tr1271: + { window_add_bit(KNOT_RRTYPE_DS, s); } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1148; +tr1284: + { window_add_bit(KNOT_RRTYPE_EUI48, s); } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1148; +tr1293: + { window_add_bit(KNOT_RRTYPE_EUI64, s); } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1148; +tr1305: + { window_add_bit(KNOT_RRTYPE_HINFO, s); } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1148; +tr1320: + { window_add_bit(KNOT_RRTYPE_IPSECKEY, s); } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1148; +tr1331: + { window_add_bit(KNOT_RRTYPE_KEY, s); } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1148; +tr1339: + { window_add_bit(KNOT_RRTYPE_KX, s); } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1148; +tr1352: + { window_add_bit(KNOT_RRTYPE_L32, s); } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1148; +tr1361: + { window_add_bit(KNOT_RRTYPE_L64, s); } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1148; +tr1370: + { window_add_bit(KNOT_RRTYPE_LOC, s); } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1148; +tr1378: + { window_add_bit(KNOT_RRTYPE_LP, s); } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1148; +tr1391: + { window_add_bit(KNOT_RRTYPE_MINFO, s); } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1148; +tr1399: + { window_add_bit(KNOT_RRTYPE_MX, s); } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1148; +tr1413: + { window_add_bit(KNOT_RRTYPE_NAPTR, s); } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1148; +tr1422: + { window_add_bit(KNOT_RRTYPE_NID, s); } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1148; +tr1431: + { window_add_bit(KNOT_RRTYPE_NS, s); } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1148; +tr1441: + { window_add_bit(KNOT_RRTYPE_NSEC, s); } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1148; +tr1450: + { window_add_bit(KNOT_RRTYPE_NSEC3, s); } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1148; +tr1462: + { window_add_bit(KNOT_RRTYPE_NSEC3PARAM, s); } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1148; +tr1472: + { window_add_bit(KNOT_RRTYPE_PTR, s); } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1148; +tr1483: + { window_add_bit(KNOT_RRTYPE_RP, s); } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1148; +tr1494: + { window_add_bit(KNOT_RRTYPE_RRSIG, s); } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1148; +tr1502: + { window_add_bit(KNOT_RRTYPE_RT, s); } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1148; +tr1515: + { window_add_bit(KNOT_RRTYPE_SOA, s); } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1148; +tr1524: + { window_add_bit(KNOT_RRTYPE_SPF, s); } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1148; +tr1533: + { window_add_bit(KNOT_RRTYPE_SRV, s); } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1148; +tr1544: + { window_add_bit(KNOT_RRTYPE_SSHFP, s); } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1148; +tr1557: + { window_add_bit(KNOT_RRTYPE_TLSA, s); } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1148; +tr1566: + { window_add_bit(KNOT_RRTYPE_TXT, s); } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1148; +tr1578: + { + if (s->number64 <= UINT16_MAX) { + window_add_bit(s->number64, s); + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1148; +tr1588: + { window_add_bit(KNOT_RRTYPE_URI, s); } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1148; +tr1599: + { window_add_bit(KNOT_RRTYPE_AFSDB, s); } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1148; +tr1608: + { window_add_bit(KNOT_RRTYPE_APL, s); } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1148; +st1148: + if ( ++p == pe ) + goto _test_eof1148; +case 1148: + goto st0; +tr1125: + { + memset(s->windows, 0, sizeof(s->windows)); + s->last_window = -1; + } + { + s->line_counter++; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1149; +tr1148: + { + s->line_counter++; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1149; +tr1159: + { window_add_bit(KNOT_RRTYPE_A, s); } + { + s->line_counter++; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1149; +tr1169: + { window_add_bit(KNOT_RRTYPE_AAAA, s); } + { + s->line_counter++; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1149; +tr1184: + { window_add_bit(KNOT_RRTYPE_CAA, s); } + { + s->line_counter++; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1149; +tr1198: + { window_add_bit(KNOT_RRTYPE_CDNSKEY, s); } + { + s->line_counter++; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1149; +tr1206: + { window_add_bit(KNOT_RRTYPE_CDS, s); } + { + s->line_counter++; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1149; +tr1216: + { window_add_bit(KNOT_RRTYPE_CERT, s); } + { + s->line_counter++; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1149; +tr1227: + { window_add_bit(KNOT_RRTYPE_CNAME, s); } + { + s->line_counter++; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1149; +tr1241: + { window_add_bit(KNOT_RRTYPE_DHCID, s); } + { + s->line_counter++; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1149; +tr1253: + { window_add_bit(KNOT_RRTYPE_DNAME, s); } + { + s->line_counter++; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1149; +tr1264: + { window_add_bit(KNOT_RRTYPE_DNSKEY, s); } + { + s->line_counter++; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1149; +tr1272: + { window_add_bit(KNOT_RRTYPE_DS, s); } + { + s->line_counter++; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1149; +tr1285: + { window_add_bit(KNOT_RRTYPE_EUI48, s); } + { + s->line_counter++; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1149; +tr1294: + { window_add_bit(KNOT_RRTYPE_EUI64, s); } + { + s->line_counter++; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1149; +tr1306: + { window_add_bit(KNOT_RRTYPE_HINFO, s); } + { + s->line_counter++; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1149; +tr1321: + { window_add_bit(KNOT_RRTYPE_IPSECKEY, s); } + { + s->line_counter++; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1149; +tr1332: + { window_add_bit(KNOT_RRTYPE_KEY, s); } + { + s->line_counter++; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1149; +tr1340: + { window_add_bit(KNOT_RRTYPE_KX, s); } + { + s->line_counter++; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1149; +tr1353: + { window_add_bit(KNOT_RRTYPE_L32, s); } + { + s->line_counter++; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1149; +tr1362: + { window_add_bit(KNOT_RRTYPE_L64, s); } + { + s->line_counter++; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1149; +tr1371: + { window_add_bit(KNOT_RRTYPE_LOC, s); } + { + s->line_counter++; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1149; +tr1379: + { window_add_bit(KNOT_RRTYPE_LP, s); } + { + s->line_counter++; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1149; +tr1392: + { window_add_bit(KNOT_RRTYPE_MINFO, s); } + { + s->line_counter++; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1149; +tr1400: + { window_add_bit(KNOT_RRTYPE_MX, s); } + { + s->line_counter++; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1149; +tr1414: + { window_add_bit(KNOT_RRTYPE_NAPTR, s); } + { + s->line_counter++; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1149; +tr1423: + { window_add_bit(KNOT_RRTYPE_NID, s); } + { + s->line_counter++; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1149; +tr1432: + { window_add_bit(KNOT_RRTYPE_NS, s); } + { + s->line_counter++; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1149; +tr1442: + { window_add_bit(KNOT_RRTYPE_NSEC, s); } + { + s->line_counter++; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1149; +tr1451: + { window_add_bit(KNOT_RRTYPE_NSEC3, s); } + { + s->line_counter++; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1149; +tr1463: + { window_add_bit(KNOT_RRTYPE_NSEC3PARAM, s); } + { + s->line_counter++; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1149; +tr1473: + { window_add_bit(KNOT_RRTYPE_PTR, s); } + { + s->line_counter++; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1149; +tr1484: + { window_add_bit(KNOT_RRTYPE_RP, s); } + { + s->line_counter++; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1149; +tr1495: + { window_add_bit(KNOT_RRTYPE_RRSIG, s); } + { + s->line_counter++; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1149; +tr1503: + { window_add_bit(KNOT_RRTYPE_RT, s); } + { + s->line_counter++; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1149; +tr1516: + { window_add_bit(KNOT_RRTYPE_SOA, s); } + { + s->line_counter++; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1149; +tr1525: + { window_add_bit(KNOT_RRTYPE_SPF, s); } + { + s->line_counter++; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1149; +tr1534: + { window_add_bit(KNOT_RRTYPE_SRV, s); } + { + s->line_counter++; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1149; +tr1545: + { window_add_bit(KNOT_RRTYPE_SSHFP, s); } + { + s->line_counter++; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1149; +tr1558: + { window_add_bit(KNOT_RRTYPE_TLSA, s); } + { + s->line_counter++; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1149; +tr1567: + { window_add_bit(KNOT_RRTYPE_TXT, s); } + { + s->line_counter++; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1149; +tr1579: + { + if (s->number64 <= UINT16_MAX) { + window_add_bit(s->number64, s); + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1149; +tr1589: + { window_add_bit(KNOT_RRTYPE_URI, s); } + { + s->line_counter++; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1149; +tr1600: + { window_add_bit(KNOT_RRTYPE_AFSDB, s); } + { + s->line_counter++; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1149; +tr1609: + { window_add_bit(KNOT_RRTYPE_APL, s); } + { + s->line_counter++; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1149; +st1149: + if ( ++p == pe ) + goto _test_eof1149; +case 1149: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto st337; + case 32: goto st337; + case 40: goto tr1128; + case 41: goto tr1129; + case 65: goto st338; + case 67: goto st343; + case 68: goto st360; + case 69: goto st374; + case 72: goto st381; + case 73: goto st386; + case 75: goto st394; + case 76: goto st398; + case 77: goto st406; + case 78: goto st412; + case 80: goto st428; + case 82: goto st431; + case 83: goto st438; + case 84: goto st449; + case 85: goto st459; + case 97: goto st338; + case 99: goto st343; + case 100: goto st360; + case 101: goto st374; + case 104: goto st381; + case 105: goto st386; + case 107: goto st394; + case 108: goto st398; + case 109: goto st406; + case 110: goto st412; + case 112: goto st428; + case 114: goto st431; + case 115: goto st438; + case 116: goto st449; + case 117: goto st459; + case 2058: goto tr1145; + case 2107: goto tr1146; + case 2314: goto tr1147; + case 2363: goto tr1147; + case 2570: goto tr1148; + case 2619: goto tr1149; + } + goto tr1118; +st343: + if ( ++p == pe ) + goto _test_eof343; +case 343: + switch( (*p) ) { + case 65: goto st344; + case 68: goto st346; + case 69: goto st353; + case 78: goto st356; + case 97: goto st344; + case 100: goto st346; + case 101: goto st353; + case 110: goto st356; + } + goto tr1118; +st344: + if ( ++p == pe ) + goto _test_eof344; +case 344: + switch( (*p) ) { + case 65: goto st345; + case 97: goto st345; + } + goto tr1118; +st345: + if ( ++p == pe ) + goto _test_eof345; +case 345: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr1178; + case 32: goto tr1178; + case 40: goto tr1179; + case 41: goto tr1180; + case 2058: goto tr1181; + case 2107: goto tr1182; + case 2314: goto tr1183; + case 2363: goto tr1183; + case 2570: goto tr1184; + case 2619: goto tr1185; + } + goto tr1118; +tr1126: + { + memset(s->windows, 0, sizeof(s->windows)); + s->last_window = -1; + } + { + s->buffer_length = 0; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1150; +tr1149: + { + s->buffer_length = 0; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1150; +tr1160: + { window_add_bit(KNOT_RRTYPE_A, s); } + { + s->buffer_length = 0; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1150; +tr1170: + { window_add_bit(KNOT_RRTYPE_AAAA, s); } + { + s->buffer_length = 0; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1150; +tr1185: + { window_add_bit(KNOT_RRTYPE_CAA, s); } + { + s->buffer_length = 0; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1150; +tr1199: + { window_add_bit(KNOT_RRTYPE_CDNSKEY, s); } + { + s->buffer_length = 0; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1150; +tr1207: + { window_add_bit(KNOT_RRTYPE_CDS, s); } + { + s->buffer_length = 0; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1150; +tr1217: + { window_add_bit(KNOT_RRTYPE_CERT, s); } + { + s->buffer_length = 0; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1150; +tr1228: + { window_add_bit(KNOT_RRTYPE_CNAME, s); } + { + s->buffer_length = 0; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1150; +tr1242: + { window_add_bit(KNOT_RRTYPE_DHCID, s); } + { + s->buffer_length = 0; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1150; +tr1254: + { window_add_bit(KNOT_RRTYPE_DNAME, s); } + { + s->buffer_length = 0; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1150; +tr1265: + { window_add_bit(KNOT_RRTYPE_DNSKEY, s); } + { + s->buffer_length = 0; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1150; +tr1273: + { window_add_bit(KNOT_RRTYPE_DS, s); } + { + s->buffer_length = 0; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1150; +tr1286: + { window_add_bit(KNOT_RRTYPE_EUI48, s); } + { + s->buffer_length = 0; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1150; +tr1295: + { window_add_bit(KNOT_RRTYPE_EUI64, s); } + { + s->buffer_length = 0; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1150; +tr1307: + { window_add_bit(KNOT_RRTYPE_HINFO, s); } + { + s->buffer_length = 0; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1150; +tr1322: + { window_add_bit(KNOT_RRTYPE_IPSECKEY, s); } + { + s->buffer_length = 0; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1150; +tr1333: + { window_add_bit(KNOT_RRTYPE_KEY, s); } + { + s->buffer_length = 0; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1150; +tr1341: + { window_add_bit(KNOT_RRTYPE_KX, s); } + { + s->buffer_length = 0; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1150; +tr1354: + { window_add_bit(KNOT_RRTYPE_L32, s); } + { + s->buffer_length = 0; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1150; +tr1363: + { window_add_bit(KNOT_RRTYPE_L64, s); } + { + s->buffer_length = 0; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1150; +tr1372: + { window_add_bit(KNOT_RRTYPE_LOC, s); } + { + s->buffer_length = 0; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1150; +tr1380: + { window_add_bit(KNOT_RRTYPE_LP, s); } + { + s->buffer_length = 0; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1150; +tr1393: + { window_add_bit(KNOT_RRTYPE_MINFO, s); } + { + s->buffer_length = 0; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1150; +tr1401: + { window_add_bit(KNOT_RRTYPE_MX, s); } + { + s->buffer_length = 0; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1150; +tr1415: + { window_add_bit(KNOT_RRTYPE_NAPTR, s); } + { + s->buffer_length = 0; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1150; +tr1424: + { window_add_bit(KNOT_RRTYPE_NID, s); } + { + s->buffer_length = 0; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1150; +tr1433: + { window_add_bit(KNOT_RRTYPE_NS, s); } + { + s->buffer_length = 0; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1150; +tr1443: + { window_add_bit(KNOT_RRTYPE_NSEC, s); } + { + s->buffer_length = 0; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1150; +tr1452: + { window_add_bit(KNOT_RRTYPE_NSEC3, s); } + { + s->buffer_length = 0; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1150; +tr1464: + { window_add_bit(KNOT_RRTYPE_NSEC3PARAM, s); } + { + s->buffer_length = 0; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1150; +tr1474: + { window_add_bit(KNOT_RRTYPE_PTR, s); } + { + s->buffer_length = 0; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1150; +tr1485: + { window_add_bit(KNOT_RRTYPE_RP, s); } + { + s->buffer_length = 0; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1150; +tr1496: + { window_add_bit(KNOT_RRTYPE_RRSIG, s); } + { + s->buffer_length = 0; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1150; +tr1504: + { window_add_bit(KNOT_RRTYPE_RT, s); } + { + s->buffer_length = 0; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1150; +tr1517: + { window_add_bit(KNOT_RRTYPE_SOA, s); } + { + s->buffer_length = 0; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1150; +tr1526: + { window_add_bit(KNOT_RRTYPE_SPF, s); } + { + s->buffer_length = 0; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1150; +tr1535: + { window_add_bit(KNOT_RRTYPE_SRV, s); } + { + s->buffer_length = 0; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1150; +tr1546: + { window_add_bit(KNOT_RRTYPE_SSHFP, s); } + { + s->buffer_length = 0; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1150; +tr1559: + { window_add_bit(KNOT_RRTYPE_TLSA, s); } + { + s->buffer_length = 0; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1150; +tr1568: + { window_add_bit(KNOT_RRTYPE_TXT, s); } + { + s->buffer_length = 0; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1150; +tr1580: + { + if (s->number64 <= UINT16_MAX) { + window_add_bit(s->number64, s); + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1150; +tr1590: + { window_add_bit(KNOT_RRTYPE_URI, s); } + { + s->buffer_length = 0; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1150; +tr1601: + { window_add_bit(KNOT_RRTYPE_AFSDB, s); } + { + s->buffer_length = 0; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1150; +tr1610: + { window_add_bit(KNOT_RRTYPE_APL, s); } + { + s->buffer_length = 0; + } + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1150; +st1150: + if ( ++p == pe ) + goto _test_eof1150; +case 1150: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr1172; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr1171; + goto tr1118; +st346: + if ( ++p == pe ) + goto _test_eof346; +case 346: + switch( (*p) ) { + case 78: goto st347; + case 83: goto st352; + case 110: goto st347; + case 115: goto st352; + } + goto tr1118; +st347: + if ( ++p == pe ) + goto _test_eof347; +case 347: + switch( (*p) ) { + case 83: goto st348; + case 115: goto st348; + } + goto tr1118; +st348: + if ( ++p == pe ) + goto _test_eof348; +case 348: + switch( (*p) ) { + case 75: goto st349; + case 107: goto st349; + } + goto tr1118; +st349: + if ( ++p == pe ) + goto _test_eof349; +case 349: + switch( (*p) ) { + case 69: goto st350; + case 101: goto st350; + } + goto tr1118; +st350: + if ( ++p == pe ) + goto _test_eof350; +case 350: + switch( (*p) ) { + case 89: goto st351; + case 121: goto st351; + } + goto tr1118; +st351: + if ( ++p == pe ) + goto _test_eof351; +case 351: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr1192; + case 32: goto tr1192; + case 40: goto tr1193; + case 41: goto tr1194; + case 2058: goto tr1195; + case 2107: goto tr1196; + case 2314: goto tr1197; + case 2363: goto tr1197; + case 2570: goto tr1198; + case 2619: goto tr1199; + } + goto tr1118; +st352: + if ( ++p == pe ) + goto _test_eof352; +case 352: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr1200; + case 32: goto tr1200; + case 40: goto tr1201; + case 41: goto tr1202; + case 2058: goto tr1203; + case 2107: goto tr1204; + case 2314: goto tr1205; + case 2363: goto tr1205; + case 2570: goto tr1206; + case 2619: goto tr1207; + } + goto tr1118; +st353: + if ( ++p == pe ) + goto _test_eof353; +case 353: + switch( (*p) ) { + case 82: goto st354; + case 114: goto st354; + } + goto tr1118; +st354: + if ( ++p == pe ) + goto _test_eof354; +case 354: + switch( (*p) ) { + case 84: goto st355; + case 116: goto st355; + } + goto tr1118; +st355: + if ( ++p == pe ) + goto _test_eof355; +case 355: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr1210; + case 32: goto tr1210; + case 40: goto tr1211; + case 41: goto tr1212; + case 2058: goto tr1213; + case 2107: goto tr1214; + case 2314: goto tr1215; + case 2363: goto tr1215; + case 2570: goto tr1216; + case 2619: goto tr1217; + } + goto tr1118; +st356: + if ( ++p == pe ) + goto _test_eof356; +case 356: + switch( (*p) ) { + case 65: goto st357; + case 97: goto st357; + } + goto tr1118; +st357: + if ( ++p == pe ) + goto _test_eof357; +case 357: + switch( (*p) ) { + case 77: goto st358; + case 109: goto st358; + } + goto tr1118; +st358: + if ( ++p == pe ) + goto _test_eof358; +case 358: + switch( (*p) ) { + case 69: goto st359; + case 101: goto st359; + } + goto tr1118; +st359: + if ( ++p == pe ) + goto _test_eof359; +case 359: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr1221; + case 32: goto tr1221; + case 40: goto tr1222; + case 41: goto tr1223; + case 2058: goto tr1224; + case 2107: goto tr1225; + case 2314: goto tr1226; + case 2363: goto tr1226; + case 2570: goto tr1227; + case 2619: goto tr1228; + } + goto tr1118; +st360: + if ( ++p == pe ) + goto _test_eof360; +case 360: + switch( (*p) ) { + case 72: goto st361; + case 78: goto st365; + case 83: goto st373; + case 104: goto st361; + case 110: goto st365; + case 115: goto st373; + } + goto tr1118; +st361: + if ( ++p == pe ) + goto _test_eof361; +case 361: + switch( (*p) ) { + case 67: goto st362; + case 99: goto st362; + } + goto tr1118; +st362: + if ( ++p == pe ) + goto _test_eof362; +case 362: + switch( (*p) ) { + case 73: goto st363; + case 105: goto st363; + } + goto tr1118; +st363: + if ( ++p == pe ) + goto _test_eof363; +case 363: + switch( (*p) ) { + case 68: goto st364; + case 100: goto st364; + } + goto tr1118; +st364: + if ( ++p == pe ) + goto _test_eof364; +case 364: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr1235; + case 32: goto tr1235; + case 40: goto tr1236; + case 41: goto tr1237; + case 2058: goto tr1238; + case 2107: goto tr1239; + case 2314: goto tr1240; + case 2363: goto tr1240; + case 2570: goto tr1241; + case 2619: goto tr1242; + } + goto tr1118; +st365: + if ( ++p == pe ) + goto _test_eof365; +case 365: + switch( (*p) ) { + case 65: goto st366; + case 83: goto st369; + case 97: goto st366; + case 115: goto st369; + } + goto tr1118; +st366: + if ( ++p == pe ) + goto _test_eof366; +case 366: + switch( (*p) ) { + case 77: goto st367; + case 109: goto st367; + } + goto tr1118; +st367: + if ( ++p == pe ) + goto _test_eof367; +case 367: + switch( (*p) ) { + case 69: goto st368; + case 101: goto st368; + } + goto tr1118; +st368: + if ( ++p == pe ) + goto _test_eof368; +case 368: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr1247; + case 32: goto tr1247; + case 40: goto tr1248; + case 41: goto tr1249; + case 2058: goto tr1250; + case 2107: goto tr1251; + case 2314: goto tr1252; + case 2363: goto tr1252; + case 2570: goto tr1253; + case 2619: goto tr1254; + } + goto tr1118; +st369: + if ( ++p == pe ) + goto _test_eof369; +case 369: + switch( (*p) ) { + case 75: goto st370; + case 107: goto st370; + } + goto tr1118; +st370: + if ( ++p == pe ) + goto _test_eof370; +case 370: + switch( (*p) ) { + case 69: goto st371; + case 101: goto st371; + } + goto tr1118; +st371: + if ( ++p == pe ) + goto _test_eof371; +case 371: + switch( (*p) ) { + case 89: goto st372; + case 121: goto st372; + } + goto tr1118; +st372: + if ( ++p == pe ) + goto _test_eof372; +case 372: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr1258; + case 32: goto tr1258; + case 40: goto tr1259; + case 41: goto tr1260; + case 2058: goto tr1261; + case 2107: goto tr1262; + case 2314: goto tr1263; + case 2363: goto tr1263; + case 2570: goto tr1264; + case 2619: goto tr1265; + } + goto tr1118; +st373: + if ( ++p == pe ) + goto _test_eof373; +case 373: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr1266; + case 32: goto tr1266; + case 40: goto tr1267; + case 41: goto tr1268; + case 2058: goto tr1269; + case 2107: goto tr1270; + case 2314: goto tr1271; + case 2363: goto tr1271; + case 2570: goto tr1272; + case 2619: goto tr1273; + } + goto tr1118; +st374: + if ( ++p == pe ) + goto _test_eof374; +case 374: + switch( (*p) ) { + case 85: goto st375; + case 117: goto st375; + } + goto tr1118; +st375: + if ( ++p == pe ) + goto _test_eof375; +case 375: + switch( (*p) ) { + case 73: goto st376; + case 105: goto st376; + } + goto tr1118; +st376: + if ( ++p == pe ) + goto _test_eof376; +case 376: + switch( (*p) ) { + case 52: goto st377; + case 54: goto st379; + } + goto tr1118; +st377: + if ( ++p == pe ) + goto _test_eof377; +case 377: + if ( (*p) == 56 ) + goto st378; + goto tr1118; +st378: + if ( ++p == pe ) + goto _test_eof378; +case 378: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr1279; + case 32: goto tr1279; + case 40: goto tr1280; + case 41: goto tr1281; + case 2058: goto tr1282; + case 2107: goto tr1283; + case 2314: goto tr1284; + case 2363: goto tr1284; + case 2570: goto tr1285; + case 2619: goto tr1286; + } + goto tr1118; +st379: + if ( ++p == pe ) + goto _test_eof379; +case 379: + if ( (*p) == 52 ) + goto st380; + goto tr1118; +st380: + if ( ++p == pe ) + goto _test_eof380; +case 380: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr1288; + case 32: goto tr1288; + case 40: goto tr1289; + case 41: goto tr1290; + case 2058: goto tr1291; + case 2107: goto tr1292; + case 2314: goto tr1293; + case 2363: goto tr1293; + case 2570: goto tr1294; + case 2619: goto tr1295; + } + goto tr1118; +st381: + if ( ++p == pe ) + goto _test_eof381; +case 381: + switch( (*p) ) { + case 73: goto st382; + case 105: goto st382; + } + goto tr1118; +st382: + if ( ++p == pe ) + goto _test_eof382; +case 382: + switch( (*p) ) { + case 78: goto st383; + case 110: goto st383; + } + goto tr1118; +st383: + if ( ++p == pe ) + goto _test_eof383; +case 383: + switch( (*p) ) { + case 70: goto st384; + case 102: goto st384; + } + goto tr1118; +st384: + if ( ++p == pe ) + goto _test_eof384; +case 384: + switch( (*p) ) { + case 79: goto st385; + case 111: goto st385; + } + goto tr1118; +st385: + if ( ++p == pe ) + goto _test_eof385; +case 385: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr1300; + case 32: goto tr1300; + case 40: goto tr1301; + case 41: goto tr1302; + case 2058: goto tr1303; + case 2107: goto tr1304; + case 2314: goto tr1305; + case 2363: goto tr1305; + case 2570: goto tr1306; + case 2619: goto tr1307; + } + goto tr1118; +st386: + if ( ++p == pe ) + goto _test_eof386; +case 386: + switch( (*p) ) { + case 80: goto st387; + case 112: goto st387; + } + goto tr1118; +st387: + if ( ++p == pe ) + goto _test_eof387; +case 387: + switch( (*p) ) { + case 83: goto st388; + case 115: goto st388; + } + goto tr1118; +st388: + if ( ++p == pe ) + goto _test_eof388; +case 388: + switch( (*p) ) { + case 69: goto st389; + case 101: goto st389; + } + goto tr1118; +st389: + if ( ++p == pe ) + goto _test_eof389; +case 389: + switch( (*p) ) { + case 67: goto st390; + case 99: goto st390; + } + goto tr1118; +st390: + if ( ++p == pe ) + goto _test_eof390; +case 390: + switch( (*p) ) { + case 75: goto st391; + case 107: goto st391; + } + goto tr1118; +st391: + if ( ++p == pe ) + goto _test_eof391; +case 391: + switch( (*p) ) { + case 69: goto st392; + case 101: goto st392; + } + goto tr1118; +st392: + if ( ++p == pe ) + goto _test_eof392; +case 392: + switch( (*p) ) { + case 89: goto st393; + case 121: goto st393; + } + goto tr1118; +st393: + if ( ++p == pe ) + goto _test_eof393; +case 393: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr1315; + case 32: goto tr1315; + case 40: goto tr1316; + case 41: goto tr1317; + case 2058: goto tr1318; + case 2107: goto tr1319; + case 2314: goto tr1320; + case 2363: goto tr1320; + case 2570: goto tr1321; + case 2619: goto tr1322; + } + goto tr1118; +st394: + if ( ++p == pe ) + goto _test_eof394; +case 394: + switch( (*p) ) { + case 69: goto st395; + case 88: goto st397; + case 101: goto st395; + case 120: goto st397; + } + goto tr1118; +st395: + if ( ++p == pe ) + goto _test_eof395; +case 395: + switch( (*p) ) { + case 89: goto st396; + case 121: goto st396; + } + goto tr1118; +st396: + if ( ++p == pe ) + goto _test_eof396; +case 396: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr1326; + case 32: goto tr1326; + case 40: goto tr1327; + case 41: goto tr1328; + case 2058: goto tr1329; + case 2107: goto tr1330; + case 2314: goto tr1331; + case 2363: goto tr1331; + case 2570: goto tr1332; + case 2619: goto tr1333; + } + goto tr1118; +st397: + if ( ++p == pe ) + goto _test_eof397; +case 397: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr1334; + case 32: goto tr1334; + case 40: goto tr1335; + case 41: goto tr1336; + case 2058: goto tr1337; + case 2107: goto tr1338; + case 2314: goto tr1339; + case 2363: goto tr1339; + case 2570: goto tr1340; + case 2619: goto tr1341; + } + goto tr1118; +st398: + if ( ++p == pe ) + goto _test_eof398; +case 398: + switch( (*p) ) { + case 51: goto st399; + case 54: goto st401; + case 79: goto st403; + case 80: goto st405; + case 111: goto st403; + case 112: goto st405; + } + goto tr1118; +st399: + if ( ++p == pe ) + goto _test_eof399; +case 399: + if ( (*p) == 50 ) + goto st400; + goto tr1118; +st400: + if ( ++p == pe ) + goto _test_eof400; +case 400: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr1347; + case 32: goto tr1347; + case 40: goto tr1348; + case 41: goto tr1349; + case 2058: goto tr1350; + case 2107: goto tr1351; + case 2314: goto tr1352; + case 2363: goto tr1352; + case 2570: goto tr1353; + case 2619: goto tr1354; + } + goto tr1118; +st401: + if ( ++p == pe ) + goto _test_eof401; +case 401: + if ( (*p) == 52 ) + goto st402; + goto tr1118; +st402: + if ( ++p == pe ) + goto _test_eof402; +case 402: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr1356; + case 32: goto tr1356; + case 40: goto tr1357; + case 41: goto tr1358; + case 2058: goto tr1359; + case 2107: goto tr1360; + case 2314: goto tr1361; + case 2363: goto tr1361; + case 2570: goto tr1362; + case 2619: goto tr1363; + } + goto tr1118; +st403: + if ( ++p == pe ) + goto _test_eof403; +case 403: + switch( (*p) ) { + case 67: goto st404; + case 99: goto st404; + } + goto tr1118; +st404: + if ( ++p == pe ) + goto _test_eof404; +case 404: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr1365; + case 32: goto tr1365; + case 40: goto tr1366; + case 41: goto tr1367; + case 2058: goto tr1368; + case 2107: goto tr1369; + case 2314: goto tr1370; + case 2363: goto tr1370; + case 2570: goto tr1371; + case 2619: goto tr1372; + } + goto tr1118; +st405: + if ( ++p == pe ) + goto _test_eof405; +case 405: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr1373; + case 32: goto tr1373; + case 40: goto tr1374; + case 41: goto tr1375; + case 2058: goto tr1376; + case 2107: goto tr1377; + case 2314: goto tr1378; + case 2363: goto tr1378; + case 2570: goto tr1379; + case 2619: goto tr1380; + } + goto tr1118; +st406: + if ( ++p == pe ) + goto _test_eof406; +case 406: + switch( (*p) ) { + case 73: goto st407; + case 88: goto st411; + case 105: goto st407; + case 120: goto st411; + } + goto tr1118; +st407: + if ( ++p == pe ) + goto _test_eof407; +case 407: + switch( (*p) ) { + case 78: goto st408; + case 110: goto st408; + } + goto tr1118; +st408: + if ( ++p == pe ) + goto _test_eof408; +case 408: + switch( (*p) ) { + case 70: goto st409; + case 102: goto st409; + } + goto tr1118; +st409: + if ( ++p == pe ) + goto _test_eof409; +case 409: + switch( (*p) ) { + case 79: goto st410; + case 111: goto st410; + } + goto tr1118; +st410: + if ( ++p == pe ) + goto _test_eof410; +case 410: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr1386; + case 32: goto tr1386; + case 40: goto tr1387; + case 41: goto tr1388; + case 2058: goto tr1389; + case 2107: goto tr1390; + case 2314: goto tr1391; + case 2363: goto tr1391; + case 2570: goto tr1392; + case 2619: goto tr1393; + } + goto tr1118; +st411: + if ( ++p == pe ) + goto _test_eof411; +case 411: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr1394; + case 32: goto tr1394; + case 40: goto tr1395; + case 41: goto tr1396; + case 2058: goto tr1397; + case 2107: goto tr1398; + case 2314: goto tr1399; + case 2363: goto tr1399; + case 2570: goto tr1400; + case 2619: goto tr1401; + } + goto tr1118; +st412: + if ( ++p == pe ) + goto _test_eof412; +case 412: + switch( (*p) ) { + case 65: goto st413; + case 73: goto st417; + case 83: goto st419; + case 97: goto st413; + case 105: goto st417; + case 115: goto st419; + } + goto tr1118; +st413: + if ( ++p == pe ) + goto _test_eof413; +case 413: + switch( (*p) ) { + case 80: goto st414; + case 112: goto st414; + } + goto tr1118; +st414: + if ( ++p == pe ) + goto _test_eof414; +case 414: + switch( (*p) ) { + case 84: goto st415; + case 116: goto st415; + } + goto tr1118; +st415: + if ( ++p == pe ) + goto _test_eof415; +case 415: + switch( (*p) ) { + case 82: goto st416; + case 114: goto st416; + } + goto tr1118; +st416: + if ( ++p == pe ) + goto _test_eof416; +case 416: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr1408; + case 32: goto tr1408; + case 40: goto tr1409; + case 41: goto tr1410; + case 2058: goto tr1411; + case 2107: goto tr1412; + case 2314: goto tr1413; + case 2363: goto tr1413; + case 2570: goto tr1414; + case 2619: goto tr1415; + } + goto tr1118; +st417: + if ( ++p == pe ) + goto _test_eof417; +case 417: + switch( (*p) ) { + case 68: goto st418; + case 100: goto st418; + } + goto tr1118; +st418: + if ( ++p == pe ) + goto _test_eof418; +case 418: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr1417; + case 32: goto tr1417; + case 40: goto tr1418; + case 41: goto tr1419; + case 2058: goto tr1420; + case 2107: goto tr1421; + case 2314: goto tr1422; + case 2363: goto tr1422; + case 2570: goto tr1423; + case 2619: goto tr1424; + } + goto tr1118; +st419: + if ( ++p == pe ) + goto _test_eof419; +case 419: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr1425; + case 32: goto tr1425; + case 40: goto tr1426; + case 41: goto tr1427; + case 69: goto st420; + case 101: goto st420; + case 2058: goto tr1429; + case 2107: goto tr1430; + case 2314: goto tr1431; + case 2363: goto tr1431; + case 2570: goto tr1432; + case 2619: goto tr1433; + } + goto tr1118; +st420: + if ( ++p == pe ) + goto _test_eof420; +case 420: + switch( (*p) ) { + case 67: goto st421; + case 99: goto st421; + } + goto tr1118; +st421: + if ( ++p == pe ) + goto _test_eof421; +case 421: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr1435; + case 32: goto tr1435; + case 40: goto tr1436; + case 41: goto tr1437; + case 51: goto st422; + case 2058: goto tr1439; + case 2107: goto tr1440; + case 2314: goto tr1441; + case 2363: goto tr1441; + case 2570: goto tr1442; + case 2619: goto tr1443; + } + goto tr1118; +st422: + if ( ++p == pe ) + goto _test_eof422; +case 422: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr1444; + case 32: goto tr1444; + case 40: goto tr1445; + case 41: goto tr1446; + case 80: goto st423; + case 112: goto st423; + case 2058: goto tr1448; + case 2107: goto tr1449; + case 2314: goto tr1450; + case 2363: goto tr1450; + case 2570: goto tr1451; + case 2619: goto tr1452; + } + goto tr1118; +st423: + if ( ++p == pe ) + goto _test_eof423; +case 423: + switch( (*p) ) { + case 65: goto st424; + case 97: goto st424; + } + goto tr1118; +st424: + if ( ++p == pe ) + goto _test_eof424; +case 424: + switch( (*p) ) { + case 82: goto st425; + case 114: goto st425; + } + goto tr1118; +st425: + if ( ++p == pe ) + goto _test_eof425; +case 425: + switch( (*p) ) { + case 65: goto st426; + case 97: goto st426; + } + goto tr1118; +st426: + if ( ++p == pe ) + goto _test_eof426; +case 426: + switch( (*p) ) { + case 77: goto st427; + case 109: goto st427; + } + goto tr1118; +st427: + if ( ++p == pe ) + goto _test_eof427; +case 427: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr1457; + case 32: goto tr1457; + case 40: goto tr1458; + case 41: goto tr1459; + case 2058: goto tr1460; + case 2107: goto tr1461; + case 2314: goto tr1462; + case 2363: goto tr1462; + case 2570: goto tr1463; + case 2619: goto tr1464; + } + goto tr1118; +st428: + if ( ++p == pe ) + goto _test_eof428; +case 428: + switch( (*p) ) { + case 84: goto st429; + case 116: goto st429; + } + goto tr1118; +st429: + if ( ++p == pe ) + goto _test_eof429; +case 429: + switch( (*p) ) { + case 82: goto st430; + case 114: goto st430; + } + goto tr1118; +st430: + if ( ++p == pe ) + goto _test_eof430; +case 430: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr1467; + case 32: goto tr1467; + case 40: goto tr1468; + case 41: goto tr1469; + case 2058: goto tr1470; + case 2107: goto tr1471; + case 2314: goto tr1472; + case 2363: goto tr1472; + case 2570: goto tr1473; + case 2619: goto tr1474; + } + goto tr1118; +st431: + if ( ++p == pe ) + goto _test_eof431; +case 431: + switch( (*p) ) { + case 80: goto st432; + case 82: goto st433; + case 84: goto st437; + case 112: goto st432; + case 114: goto st433; + case 116: goto st437; + } + goto tr1118; +st432: + if ( ++p == pe ) + goto _test_eof432; +case 432: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr1478; + case 32: goto tr1478; + case 40: goto tr1479; + case 41: goto tr1480; + case 2058: goto tr1481; + case 2107: goto tr1482; + case 2314: goto tr1483; + case 2363: goto tr1483; + case 2570: goto tr1484; + case 2619: goto tr1485; + } + goto tr1118; +st433: + if ( ++p == pe ) + goto _test_eof433; +case 433: + switch( (*p) ) { + case 83: goto st434; + case 115: goto st434; + } + goto tr1118; +st434: + if ( ++p == pe ) + goto _test_eof434; +case 434: + switch( (*p) ) { + case 73: goto st435; + case 105: goto st435; + } + goto tr1118; +st435: + if ( ++p == pe ) + goto _test_eof435; +case 435: + switch( (*p) ) { + case 71: goto st436; + case 103: goto st436; + } + goto tr1118; +st436: + if ( ++p == pe ) + goto _test_eof436; +case 436: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr1489; + case 32: goto tr1489; + case 40: goto tr1490; + case 41: goto tr1491; + case 2058: goto tr1492; + case 2107: goto tr1493; + case 2314: goto tr1494; + case 2363: goto tr1494; + case 2570: goto tr1495; + case 2619: goto tr1496; + } + goto tr1118; +st437: + if ( ++p == pe ) + goto _test_eof437; +case 437: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr1497; + case 32: goto tr1497; + case 40: goto tr1498; + case 41: goto tr1499; + case 2058: goto tr1500; + case 2107: goto tr1501; + case 2314: goto tr1502; + case 2363: goto tr1502; + case 2570: goto tr1503; + case 2619: goto tr1504; + } + goto tr1118; +st438: + if ( ++p == pe ) + goto _test_eof438; +case 438: + switch( (*p) ) { + case 79: goto st439; + case 80: goto st441; + case 82: goto st443; + case 83: goto st445; + case 111: goto st439; + case 112: goto st441; + case 114: goto st443; + case 115: goto st445; + } + goto tr1118; +st439: + if ( ++p == pe ) + goto _test_eof439; +case 439: + switch( (*p) ) { + case 65: goto st440; + case 97: goto st440; + } + goto tr1118; +st440: + if ( ++p == pe ) + goto _test_eof440; +case 440: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr1510; + case 32: goto tr1510; + case 40: goto tr1511; + case 41: goto tr1512; + case 2058: goto tr1513; + case 2107: goto tr1514; + case 2314: goto tr1515; + case 2363: goto tr1515; + case 2570: goto tr1516; + case 2619: goto tr1517; + } + goto tr1118; +st441: + if ( ++p == pe ) + goto _test_eof441; +case 441: + switch( (*p) ) { + case 70: goto st442; + case 102: goto st442; + } + goto tr1118; +st442: + if ( ++p == pe ) + goto _test_eof442; +case 442: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr1519; + case 32: goto tr1519; + case 40: goto tr1520; + case 41: goto tr1521; + case 2058: goto tr1522; + case 2107: goto tr1523; + case 2314: goto tr1524; + case 2363: goto tr1524; + case 2570: goto tr1525; + case 2619: goto tr1526; + } + goto tr1118; +st443: + if ( ++p == pe ) + goto _test_eof443; +case 443: + switch( (*p) ) { + case 86: goto st444; + case 118: goto st444; + } + goto tr1118; +st444: + if ( ++p == pe ) + goto _test_eof444; +case 444: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr1528; + case 32: goto tr1528; + case 40: goto tr1529; + case 41: goto tr1530; + case 2058: goto tr1531; + case 2107: goto tr1532; + case 2314: goto tr1533; + case 2363: goto tr1533; + case 2570: goto tr1534; + case 2619: goto tr1535; + } + goto tr1118; +st445: + if ( ++p == pe ) + goto _test_eof445; +case 445: + switch( (*p) ) { + case 72: goto st446; + case 104: goto st446; + } + goto tr1118; +st446: + if ( ++p == pe ) + goto _test_eof446; +case 446: + switch( (*p) ) { + case 70: goto st447; + case 102: goto st447; + } + goto tr1118; +st447: + if ( ++p == pe ) + goto _test_eof447; +case 447: + switch( (*p) ) { + case 80: goto st448; + case 112: goto st448; + } + goto tr1118; +st448: + if ( ++p == pe ) + goto _test_eof448; +case 448: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr1539; + case 32: goto tr1539; + case 40: goto tr1540; + case 41: goto tr1541; + case 2058: goto tr1542; + case 2107: goto tr1543; + case 2314: goto tr1544; + case 2363: goto tr1544; + case 2570: goto tr1545; + case 2619: goto tr1546; + } + goto tr1118; +st449: + if ( ++p == pe ) + goto _test_eof449; +case 449: + switch( (*p) ) { + case 76: goto st450; + case 88: goto st453; + case 89: goto st455; + case 108: goto st450; + case 120: goto st453; + case 121: goto st455; + } + goto tr1118; +st450: + if ( ++p == pe ) + goto _test_eof450; +case 450: + switch( (*p) ) { + case 83: goto st451; + case 115: goto st451; + } + goto tr1118; +st451: + if ( ++p == pe ) + goto _test_eof451; +case 451: + switch( (*p) ) { + case 65: goto st452; + case 97: goto st452; + } + goto tr1118; +st452: + if ( ++p == pe ) + goto _test_eof452; +case 452: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr1552; + case 32: goto tr1552; + case 40: goto tr1553; + case 41: goto tr1554; + case 2058: goto tr1555; + case 2107: goto tr1556; + case 2314: goto tr1557; + case 2363: goto tr1557; + case 2570: goto tr1558; + case 2619: goto tr1559; + } + goto tr1118; +st453: + if ( ++p == pe ) + goto _test_eof453; +case 453: + switch( (*p) ) { + case 84: goto st454; + case 116: goto st454; + } + goto tr1118; +st454: + if ( ++p == pe ) + goto _test_eof454; +case 454: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr1561; + case 32: goto tr1561; + case 40: goto tr1562; + case 41: goto tr1563; + case 2058: goto tr1564; + case 2107: goto tr1565; + case 2314: goto tr1566; + case 2363: goto tr1566; + case 2570: goto tr1567; + case 2619: goto tr1568; + } + goto tr1118; +st455: + if ( ++p == pe ) + goto _test_eof455; +case 455: + switch( (*p) ) { + case 80: goto st456; + case 112: goto st456; + } + goto tr1118; +st456: + if ( ++p == pe ) + goto _test_eof456; +case 456: + switch( (*p) ) { + case 69: goto st457; + case 101: goto st457; + } + goto tr1118; +st457: + if ( ++p == pe ) + goto _test_eof457; +case 457: + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr1571; + goto tr1118; +tr1571: + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st458; +tr1575: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st458; +st458: + if ( ++p == pe ) + goto _test_eof458; +case 458: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr1572; + case 32: goto tr1572; + case 40: goto tr1573; + case 41: goto tr1574; + case 2058: goto tr1576; + case 2107: goto tr1577; + case 2314: goto tr1578; + case 2363: goto tr1578; + case 2570: goto tr1579; + case 2619: goto tr1580; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr1575; + goto tr1118; +st459: + if ( ++p == pe ) + goto _test_eof459; +case 459: + switch( (*p) ) { + case 82: goto st460; + case 114: goto st460; + } + goto tr1118; +st460: + if ( ++p == pe ) + goto _test_eof460; +case 460: + switch( (*p) ) { + case 73: goto st461; + case 105: goto st461; + } + goto tr1118; +st461: + if ( ++p == pe ) + goto _test_eof461; +case 461: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr1583; + case 32: goto tr1583; + case 40: goto tr1584; + case 41: goto tr1585; + case 2058: goto tr1586; + case 2107: goto tr1587; + case 2314: goto tr1588; + case 2363: goto tr1588; + case 2570: goto tr1589; + case 2619: goto tr1590; + } + goto tr1118; +st462: + if ( ++p == pe ) + goto _test_eof462; +case 462: + switch( (*p) ) { + case 83: goto st463; + case 115: goto st463; + } + goto tr1118; +st463: + if ( ++p == pe ) + goto _test_eof463; +case 463: + switch( (*p) ) { + case 68: goto st464; + case 100: goto st464; + } + goto tr1118; +st464: + if ( ++p == pe ) + goto _test_eof464; +case 464: + switch( (*p) ) { + case 66: goto st465; + case 98: goto st465; + } + goto tr1118; +st465: + if ( ++p == pe ) + goto _test_eof465; +case 465: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr1594; + case 32: goto tr1594; + case 40: goto tr1595; + case 41: goto tr1596; + case 2058: goto tr1597; + case 2107: goto tr1598; + case 2314: goto tr1599; + case 2363: goto tr1599; + case 2570: goto tr1600; + case 2619: goto tr1601; + } + goto tr1118; +st466: + if ( ++p == pe ) + goto _test_eof466; +case 466: + switch( (*p) ) { + case 76: goto st467; + case 108: goto st467; + } + goto tr1118; +st467: + if ( ++p == pe ) + goto _test_eof467; +case 467: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr1603; + case 32: goto tr1603; + case 40: goto tr1604; + case 41: goto tr1605; + case 2058: goto tr1606; + case 2107: goto tr1607; + case 2314: goto tr1608; + case 2363: goto tr1608; + case 2570: goto tr1609; + case 2619: goto tr1610; + } + goto tr1118; +st468: + if ( ++p == pe ) + goto _test_eof468; +case 468: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st469; + case 32: goto st469; + case 40: goto tr1613; + case 41: goto tr1614; + case 1034: goto tr1615; + case 1083: goto tr1616; + } + goto tr1611; +tr1613: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st469; +tr1614: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st469; +tr1615: + { + s->line_counter++; + } + goto st469; +tr1646: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st469; +st469: + if ( ++p == pe ) + goto _test_eof469; +case 469: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st469; + case 32: goto st469; + case 40: goto tr1613; + case 41: goto tr1614; + case 1034: goto tr1615; + case 1083: goto tr1616; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr1618; + goto tr1617; +tr1618: + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st470; +tr1622: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st470; +st470: + if ( ++p == pe ) + goto _test_eof470; +case 470: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr1619; + case 32: goto tr1619; + case 40: goto tr1620; + case 41: goto tr1621; + case 1034: goto tr1623; + case 1083: goto tr1624; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr1622; + goto tr1617; +tr1627: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st471; +tr1628: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st471; +tr1630: + { + s->line_counter++; + } + goto st471; +tr1644: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st471; +tr1619: + { + if (s->number64 <= UINT16_MAX) { + s->r_data_length = (uint16_t)(s->number64); + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + goto st471; +tr1620: + { + if (s->number64 <= UINT16_MAX) { + s->r_data_length = (uint16_t)(s->number64); + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st471; +tr1621: + { + if (s->number64 <= UINT16_MAX) { + s->r_data_length = (uint16_t)(s->number64); + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st471; +tr1623: + { + if (s->number64 <= UINT16_MAX) { + s->r_data_length = (uint16_t)(s->number64); + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st471; +st471: + if ( ++p == pe ) + goto _test_eof471; +case 471: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st471; + case 32: goto st471; + case 40: goto tr1627; + case 41: goto tr1628; + case 1034: goto tr1630; + case 1083: goto tr1631; + } + if ( _widec < 65 ) { + if ( 48 <= _widec && _widec <= 57 ) + goto tr1629; + } else if ( _widec > 70 ) { + if ( 97 <= _widec && _widec <= 102 ) + goto tr1629; + } else + goto tr1629; + goto tr1625; +tr1629: + { + if (rdata_tail <= rdata_stop) { + *rdata_tail = first_hex_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + goto st472; +st472: + if ( ++p == pe ) + goto _test_eof472; +case 472: + if ( (*p) < 65 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr1632; + } else if ( (*p) > 70 ) { + if ( 97 <= (*p) && (*p) <= 102 ) + goto tr1632; + } else + goto tr1632; + goto tr1625; +tr1634: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st473; +tr1635: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st473; +tr1636: + { + s->line_counter++; + } + goto st473; +tr1642: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st473; +tr1632: + { + *rdata_tail += second_hex_to_num[(uint8_t)(*p)]; + rdata_tail++; + } + goto st473; +st473: + if ( ++p == pe ) + goto _test_eof473; +case 473: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto st473; + case 32: goto st473; + case 40: goto tr1634; + case 41: goto tr1635; + case 2058: goto tr1636; + case 2107: goto tr1637; + case 2314: goto tr1638; + case 2363: goto tr1638; + case 2570: goto tr1639; + case 2619: goto tr1640; + } + if ( _widec < 65 ) { + if ( 48 <= _widec && _widec <= 57 ) + goto tr1629; + } else if ( _widec > 70 ) { + if ( 97 <= _widec && _widec <= 102 ) + goto tr1629; + } else + goto tr1629; + goto tr1625; +tr1637: + { + s->buffer_length = 0; + } + goto st474; +tr1641: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st474; +st474: + if ( ++p == pe ) + goto _test_eof474; +case 474: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr1642; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr1641; + goto tr1625; +tr1638: + { + if ((rdata_tail - s->r_data) != s->r_data_length) { + WARN(ZS_BAD_RDATA_LENGTH); + p--; {goto st268;} + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1151; +st1151: + if ( ++p == pe ) + goto _test_eof1151; +case 1151: + goto st0; +tr1639: + { + s->line_counter++; + } + { + if ((rdata_tail - s->r_data) != s->r_data_length) { + WARN(ZS_BAD_RDATA_LENGTH); + p--; {goto st268;} + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1152; +st1152: + if ( ++p == pe ) + goto _test_eof1152; +case 1152: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto st473; + case 32: goto st473; + case 40: goto tr1634; + case 41: goto tr1635; + case 2058: goto tr1636; + case 2107: goto tr1637; + case 2314: goto tr1638; + case 2363: goto tr1638; + case 2570: goto tr1639; + case 2619: goto tr1640; + } + if ( _widec < 65 ) { + if ( 48 <= _widec && _widec <= 57 ) + goto tr1629; + } else if ( _widec > 70 ) { + if ( 97 <= _widec && _widec <= 102 ) + goto tr1629; + } else + goto tr1629; + goto tr1625; +tr1640: + { + s->buffer_length = 0; + } + { + if ((rdata_tail - s->r_data) != s->r_data_length) { + WARN(ZS_BAD_RDATA_LENGTH); + p--; {goto st268;} + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1153; +st1153: + if ( ++p == pe ) + goto _test_eof1153; +case 1153: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr1642; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr1641; + goto tr1625; +tr1631: + { + s->buffer_length = 0; + } + goto st475; +tr1643: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st475; +tr1624: + { + if (s->number64 <= UINT16_MAX) { + s->r_data_length = (uint16_t)(s->number64); + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st475; +st475: + if ( ++p == pe ) + goto _test_eof475; +case 475: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr1644; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr1643; + goto tr1611; +tr1616: + { + s->buffer_length = 0; + } + goto st476; +tr1645: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st476; +st476: + if ( ++p == pe ) + goto _test_eof476; +case 476: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr1646; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr1645; + goto tr1611; +st477: + if ( ++p == pe ) + goto _test_eof477; +case 477: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st478; + case 32: goto st478; + case 40: goto tr1648; + case 41: goto tr1649; + case 1034: goto tr1650; + case 1083: goto tr1651; + } + goto tr1611; +tr1648: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st478; +tr1649: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st478; +tr1650: + { + s->line_counter++; + } + goto st478; +tr1686: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st478; +st478: + if ( ++p == pe ) + goto _test_eof478; +case 478: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st478; + case 32: goto st478; + case 40: goto tr1648; + case 41: goto tr1649; + case 48: goto tr1652; + case 1034: goto tr1650; + case 1083: goto tr1651; + } + if ( 49 <= _widec && _widec <= 57 ) + goto tr1653; + goto tr1617; +tr1652: + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st479; +st479: + if ( ++p == pe ) + goto _test_eof479; +case 479: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr1654; + case 32: goto tr1654; + case 40: goto tr1655; + case 41: goto tr1656; + case 778: goto tr1658; + case 827: goto tr1658; + case 1034: goto tr1659; + case 1083: goto tr1660; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr1657; + goto tr1617; +tr1654: + { + p--; {cs = stack[--top];goto _again;} + } + { + if (s->number64 <= UINT16_MAX) { + s->r_data_length = (uint16_t)(s->number64); + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + goto st1154; +tr1655: + { + p--; {cs = stack[--top];goto _again;} + } + { + if (s->number64 <= UINT16_MAX) { + s->r_data_length = (uint16_t)(s->number64); + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st1154; +tr1656: + { + p--; {cs = stack[--top];goto _again;} + } + { + if (s->number64 <= UINT16_MAX) { + s->r_data_length = (uint16_t)(s->number64); + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st1154; +tr1659: + { + p--; {cs = stack[--top];goto _again;} + } + { + if (s->number64 <= UINT16_MAX) { + s->r_data_length = (uint16_t)(s->number64); + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st1154; +st1154: + if ( ++p == pe ) + goto _test_eof1154; +case 1154: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st480; + case 32: goto st480; + case 40: goto tr1662; + case 41: goto tr1663; + case 1034: goto tr1665; + case 1083: goto tr1666; + } + if ( _widec < 65 ) { + if ( 48 <= _widec && _widec <= 57 ) + goto tr1664; + } else if ( _widec > 70 ) { + if ( 97 <= _widec && _widec <= 102 ) + goto tr1664; + } else + goto tr1664; + goto tr1625; +tr1662: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st480; +tr1663: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st480; +tr1665: + { + s->line_counter++; + } + goto st480; +tr1679: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st480; +tr1680: + { + if (s->number64 <= UINT16_MAX) { + s->r_data_length = (uint16_t)(s->number64); + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + goto st480; +tr1681: + { + if (s->number64 <= UINT16_MAX) { + s->r_data_length = (uint16_t)(s->number64); + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st480; +tr1682: + { + if (s->number64 <= UINT16_MAX) { + s->r_data_length = (uint16_t)(s->number64); + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st480; +tr1683: + { + if (s->number64 <= UINT16_MAX) { + s->r_data_length = (uint16_t)(s->number64); + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st480; +st480: + if ( ++p == pe ) + goto _test_eof480; +case 480: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st480; + case 32: goto st480; + case 40: goto tr1662; + case 41: goto tr1663; + case 1034: goto tr1665; + case 1083: goto tr1666; + } + if ( _widec < 65 ) { + if ( 48 <= _widec && _widec <= 57 ) + goto tr1664; + } else if ( _widec > 70 ) { + if ( 97 <= _widec && _widec <= 102 ) + goto tr1664; + } else + goto tr1664; + goto tr1625; +tr1664: + { + if (rdata_tail <= rdata_stop) { + *rdata_tail = first_hex_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + goto st481; +st481: + if ( ++p == pe ) + goto _test_eof481; +case 481: + if ( (*p) < 65 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr1667; + } else if ( (*p) > 70 ) { + if ( 97 <= (*p) && (*p) <= 102 ) + goto tr1667; + } else + goto tr1667; + goto tr1625; +tr1669: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st482; +tr1670: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st482; +tr1671: + { + s->line_counter++; + } + goto st482; +tr1677: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st482; +tr1667: + { + *rdata_tail += second_hex_to_num[(uint8_t)(*p)]; + rdata_tail++; + } + goto st482; +st482: + if ( ++p == pe ) + goto _test_eof482; +case 482: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto st482; + case 32: goto st482; + case 40: goto tr1669; + case 41: goto tr1670; + case 2058: goto tr1671; + case 2107: goto tr1672; + case 2314: goto tr1673; + case 2363: goto tr1673; + case 2570: goto tr1674; + case 2619: goto tr1675; + } + if ( _widec < 65 ) { + if ( 48 <= _widec && _widec <= 57 ) + goto tr1664; + } else if ( _widec > 70 ) { + if ( 97 <= _widec && _widec <= 102 ) + goto tr1664; + } else + goto tr1664; + goto tr1625; +tr1672: + { + s->buffer_length = 0; + } + goto st483; +tr1676: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st483; +st483: + if ( ++p == pe ) + goto _test_eof483; +case 483: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr1677; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr1676; + goto tr1625; +tr1658: + { + p--; {cs = stack[--top];goto _again;} + } + goto st1155; +tr1673: + { + if ((rdata_tail - s->r_data) != s->r_data_length) { + WARN(ZS_BAD_RDATA_LENGTH); + p--; {goto st268;} + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1155; +st1155: + if ( ++p == pe ) + goto _test_eof1155; +case 1155: + goto tr1611; +tr1674: + { + s->line_counter++; + } + { + if ((rdata_tail - s->r_data) != s->r_data_length) { + WARN(ZS_BAD_RDATA_LENGTH); + p--; {goto st268;} + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1156; +st1156: + if ( ++p == pe ) + goto _test_eof1156; +case 1156: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto st482; + case 32: goto st482; + case 40: goto tr1669; + case 41: goto tr1670; + case 2058: goto tr1671; + case 2107: goto tr1672; + case 2314: goto tr1673; + case 2363: goto tr1673; + case 2570: goto tr1674; + case 2619: goto tr1675; + } + if ( _widec < 65 ) { + if ( 48 <= _widec && _widec <= 57 ) + goto tr1664; + } else if ( _widec > 70 ) { + if ( 97 <= _widec && _widec <= 102 ) + goto tr1664; + } else + goto tr1664; + goto tr1625; +tr1675: + { + s->buffer_length = 0; + } + { + if ((rdata_tail - s->r_data) != s->r_data_length) { + WARN(ZS_BAD_RDATA_LENGTH); + p--; {goto st268;} + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1157; +st1157: + if ( ++p == pe ) + goto _test_eof1157; +case 1157: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr1677; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr1676; + goto tr1625; +tr1666: + { + s->buffer_length = 0; + } + goto st484; +tr1678: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st484; +tr1684: + { + if (s->number64 <= UINT16_MAX) { + s->r_data_length = (uint16_t)(s->number64); + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st484; +st484: + if ( ++p == pe ) + goto _test_eof484; +case 484: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr1679; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr1678; + goto tr1611; +tr1653: + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st485; +tr1657: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st485; +st485: + if ( ++p == pe ) + goto _test_eof485; +case 485: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr1680; + case 32: goto tr1680; + case 40: goto tr1681; + case 41: goto tr1682; + case 1034: goto tr1683; + case 1083: goto tr1684; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr1657; + goto tr1617; +tr1660: + { + p--; {cs = stack[--top];goto _again;} + } + { + if (s->number64 <= UINT16_MAX) { + s->r_data_length = (uint16_t)(s->number64); + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st1158; +st1158: + if ( ++p == pe ) + goto _test_eof1158; +case 1158: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr1679; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr1678; + goto tr1611; +tr1651: + { + s->buffer_length = 0; + } + goto st486; +tr1685: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st486; +st486: + if ( ++p == pe ) + goto _test_eof486; +case 486: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr1686; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr1685; + goto tr1611; +st487: + if ( ++p == pe ) + goto _test_eof487; +case 487: + switch( (*p) ) { + case 68: goto st489; + case 69: goto st504; + case 73: goto st543; + case 80: goto st551; + case 82: goto st564; + case 100: goto st489; + case 101: goto st504; + case 105: goto st543; + case 112: goto st551; + case 114: goto st564; + } + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr1688; + goto tr1687; +tr1688: + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st488; +tr1695: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st488; +st488: + if ( ++p == pe ) + goto _test_eof488; +case 488: + switch( (*p) ) { + case 32: goto tr1694; + case 59: goto tr1694; + } + if ( (*p) < 40 ) { + if ( 9 <= (*p) && (*p) <= 10 ) + goto tr1694; + } else if ( (*p) > 41 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr1695; + } else + goto tr1694; + goto tr1687; +tr1694: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1159; +tr1698: + { + *(rdata_tail++) = 2; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1159; +tr1700: + { + *(rdata_tail++) = 3; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1159; +tr1712: + { + *(rdata_tail++) = 6; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1159; +tr1722: + { + *(rdata_tail++) = 12; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1159; +tr1736: + { + *(rdata_tail++) = 13; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1159; +tr1745: + { + *(rdata_tail++) = 14; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1159; +tr1752: + { + *(rdata_tail++) = 15; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1159; +tr1755: + { + *(rdata_tail++) = 16; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1159; +tr1763: + { + *(rdata_tail++) = 252; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1159; +tr1774: + { + *(rdata_tail++) = 253; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1159; +tr1777: + { + *(rdata_tail++) = 254; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1159; +tr1784: + { + *(rdata_tail++) = 1; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1159; +tr1790: + { + *(rdata_tail++) = 5; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1159; +tr1802: + { + *(rdata_tail++) = 7; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1159; +tr1805: + { + *(rdata_tail++) = 8; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1159; +tr1808: + { + *(rdata_tail++) = 10; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1159; +st1159: + if ( ++p == pe ) + goto _test_eof1159; +case 1159: + goto st0; +st489: + if ( ++p == pe ) + goto _test_eof489; +case 489: + switch( (*p) ) { + case 72: goto st490; + case 83: goto st491; + case 104: goto st490; + case 115: goto st491; + } + goto tr1687; +st490: + if ( ++p == pe ) + goto _test_eof490; +case 490: + switch( (*p) ) { + case 32: goto tr1698; + case 59: goto tr1698; + } + if ( (*p) > 10 ) { + if ( 40 <= (*p) && (*p) <= 41 ) + goto tr1698; + } else if ( (*p) >= 9 ) + goto tr1698; + goto tr1687; +st491: + if ( ++p == pe ) + goto _test_eof491; +case 491: + switch( (*p) ) { + case 65: goto st492; + case 97: goto st492; + } + goto tr1687; +st492: + if ( ++p == pe ) + goto _test_eof492; +case 492: + switch( (*p) ) { + case 32: goto tr1700; + case 45: goto st493; + case 59: goto tr1700; + } + if ( (*p) > 10 ) { + if ( 40 <= (*p) && (*p) <= 41 ) + goto tr1700; + } else if ( (*p) >= 9 ) + goto tr1700; + goto tr1687; +st493: + if ( ++p == pe ) + goto _test_eof493; +case 493: + switch( (*p) ) { + case 78: goto st494; + case 110: goto st494; + } + goto tr1687; +st494: + if ( ++p == pe ) + goto _test_eof494; +case 494: + switch( (*p) ) { + case 83: goto st495; + case 115: goto st495; + } + goto tr1687; +st495: + if ( ++p == pe ) + goto _test_eof495; +case 495: + switch( (*p) ) { + case 69: goto st496; + case 101: goto st496; + } + goto tr1687; +st496: + if ( ++p == pe ) + goto _test_eof496; +case 496: + switch( (*p) ) { + case 67: goto st497; + case 99: goto st497; + } + goto tr1687; +st497: + if ( ++p == pe ) + goto _test_eof497; +case 497: + if ( (*p) == 51 ) + goto st498; + goto tr1687; +st498: + if ( ++p == pe ) + goto _test_eof498; +case 498: + if ( (*p) == 45 ) + goto st499; + goto tr1687; +st499: + if ( ++p == pe ) + goto _test_eof499; +case 499: + switch( (*p) ) { + case 83: goto st500; + case 115: goto st500; + } + goto tr1687; +st500: + if ( ++p == pe ) + goto _test_eof500; +case 500: + switch( (*p) ) { + case 72: goto st501; + case 104: goto st501; + } + goto tr1687; +st501: + if ( ++p == pe ) + goto _test_eof501; +case 501: + switch( (*p) ) { + case 65: goto st502; + case 97: goto st502; + } + goto tr1687; +st502: + if ( ++p == pe ) + goto _test_eof502; +case 502: + if ( (*p) == 49 ) + goto st503; + goto tr1687; +st503: + if ( ++p == pe ) + goto _test_eof503; +case 503: + switch( (*p) ) { + case 32: goto tr1712; + case 59: goto tr1712; + } + if ( (*p) > 10 ) { + if ( 40 <= (*p) && (*p) <= 41 ) + goto tr1712; + } else if ( (*p) >= 9 ) + goto tr1712; + goto tr1687; +st504: + if ( ++p == pe ) + goto _test_eof504; +case 504: + switch( (*p) ) { + case 67: goto st505; + case 68: goto st534; + case 99: goto st505; + case 100: goto st534; + } + goto tr1687; +st505: + if ( ++p == pe ) + goto _test_eof505; +case 505: + switch( (*p) ) { + case 67: goto st506; + case 68: goto st512; + case 99: goto st506; + case 100: goto st512; + } + goto tr1687; +st506: + if ( ++p == pe ) + goto _test_eof506; +case 506: + if ( (*p) == 45 ) + goto st507; + goto tr1687; +st507: + if ( ++p == pe ) + goto _test_eof507; +case 507: + switch( (*p) ) { + case 71: goto st508; + case 103: goto st508; + } + goto tr1687; +st508: + if ( ++p == pe ) + goto _test_eof508; +case 508: + switch( (*p) ) { + case 79: goto st509; + case 111: goto st509; + } + goto tr1687; +st509: + if ( ++p == pe ) + goto _test_eof509; +case 509: + switch( (*p) ) { + case 83: goto st510; + case 115: goto st510; + } + goto tr1687; +st510: + if ( ++p == pe ) + goto _test_eof510; +case 510: + switch( (*p) ) { + case 84: goto st511; + case 116: goto st511; + } + goto tr1687; +st511: + if ( ++p == pe ) + goto _test_eof511; +case 511: + switch( (*p) ) { + case 32: goto tr1722; + case 59: goto tr1722; + } + if ( (*p) > 10 ) { + if ( 40 <= (*p) && (*p) <= 41 ) + goto tr1722; + } else if ( (*p) >= 9 ) + goto tr1722; + goto tr1687; +st512: + if ( ++p == pe ) + goto _test_eof512; +case 512: + switch( (*p) ) { + case 83: goto st513; + case 115: goto st513; + } + goto tr1687; +st513: + if ( ++p == pe ) + goto _test_eof513; +case 513: + switch( (*p) ) { + case 65: goto st514; + case 97: goto st514; + } + goto tr1687; +st514: + if ( ++p == pe ) + goto _test_eof514; +case 514: + switch( (*p) ) { + case 80: goto st515; + case 112: goto st515; + } + goto tr1687; +st515: + if ( ++p == pe ) + goto _test_eof515; +case 515: + switch( (*p) ) { + case 50: goto st516; + case 51: goto st525; + } + goto tr1687; +st516: + if ( ++p == pe ) + goto _test_eof516; +case 516: + if ( (*p) == 53 ) + goto st517; + goto tr1687; +st517: + if ( ++p == pe ) + goto _test_eof517; +case 517: + if ( (*p) == 54 ) + goto st518; + goto tr1687; +st518: + if ( ++p == pe ) + goto _test_eof518; +case 518: + switch( (*p) ) { + case 83: goto st519; + case 115: goto st519; + } + goto tr1687; +st519: + if ( ++p == pe ) + goto _test_eof519; +case 519: + switch( (*p) ) { + case 72: goto st520; + case 104: goto st520; + } + goto tr1687; +st520: + if ( ++p == pe ) + goto _test_eof520; +case 520: + switch( (*p) ) { + case 65: goto st521; + case 97: goto st521; + } + goto tr1687; +st521: + if ( ++p == pe ) + goto _test_eof521; +case 521: + if ( (*p) == 50 ) + goto st522; + goto tr1687; +st522: + if ( ++p == pe ) + goto _test_eof522; +case 522: + if ( (*p) == 53 ) + goto st523; + goto tr1687; +st523: + if ( ++p == pe ) + goto _test_eof523; +case 523: + if ( (*p) == 54 ) + goto st524; + goto tr1687; +st524: + if ( ++p == pe ) + goto _test_eof524; +case 524: + switch( (*p) ) { + case 32: goto tr1736; + case 59: goto tr1736; + } + if ( (*p) > 10 ) { + if ( 40 <= (*p) && (*p) <= 41 ) + goto tr1736; + } else if ( (*p) >= 9 ) + goto tr1736; + goto tr1687; +st525: + if ( ++p == pe ) + goto _test_eof525; +case 525: + if ( (*p) == 56 ) + goto st526; + goto tr1687; +st526: + if ( ++p == pe ) + goto _test_eof526; +case 526: + if ( (*p) == 52 ) + goto st527; + goto tr1687; +st527: + if ( ++p == pe ) + goto _test_eof527; +case 527: + switch( (*p) ) { + case 83: goto st528; + case 115: goto st528; + } + goto tr1687; +st528: + if ( ++p == pe ) + goto _test_eof528; +case 528: + switch( (*p) ) { + case 72: goto st529; + case 104: goto st529; + } + goto tr1687; +st529: + if ( ++p == pe ) + goto _test_eof529; +case 529: + switch( (*p) ) { + case 65: goto st530; + case 97: goto st530; + } + goto tr1687; +st530: + if ( ++p == pe ) + goto _test_eof530; +case 530: + if ( (*p) == 51 ) + goto st531; + goto tr1687; +st531: + if ( ++p == pe ) + goto _test_eof531; +case 531: + if ( (*p) == 56 ) + goto st532; + goto tr1687; +st532: + if ( ++p == pe ) + goto _test_eof532; +case 532: + if ( (*p) == 52 ) + goto st533; + goto tr1687; +st533: + if ( ++p == pe ) + goto _test_eof533; +case 533: + switch( (*p) ) { + case 32: goto tr1745; + case 59: goto tr1745; + } + if ( (*p) > 10 ) { + if ( 40 <= (*p) && (*p) <= 41 ) + goto tr1745; + } else if ( (*p) >= 9 ) + goto tr1745; + goto tr1687; +st534: + if ( ++p == pe ) + goto _test_eof534; +case 534: + switch( (*p) ) { + case 50: goto st535; + case 52: goto st540; + } + goto tr1687; +st535: + if ( ++p == pe ) + goto _test_eof535; +case 535: + if ( (*p) == 53 ) + goto st536; + goto tr1687; +st536: + if ( ++p == pe ) + goto _test_eof536; +case 536: + if ( (*p) == 53 ) + goto st537; + goto tr1687; +st537: + if ( ++p == pe ) + goto _test_eof537; +case 537: + if ( (*p) == 49 ) + goto st538; + goto tr1687; +st538: + if ( ++p == pe ) + goto _test_eof538; +case 538: + if ( (*p) == 57 ) + goto st539; + goto tr1687; +st539: + if ( ++p == pe ) + goto _test_eof539; +case 539: + switch( (*p) ) { + case 32: goto tr1752; + case 59: goto tr1752; + } + if ( (*p) > 10 ) { + if ( 40 <= (*p) && (*p) <= 41 ) + goto tr1752; + } else if ( (*p) >= 9 ) + goto tr1752; + goto tr1687; +st540: + if ( ++p == pe ) + goto _test_eof540; +case 540: + if ( (*p) == 52 ) + goto st541; + goto tr1687; +st541: + if ( ++p == pe ) + goto _test_eof541; +case 541: + if ( (*p) == 56 ) + goto st542; + goto tr1687; +st542: + if ( ++p == pe ) + goto _test_eof542; +case 542: + switch( (*p) ) { + case 32: goto tr1755; + case 59: goto tr1755; + } + if ( (*p) > 10 ) { + if ( 40 <= (*p) && (*p) <= 41 ) + goto tr1755; + } else if ( (*p) >= 9 ) + goto tr1755; + goto tr1687; +st543: + if ( ++p == pe ) + goto _test_eof543; +case 543: + switch( (*p) ) { + case 78: goto st544; + case 110: goto st544; + } + goto tr1687; +st544: + if ( ++p == pe ) + goto _test_eof544; +case 544: + switch( (*p) ) { + case 68: goto st545; + case 100: goto st545; + } + goto tr1687; +st545: + if ( ++p == pe ) + goto _test_eof545; +case 545: + switch( (*p) ) { + case 73: goto st546; + case 105: goto st546; + } + goto tr1687; +st546: + if ( ++p == pe ) + goto _test_eof546; +case 546: + switch( (*p) ) { + case 82: goto st547; + case 114: goto st547; + } + goto tr1687; +st547: + if ( ++p == pe ) + goto _test_eof547; +case 547: + switch( (*p) ) { + case 69: goto st548; + case 101: goto st548; + } + goto tr1687; +st548: + if ( ++p == pe ) + goto _test_eof548; +case 548: + switch( (*p) ) { + case 67: goto st549; + case 99: goto st549; + } + goto tr1687; +st549: + if ( ++p == pe ) + goto _test_eof549; +case 549: + switch( (*p) ) { + case 84: goto st550; + case 116: goto st550; + } + goto tr1687; +st550: + if ( ++p == pe ) + goto _test_eof550; +case 550: + switch( (*p) ) { + case 32: goto tr1763; + case 59: goto tr1763; + } + if ( (*p) > 10 ) { + if ( 40 <= (*p) && (*p) <= 41 ) + goto tr1763; + } else if ( (*p) >= 9 ) + goto tr1763; + goto tr1687; +st551: + if ( ++p == pe ) + goto _test_eof551; +case 551: + switch( (*p) ) { + case 82: goto st552; + case 114: goto st552; + } + goto tr1687; +st552: + if ( ++p == pe ) + goto _test_eof552; +case 552: + switch( (*p) ) { + case 73: goto st553; + case 105: goto st553; + } + goto tr1687; +st553: + if ( ++p == pe ) + goto _test_eof553; +case 553: + switch( (*p) ) { + case 86: goto st554; + case 118: goto st554; + } + goto tr1687; +st554: + if ( ++p == pe ) + goto _test_eof554; +case 554: + switch( (*p) ) { + case 65: goto st555; + case 97: goto st555; + } + goto tr1687; +st555: + if ( ++p == pe ) + goto _test_eof555; +case 555: + switch( (*p) ) { + case 84: goto st556; + case 116: goto st556; + } + goto tr1687; +st556: + if ( ++p == pe ) + goto _test_eof556; +case 556: + switch( (*p) ) { + case 69: goto st557; + case 101: goto st557; + } + goto tr1687; +st557: + if ( ++p == pe ) + goto _test_eof557; +case 557: + switch( (*p) ) { + case 68: goto st558; + case 79: goto st561; + case 100: goto st558; + case 111: goto st561; + } + goto tr1687; +st558: + if ( ++p == pe ) + goto _test_eof558; +case 558: + switch( (*p) ) { + case 78: goto st559; + case 110: goto st559; + } + goto tr1687; +st559: + if ( ++p == pe ) + goto _test_eof559; +case 559: + switch( (*p) ) { + case 83: goto st560; + case 115: goto st560; + } + goto tr1687; +st560: + if ( ++p == pe ) + goto _test_eof560; +case 560: + switch( (*p) ) { + case 32: goto tr1774; + case 59: goto tr1774; + } + if ( (*p) > 10 ) { + if ( 40 <= (*p) && (*p) <= 41 ) + goto tr1774; + } else if ( (*p) >= 9 ) + goto tr1774; + goto tr1687; +st561: + if ( ++p == pe ) + goto _test_eof561; +case 561: + switch( (*p) ) { + case 73: goto st562; + case 105: goto st562; + } + goto tr1687; +st562: + if ( ++p == pe ) + goto _test_eof562; +case 562: + switch( (*p) ) { + case 68: goto st563; + case 100: goto st563; + } + goto tr1687; +st563: + if ( ++p == pe ) + goto _test_eof563; +case 563: + switch( (*p) ) { + case 32: goto tr1777; + case 59: goto tr1777; + } + if ( (*p) > 10 ) { + if ( 40 <= (*p) && (*p) <= 41 ) + goto tr1777; + } else if ( (*p) >= 9 ) + goto tr1777; + goto tr1687; +st564: + if ( ++p == pe ) + goto _test_eof564; +case 564: + switch( (*p) ) { + case 83: goto st565; + case 115: goto st565; + } + goto tr1687; +st565: + if ( ++p == pe ) + goto _test_eof565; +case 565: + switch( (*p) ) { + case 65: goto st566; + case 97: goto st566; + } + goto tr1687; +st566: + if ( ++p == pe ) + goto _test_eof566; +case 566: + switch( (*p) ) { + case 77: goto st567; + case 83: goto st570; + case 109: goto st567; + case 115: goto st570; + } + goto tr1687; +st567: + if ( ++p == pe ) + goto _test_eof567; +case 567: + switch( (*p) ) { + case 68: goto st568; + case 100: goto st568; + } + goto tr1687; +st568: + if ( ++p == pe ) + goto _test_eof568; +case 568: + if ( (*p) == 53 ) + goto st569; + goto tr1687; +st569: + if ( ++p == pe ) + goto _test_eof569; +case 569: + switch( (*p) ) { + case 32: goto tr1784; + case 59: goto tr1784; + } + if ( (*p) > 10 ) { + if ( 40 <= (*p) && (*p) <= 41 ) + goto tr1784; + } else if ( (*p) >= 9 ) + goto tr1784; + goto tr1687; +st570: + if ( ++p == pe ) + goto _test_eof570; +case 570: + switch( (*p) ) { + case 72: goto st571; + case 104: goto st571; + } + goto tr1687; +st571: + if ( ++p == pe ) + goto _test_eof571; +case 571: + switch( (*p) ) { + case 65: goto st572; + case 97: goto st572; + } + goto tr1687; +st572: + if ( ++p == pe ) + goto _test_eof572; +case 572: + switch( (*p) ) { + case 49: goto st573; + case 50: goto st585; + case 53: goto st588; + } + goto tr1687; +st573: + if ( ++p == pe ) + goto _test_eof573; +case 573: + switch( (*p) ) { + case 32: goto tr1790; + case 45: goto st574; + case 59: goto tr1790; + } + if ( (*p) > 10 ) { + if ( 40 <= (*p) && (*p) <= 41 ) + goto tr1790; + } else if ( (*p) >= 9 ) + goto tr1790; + goto tr1687; +st574: + if ( ++p == pe ) + goto _test_eof574; +case 574: + switch( (*p) ) { + case 78: goto st575; + case 110: goto st575; + } + goto tr1687; +st575: + if ( ++p == pe ) + goto _test_eof575; +case 575: + switch( (*p) ) { + case 83: goto st576; + case 115: goto st576; + } + goto tr1687; +st576: + if ( ++p == pe ) + goto _test_eof576; +case 576: + switch( (*p) ) { + case 69: goto st577; + case 101: goto st577; + } + goto tr1687; +st577: + if ( ++p == pe ) + goto _test_eof577; +case 577: + switch( (*p) ) { + case 67: goto st578; + case 99: goto st578; + } + goto tr1687; +st578: + if ( ++p == pe ) + goto _test_eof578; +case 578: + if ( (*p) == 51 ) + goto st579; + goto tr1687; +st579: + if ( ++p == pe ) + goto _test_eof579; +case 579: + if ( (*p) == 45 ) + goto st580; + goto tr1687; +st580: + if ( ++p == pe ) + goto _test_eof580; +case 580: + switch( (*p) ) { + case 83: goto st581; + case 115: goto st581; + } + goto tr1687; +st581: + if ( ++p == pe ) + goto _test_eof581; +case 581: + switch( (*p) ) { + case 72: goto st582; + case 104: goto st582; + } + goto tr1687; +st582: + if ( ++p == pe ) + goto _test_eof582; +case 582: + switch( (*p) ) { + case 65: goto st583; + case 97: goto st583; + } + goto tr1687; +st583: + if ( ++p == pe ) + goto _test_eof583; +case 583: + if ( (*p) == 49 ) + goto st584; + goto tr1687; +st584: + if ( ++p == pe ) + goto _test_eof584; +case 584: + switch( (*p) ) { + case 32: goto tr1802; + case 59: goto tr1802; + } + if ( (*p) > 10 ) { + if ( 40 <= (*p) && (*p) <= 41 ) + goto tr1802; + } else if ( (*p) >= 9 ) + goto tr1802; + goto tr1687; +st585: + if ( ++p == pe ) + goto _test_eof585; +case 585: + if ( (*p) == 53 ) + goto st586; + goto tr1687; +st586: + if ( ++p == pe ) + goto _test_eof586; +case 586: + if ( (*p) == 54 ) + goto st587; + goto tr1687; +st587: + if ( ++p == pe ) + goto _test_eof587; +case 587: + switch( (*p) ) { + case 32: goto tr1805; + case 59: goto tr1805; + } + if ( (*p) > 10 ) { + if ( 40 <= (*p) && (*p) <= 41 ) + goto tr1805; + } else if ( (*p) >= 9 ) + goto tr1805; + goto tr1687; +st588: + if ( ++p == pe ) + goto _test_eof588; +case 588: + if ( (*p) == 49 ) + goto st589; + goto tr1687; +st589: + if ( ++p == pe ) + goto _test_eof589; +case 589: + if ( (*p) == 50 ) + goto st590; + goto tr1687; +st590: + if ( ++p == pe ) + goto _test_eof590; +case 590: + switch( (*p) ) { + case 32: goto tr1808; + case 59: goto tr1808; + } + if ( (*p) > 10 ) { + if ( 40 <= (*p) && (*p) <= 41 ) + goto tr1808; + } else if ( (*p) >= 9 ) + goto tr1808; + goto tr1687; +st591: + if ( ++p == pe ) + goto _test_eof591; +case 591: + switch( (*p) ) { + case 65: goto st593; + case 73: goto st599; + case 79: goto st616; + case 80: goto st619; + case 83: goto st625; + case 85: goto st629; + case 97: goto st593; + case 105: goto st599; + case 111: goto st616; + case 112: goto st619; + case 115: goto st625; + case 117: goto st629; + } + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr1810; + goto tr1809; +tr1810: + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st592; +tr1818: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st592; +st592: + if ( ++p == pe ) + goto _test_eof592; +case 592: + switch( (*p) ) { + case 32: goto tr1817; + case 59: goto tr1817; + } + if ( (*p) < 40 ) { + if ( 9 <= (*p) && (*p) <= 10 ) + goto tr1817; + } else if ( (*p) > 41 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr1818; + } else + goto tr1817; + goto tr1809; +tr1817: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1160; +tr1824: + { + *((uint16_t *)rdata_tail) = htons(7); + rdata_tail += 2; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1160; +tr1833: + { + *((uint16_t *)rdata_tail) = htons(8); + rdata_tail += 2; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1160; +tr1837: + { + *((uint16_t *)rdata_tail) = htons(6); + rdata_tail += 2; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1160; +tr1840: + { + *((uint16_t *)rdata_tail) = htons(4); + rdata_tail += 2; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1160; +tr1844: + { + *((uint16_t *)rdata_tail) = htons(5); + rdata_tail += 2; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1160; +tr1847: + { + *((uint16_t *)rdata_tail) = htons(254); + rdata_tail += 2; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1160; +tr1851: + { + *((uint16_t *)rdata_tail) = htons(3); + rdata_tail += 2; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1160; +tr1854: + { + *((uint16_t *)rdata_tail) = htons(1); + rdata_tail += 2; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1160; +tr1858: + { + *((uint16_t *)rdata_tail) = htons(2); + rdata_tail += 2; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1160; +tr1861: + { + *((uint16_t *)rdata_tail) = htons(253); + rdata_tail += 2; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1160; +st1160: + if ( ++p == pe ) + goto _test_eof1160; +case 1160: + goto st0; +st593: + if ( ++p == pe ) + goto _test_eof593; +case 593: + switch( (*p) ) { + case 67: goto st594; + case 99: goto st594; + } + goto tr1809; +st594: + if ( ++p == pe ) + goto _test_eof594; +case 594: + switch( (*p) ) { + case 80: goto st595; + case 112: goto st595; + } + goto tr1809; +st595: + if ( ++p == pe ) + goto _test_eof595; +case 595: + switch( (*p) ) { + case 75: goto st596; + case 107: goto st596; + } + goto tr1809; +st596: + if ( ++p == pe ) + goto _test_eof596; +case 596: + switch( (*p) ) { + case 73: goto st597; + case 105: goto st597; + } + goto tr1809; +st597: + if ( ++p == pe ) + goto _test_eof597; +case 597: + switch( (*p) ) { + case 88: goto st598; + case 120: goto st598; + } + goto tr1809; +st598: + if ( ++p == pe ) + goto _test_eof598; +case 598: + switch( (*p) ) { + case 32: goto tr1824; + case 59: goto tr1824; + } + if ( (*p) > 10 ) { + if ( 40 <= (*p) && (*p) <= 41 ) + goto tr1824; + } else if ( (*p) >= 9 ) + goto tr1824; + goto tr1809; +st599: + if ( ++p == pe ) + goto _test_eof599; +case 599: + switch( (*p) ) { + case 65: goto st600; + case 80: goto st606; + case 83: goto st612; + case 97: goto st600; + case 112: goto st606; + case 115: goto st612; + } + goto tr1809; +st600: + if ( ++p == pe ) + goto _test_eof600; +case 600: + switch( (*p) ) { + case 67: goto st601; + case 99: goto st601; + } + goto tr1809; +st601: + if ( ++p == pe ) + goto _test_eof601; +case 601: + switch( (*p) ) { + case 80: goto st602; + case 112: goto st602; + } + goto tr1809; +st602: + if ( ++p == pe ) + goto _test_eof602; +case 602: + switch( (*p) ) { + case 75: goto st603; + case 107: goto st603; + } + goto tr1809; +st603: + if ( ++p == pe ) + goto _test_eof603; +case 603: + switch( (*p) ) { + case 73: goto st604; + case 105: goto st604; + } + goto tr1809; +st604: + if ( ++p == pe ) + goto _test_eof604; +case 604: + switch( (*p) ) { + case 88: goto st605; + case 120: goto st605; + } + goto tr1809; +st605: + if ( ++p == pe ) + goto _test_eof605; +case 605: + switch( (*p) ) { + case 32: goto tr1833; + case 59: goto tr1833; + } + if ( (*p) > 10 ) { + if ( 40 <= (*p) && (*p) <= 41 ) + goto tr1833; + } else if ( (*p) >= 9 ) + goto tr1833; + goto tr1809; +st606: + if ( ++p == pe ) + goto _test_eof606; +case 606: + switch( (*p) ) { + case 71: goto st607; + case 75: goto st609; + case 103: goto st607; + case 107: goto st609; + } + goto tr1809; +st607: + if ( ++p == pe ) + goto _test_eof607; +case 607: + switch( (*p) ) { + case 80: goto st608; + case 112: goto st608; + } + goto tr1809; +st608: + if ( ++p == pe ) + goto _test_eof608; +case 608: + switch( (*p) ) { + case 32: goto tr1837; + case 59: goto tr1837; + } + if ( (*p) > 10 ) { + if ( 40 <= (*p) && (*p) <= 41 ) + goto tr1837; + } else if ( (*p) >= 9 ) + goto tr1837; + goto tr1809; +st609: + if ( ++p == pe ) + goto _test_eof609; +case 609: + switch( (*p) ) { + case 73: goto st610; + case 105: goto st610; + } + goto tr1809; +st610: + if ( ++p == pe ) + goto _test_eof610; +case 610: + switch( (*p) ) { + case 88: goto st611; + case 120: goto st611; + } + goto tr1809; +st611: + if ( ++p == pe ) + goto _test_eof611; +case 611: + switch( (*p) ) { + case 32: goto tr1840; + case 59: goto tr1840; + } + if ( (*p) > 10 ) { + if ( 40 <= (*p) && (*p) <= 41 ) + goto tr1840; + } else if ( (*p) >= 9 ) + goto tr1840; + goto tr1809; +st612: + if ( ++p == pe ) + goto _test_eof612; +case 612: + switch( (*p) ) { + case 80: goto st613; + case 112: goto st613; + } + goto tr1809; +st613: + if ( ++p == pe ) + goto _test_eof613; +case 613: + switch( (*p) ) { + case 75: goto st614; + case 107: goto st614; + } + goto tr1809; +st614: + if ( ++p == pe ) + goto _test_eof614; +case 614: + switch( (*p) ) { + case 73: goto st615; + case 105: goto st615; + } + goto tr1809; +st615: + if ( ++p == pe ) + goto _test_eof615; +case 615: + switch( (*p) ) { + case 32: goto tr1844; + case 59: goto tr1844; + } + if ( (*p) > 10 ) { + if ( 40 <= (*p) && (*p) <= 41 ) + goto tr1844; + } else if ( (*p) >= 9 ) + goto tr1844; + goto tr1809; +st616: + if ( ++p == pe ) + goto _test_eof616; +case 616: + switch( (*p) ) { + case 73: goto st617; + case 105: goto st617; + } + goto tr1809; +st617: + if ( ++p == pe ) + goto _test_eof617; +case 617: + switch( (*p) ) { + case 68: goto st618; + case 100: goto st618; + } + goto tr1809; +st618: + if ( ++p == pe ) + goto _test_eof618; +case 618: + switch( (*p) ) { + case 32: goto tr1847; + case 59: goto tr1847; + } + if ( (*p) > 10 ) { + if ( 40 <= (*p) && (*p) <= 41 ) + goto tr1847; + } else if ( (*p) >= 9 ) + goto tr1847; + goto tr1809; +st619: + if ( ++p == pe ) + goto _test_eof619; +case 619: + switch( (*p) ) { + case 71: goto st620; + case 75: goto st622; + case 103: goto st620; + case 107: goto st622; + } + goto tr1809; +st620: + if ( ++p == pe ) + goto _test_eof620; +case 620: + switch( (*p) ) { + case 80: goto st621; + case 112: goto st621; + } + goto tr1809; +st621: + if ( ++p == pe ) + goto _test_eof621; +case 621: + switch( (*p) ) { + case 32: goto tr1851; + case 59: goto tr1851; + } + if ( (*p) > 10 ) { + if ( 40 <= (*p) && (*p) <= 41 ) + goto tr1851; + } else if ( (*p) >= 9 ) + goto tr1851; + goto tr1809; +st622: + if ( ++p == pe ) + goto _test_eof622; +case 622: + switch( (*p) ) { + case 73: goto st623; + case 105: goto st623; + } + goto tr1809; +st623: + if ( ++p == pe ) + goto _test_eof623; +case 623: + switch( (*p) ) { + case 88: goto st624; + case 120: goto st624; + } + goto tr1809; +st624: + if ( ++p == pe ) + goto _test_eof624; +case 624: + switch( (*p) ) { + case 32: goto tr1854; + case 59: goto tr1854; + } + if ( (*p) > 10 ) { + if ( 40 <= (*p) && (*p) <= 41 ) + goto tr1854; + } else if ( (*p) >= 9 ) + goto tr1854; + goto tr1809; +st625: + if ( ++p == pe ) + goto _test_eof625; +case 625: + switch( (*p) ) { + case 80: goto st626; + case 112: goto st626; + } + goto tr1809; +st626: + if ( ++p == pe ) + goto _test_eof626; +case 626: + switch( (*p) ) { + case 75: goto st627; + case 107: goto st627; + } + goto tr1809; +st627: + if ( ++p == pe ) + goto _test_eof627; +case 627: + switch( (*p) ) { + case 73: goto st628; + case 105: goto st628; + } + goto tr1809; +st628: + if ( ++p == pe ) + goto _test_eof628; +case 628: + switch( (*p) ) { + case 32: goto tr1858; + case 59: goto tr1858; + } + if ( (*p) > 10 ) { + if ( 40 <= (*p) && (*p) <= 41 ) + goto tr1858; + } else if ( (*p) >= 9 ) + goto tr1858; + goto tr1809; +st629: + if ( ++p == pe ) + goto _test_eof629; +case 629: + switch( (*p) ) { + case 82: goto st630; + case 114: goto st630; + } + goto tr1809; +st630: + if ( ++p == pe ) + goto _test_eof630; +case 630: + switch( (*p) ) { + case 73: goto st631; + case 105: goto st631; + } + goto tr1809; +st631: + if ( ++p == pe ) + goto _test_eof631; +case 631: + switch( (*p) ) { + case 32: goto tr1861; + case 59: goto tr1861; + } + if ( (*p) > 10 ) { + if ( 40 <= (*p) && (*p) <= 41 ) + goto tr1861; + } else if ( (*p) >= 9 ) + goto tr1861; + goto tr1809; +st632: + if ( ++p == pe ) + goto _test_eof632; +case 632: + if ( (*p) == 46 ) + goto tr1863; + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr1863; + goto tr1862; +tr1863: + { + s->buffer_length = 0; + } + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + goto st633; +tr1865: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + goto st633; +st633: + if ( ++p == pe ) + goto _test_eof633; +case 633: + switch( (*p) ) { + case 32: goto tr1864; + case 46: goto tr1865; + case 59: goto tr1864; + } + if ( (*p) < 40 ) { + if ( 9 <= (*p) && (*p) <= 10 ) + goto tr1864; + } else if ( (*p) > 41 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr1865; + } else + goto tr1864; + goto tr1862; +tr1864: + { + s->buffer[s->buffer_length] = 0; + + if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) { + WARN(ZS_BAD_IPV4); + p--; {goto st268;} + } + } + { + memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH); + rdata_tail += ZS_INET4_ADDR_LENGTH; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1161; +st1161: + if ( ++p == pe ) + goto _test_eof1161; +case 1161: + goto st0; +st634: + if ( ++p == pe ) + goto _test_eof634; +case 634: + switch( (*p) ) { + case 42: goto tr1866; + case 92: goto tr1866; + case 95: goto tr1866; + } + if ( (*p) < 64 ) { + if ( 45 <= (*p) && (*p) <= 57 ) + goto tr1866; + } else if ( (*p) > 90 ) { + if ( 97 <= (*p) && (*p) <= 122 ) + goto tr1866; + } else + goto tr1866; + goto tr71; +tr1866: + { + s->dname = rdata_tail; + } + { p--; {stack[top++] = 635;goto st270;} } + goto st635; +st635: + if ( ++p == pe ) + goto _test_eof635; +case 635: + switch( (*p) ) { + case 32: goto tr1867; + case 59: goto tr1867; + } + if ( (*p) > 10 ) { + if ( 40 <= (*p) && (*p) <= 41 ) + goto tr1867; + } else if ( (*p) >= 9 ) + goto tr1867; + goto tr71; +tr1867: + { + rdata_tail += s->dname_tmp_length; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1162; +st1162: + if ( ++p == pe ) + goto _test_eof1162; +case 1162: + goto st0; +st636: + if ( ++p == pe ) + goto _test_eof636; +case 636: + switch( (*p) ) { + case 42: goto tr1868; + case 92: goto tr1868; + case 95: goto tr1868; + } + if ( (*p) < 64 ) { + if ( 45 <= (*p) && (*p) <= 57 ) + goto tr1868; + } else if ( (*p) > 90 ) { + if ( 97 <= (*p) && (*p) <= 122 ) + goto tr1868; + } else + goto tr1868; + goto tr71; +tr1868: + { + s->dname = rdata_tail; + } + { p--; {stack[top++] = 637;goto st270;} } + goto st637; +st637: + if ( ++p == pe ) + goto _test_eof637; +case 637: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr1869; + case 32: goto tr1869; + case 40: goto tr1870; + case 41: goto tr1871; + case 1034: goto tr1872; + case 1083: goto tr1873; + } + goto tr71; +tr1875: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st638; +tr1876: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st638; +tr1878: + { + s->line_counter++; + } + goto st638; +tr2022: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st638; +tr1869: + { + rdata_tail += s->dname_tmp_length; + } + goto st638; +tr1870: + { + rdata_tail += s->dname_tmp_length; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st638; +tr1871: + { + rdata_tail += s->dname_tmp_length; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st638; +tr1872: + { + rdata_tail += s->dname_tmp_length; + } + { + s->line_counter++; + } + goto st638; +st638: + if ( ++p == pe ) + goto _test_eof638; +case 638: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st638; + case 32: goto st638; + case 40: goto tr1875; + case 41: goto tr1876; + case 42: goto tr1877; + case 92: goto tr1877; + case 95: goto tr1877; + case 1034: goto tr1878; + case 1083: goto tr1879; + } + if ( _widec < 64 ) { + if ( 45 <= _widec && _widec <= 57 ) + goto tr1877; + } else if ( _widec > 90 ) { + if ( 97 <= _widec && _widec <= 122 ) + goto tr1877; + } else + goto tr1877; + goto tr71; +tr1877: + { + s->dname = rdata_tail; + } + { p--; {stack[top++] = 639;goto st270;} } + goto st639; +st639: + if ( ++p == pe ) + goto _test_eof639; +case 639: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr1880; + case 32: goto tr1880; + case 40: goto tr1881; + case 41: goto tr1882; + case 1034: goto tr1883; + case 1083: goto tr1884; + } + goto tr71; +tr1887: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st640; +tr1888: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st640; +tr1890: + { + s->line_counter++; + } + goto st640; +tr2020: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st640; +tr1880: + { + rdata_tail += s->dname_tmp_length; + } + goto st640; +tr1881: + { + rdata_tail += s->dname_tmp_length; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st640; +tr1882: + { + rdata_tail += s->dname_tmp_length; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st640; +tr1883: + { + rdata_tail += s->dname_tmp_length; + } + { + s->line_counter++; + } + goto st640; +st640: + if ( ++p == pe ) + goto _test_eof640; +case 640: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st640; + case 32: goto st640; + case 40: goto tr1887; + case 41: goto tr1888; + case 1034: goto tr1890; + case 1083: goto tr1891; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr1889; + goto tr1885; +tr1889: + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st641; +tr1895: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st641; +st641: + if ( ++p == pe ) + goto _test_eof641; +case 641: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr1892; + case 32: goto tr1892; + case 40: goto tr1893; + case 41: goto tr1894; + case 1034: goto tr1896; + case 1083: goto tr1897; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr1895; + goto tr1885; +tr1899: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st642; +tr1900: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st642; +tr1902: + { + s->line_counter++; + } + goto st642; +tr2018: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st642; +tr1892: + { + if (s->number64 <= UINT32_MAX) { + uint32_t num32 = htonl((uint32_t)s->number64); + memcpy(rdata_tail, &num32, 4); + rdata_tail += 4; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st642; +tr1893: + { + if (s->number64 <= UINT32_MAX) { + uint32_t num32 = htonl((uint32_t)s->number64); + memcpy(rdata_tail, &num32, 4); + rdata_tail += 4; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st642; +tr1894: + { + if (s->number64 <= UINT32_MAX) { + uint32_t num32 = htonl((uint32_t)s->number64); + memcpy(rdata_tail, &num32, 4); + rdata_tail += 4; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st642; +tr1896: + { + if (s->number64 <= UINT32_MAX) { + uint32_t num32 = htonl((uint32_t)s->number64); + memcpy(rdata_tail, &num32, 4); + rdata_tail += 4; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st642; +st642: + if ( ++p == pe ) + goto _test_eof642; +case 642: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st642; + case 32: goto st642; + case 40: goto tr1899; + case 41: goto tr1900; + case 1034: goto tr1902; + case 1083: goto tr1903; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr1901; + goto tr1885; +tr1901: + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st643; +tr1908: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st643; +st643: + if ( ++p == pe ) + goto _test_eof643; +case 643: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr1905; + case 32: goto tr1905; + case 40: goto tr1906; + case 41: goto tr1907; + case 68: goto tr1909; + case 72: goto tr1910; + case 77: goto tr1911; + case 83: goto st662; + case 87: goto tr1913; + case 100: goto tr1909; + case 104: goto tr1910; + case 109: goto tr1911; + case 115: goto st662; + case 119: goto tr1913; + case 1034: goto tr1914; + case 1083: goto tr1915; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr1908; + goto tr1904; +tr1917: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st644; +tr1918: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st644; +tr1920: + { + s->line_counter++; + } + goto st644; +tr2003: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st644; +tr1905: + { + if (s->number64 <= UINT32_MAX) { + uint32_t num32 = htonl((uint32_t)s->number64); + memcpy(rdata_tail, &num32, 4); + rdata_tail += 4; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st644; +tr1906: + { + if (s->number64 <= UINT32_MAX) { + uint32_t num32 = htonl((uint32_t)s->number64); + memcpy(rdata_tail, &num32, 4); + rdata_tail += 4; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st644; +tr1907: + { + if (s->number64 <= UINT32_MAX) { + uint32_t num32 = htonl((uint32_t)s->number64); + memcpy(rdata_tail, &num32, 4); + rdata_tail += 4; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st644; +tr1914: + { + if (s->number64 <= UINT32_MAX) { + uint32_t num32 = htonl((uint32_t)s->number64); + memcpy(rdata_tail, &num32, 4); + rdata_tail += 4; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st644; +tr2011: + { + if (s->number64 + s->number64_tmp < UINT32_MAX) { + s->number64 += s->number64_tmp; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= UINT32_MAX) { + uint32_t num32 = htonl((uint32_t)s->number64); + memcpy(rdata_tail, &num32, 4); + rdata_tail += 4; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st644; +tr2012: + { + if (s->number64 + s->number64_tmp < UINT32_MAX) { + s->number64 += s->number64_tmp; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= UINT32_MAX) { + uint32_t num32 = htonl((uint32_t)s->number64); + memcpy(rdata_tail, &num32, 4); + rdata_tail += 4; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st644; +tr2013: + { + if (s->number64 + s->number64_tmp < UINT32_MAX) { + s->number64 += s->number64_tmp; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= UINT32_MAX) { + uint32_t num32 = htonl((uint32_t)s->number64); + memcpy(rdata_tail, &num32, 4); + rdata_tail += 4; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st644; +tr2015: + { + if (s->number64 + s->number64_tmp < UINT32_MAX) { + s->number64 += s->number64_tmp; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= UINT32_MAX) { + uint32_t num32 = htonl((uint32_t)s->number64); + memcpy(rdata_tail, &num32, 4); + rdata_tail += 4; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st644; +st644: + if ( ++p == pe ) + goto _test_eof644; +case 644: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st644; + case 32: goto st644; + case 40: goto tr1917; + case 41: goto tr1918; + case 1034: goto tr1920; + case 1083: goto tr1921; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr1919; + goto tr1885; +tr1919: + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st645; +tr1925: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st645; +st645: + if ( ++p == pe ) + goto _test_eof645; +case 645: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr1922; + case 32: goto tr1922; + case 40: goto tr1923; + case 41: goto tr1924; + case 68: goto tr1926; + case 72: goto tr1927; + case 77: goto tr1928; + case 83: goto st658; + case 87: goto tr1930; + case 100: goto tr1926; + case 104: goto tr1927; + case 109: goto tr1928; + case 115: goto st658; + case 119: goto tr1930; + case 1034: goto tr1931; + case 1083: goto tr1932; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr1925; + goto tr1904; +tr1934: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st646; +tr1935: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st646; +tr1937: + { + s->line_counter++; + } + goto st646; +tr1988: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st646; +tr1922: + { + if (s->number64 <= UINT32_MAX) { + uint32_t num32 = htonl((uint32_t)s->number64); + memcpy(rdata_tail, &num32, 4); + rdata_tail += 4; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st646; +tr1923: + { + if (s->number64 <= UINT32_MAX) { + uint32_t num32 = htonl((uint32_t)s->number64); + memcpy(rdata_tail, &num32, 4); + rdata_tail += 4; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st646; +tr1924: + { + if (s->number64 <= UINT32_MAX) { + uint32_t num32 = htonl((uint32_t)s->number64); + memcpy(rdata_tail, &num32, 4); + rdata_tail += 4; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st646; +tr1931: + { + if (s->number64 <= UINT32_MAX) { + uint32_t num32 = htonl((uint32_t)s->number64); + memcpy(rdata_tail, &num32, 4); + rdata_tail += 4; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st646; +tr1996: + { + if (s->number64 + s->number64_tmp < UINT32_MAX) { + s->number64 += s->number64_tmp; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= UINT32_MAX) { + uint32_t num32 = htonl((uint32_t)s->number64); + memcpy(rdata_tail, &num32, 4); + rdata_tail += 4; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st646; +tr1997: + { + if (s->number64 + s->number64_tmp < UINT32_MAX) { + s->number64 += s->number64_tmp; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= UINT32_MAX) { + uint32_t num32 = htonl((uint32_t)s->number64); + memcpy(rdata_tail, &num32, 4); + rdata_tail += 4; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st646; +tr1998: + { + if (s->number64 + s->number64_tmp < UINT32_MAX) { + s->number64 += s->number64_tmp; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= UINT32_MAX) { + uint32_t num32 = htonl((uint32_t)s->number64); + memcpy(rdata_tail, &num32, 4); + rdata_tail += 4; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st646; +tr2000: + { + if (s->number64 + s->number64_tmp < UINT32_MAX) { + s->number64 += s->number64_tmp; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= UINT32_MAX) { + uint32_t num32 = htonl((uint32_t)s->number64); + memcpy(rdata_tail, &num32, 4); + rdata_tail += 4; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st646; +st646: + if ( ++p == pe ) + goto _test_eof646; +case 646: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st646; + case 32: goto st646; + case 40: goto tr1934; + case 41: goto tr1935; + case 1034: goto tr1937; + case 1083: goto tr1938; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr1936; + goto tr1885; +tr1936: + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st647; +tr1942: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st647; +st647: + if ( ++p == pe ) + goto _test_eof647; +case 647: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr1939; + case 32: goto tr1939; + case 40: goto tr1940; + case 41: goto tr1941; + case 68: goto tr1943; + case 72: goto tr1944; + case 77: goto tr1945; + case 83: goto st654; + case 87: goto tr1947; + case 100: goto tr1943; + case 104: goto tr1944; + case 109: goto tr1945; + case 115: goto st654; + case 119: goto tr1947; + case 1034: goto tr1948; + case 1083: goto tr1949; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr1942; + goto tr1904; +tr1951: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st648; +tr1952: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st648; +tr1954: + { + s->line_counter++; + } + goto st648; +tr1973: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st648; +tr1939: + { + if (s->number64 <= UINT32_MAX) { + uint32_t num32 = htonl((uint32_t)s->number64); + memcpy(rdata_tail, &num32, 4); + rdata_tail += 4; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st648; +tr1940: + { + if (s->number64 <= UINT32_MAX) { + uint32_t num32 = htonl((uint32_t)s->number64); + memcpy(rdata_tail, &num32, 4); + rdata_tail += 4; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st648; +tr1941: + { + if (s->number64 <= UINT32_MAX) { + uint32_t num32 = htonl((uint32_t)s->number64); + memcpy(rdata_tail, &num32, 4); + rdata_tail += 4; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st648; +tr1948: + { + if (s->number64 <= UINT32_MAX) { + uint32_t num32 = htonl((uint32_t)s->number64); + memcpy(rdata_tail, &num32, 4); + rdata_tail += 4; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st648; +tr1981: + { + if (s->number64 + s->number64_tmp < UINT32_MAX) { + s->number64 += s->number64_tmp; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= UINT32_MAX) { + uint32_t num32 = htonl((uint32_t)s->number64); + memcpy(rdata_tail, &num32, 4); + rdata_tail += 4; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st648; +tr1982: + { + if (s->number64 + s->number64_tmp < UINT32_MAX) { + s->number64 += s->number64_tmp; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= UINT32_MAX) { + uint32_t num32 = htonl((uint32_t)s->number64); + memcpy(rdata_tail, &num32, 4); + rdata_tail += 4; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st648; +tr1983: + { + if (s->number64 + s->number64_tmp < UINT32_MAX) { + s->number64 += s->number64_tmp; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= UINT32_MAX) { + uint32_t num32 = htonl((uint32_t)s->number64); + memcpy(rdata_tail, &num32, 4); + rdata_tail += 4; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st648; +tr1985: + { + if (s->number64 + s->number64_tmp < UINT32_MAX) { + s->number64 += s->number64_tmp; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= UINT32_MAX) { + uint32_t num32 = htonl((uint32_t)s->number64); + memcpy(rdata_tail, &num32, 4); + rdata_tail += 4; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st648; +st648: + if ( ++p == pe ) + goto _test_eof648; +case 648: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st648; + case 32: goto st648; + case 40: goto tr1951; + case 41: goto tr1952; + case 1034: goto tr1954; + case 1083: goto tr1955; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr1953; + goto tr1885; +tr1953: + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st649; +tr1957: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st649; +st649: + if ( ++p == pe ) + goto _test_eof649; +case 649: + switch( (*p) ) { + case 32: goto tr1956; + case 59: goto tr1956; + case 68: goto tr1958; + case 72: goto tr1959; + case 77: goto tr1960; + case 83: goto st650; + case 87: goto tr1962; + case 100: goto tr1958; + case 104: goto tr1959; + case 109: goto tr1960; + case 115: goto st650; + case 119: goto tr1962; + } + if ( (*p) < 40 ) { + if ( 9 <= (*p) && (*p) <= 10 ) + goto tr1956; + } else if ( (*p) > 41 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr1957; + } else + goto tr1956; + goto tr1904; +tr1956: + { + if (s->number64 <= UINT32_MAX) { + uint32_t num32 = htonl((uint32_t)s->number64); + memcpy(rdata_tail, &num32, 4); + rdata_tail += 4; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1163; +tr1970: + { + if (s->number64 + s->number64_tmp < UINT32_MAX) { + s->number64 += s->number64_tmp; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= UINT32_MAX) { + uint32_t num32 = htonl((uint32_t)s->number64); + memcpy(rdata_tail, &num32, 4); + rdata_tail += 4; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1163; +st1163: + if ( ++p == pe ) + goto _test_eof1163; +case 1163: + goto st0; +tr1958: + { if (s->number64 <= (UINT32_MAX / 86400)) { + s->number64 *= 86400; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st650; +tr1959: + { if (s->number64 <= (UINT32_MAX / 3600)) { + s->number64 *= 3600; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st650; +tr1960: + { if (s->number64 <= (UINT32_MAX / 60)) { + s->number64 *= 60; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st650; +tr1962: + { if (s->number64 <= (UINT32_MAX / 604800)) { + s->number64 *= 604800; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st650; +st650: + if ( ++p == pe ) + goto _test_eof650; +case 650: + switch( (*p) ) { + case 32: goto tr1956; + case 59: goto tr1956; + } + if ( (*p) < 40 ) { + if ( 9 <= (*p) && (*p) <= 10 ) + goto tr1956; + } else if ( (*p) > 41 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr1963; + } else + goto tr1956; + goto tr1904; +tr1964: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st651; +tr1963: + { + s->number64_tmp = s->number64; + } + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st651; +tr1971: + { + if (s->number64 + s->number64_tmp < UINT32_MAX) { + s->number64 += s->number64_tmp; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + s->number64_tmp = s->number64; + } + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st651; +st651: + if ( ++p == pe ) + goto _test_eof651; +case 651: + switch( (*p) ) { + case 68: goto tr1965; + case 72: goto tr1966; + case 77: goto tr1967; + case 83: goto st652; + case 87: goto tr1969; + case 100: goto tr1965; + case 104: goto tr1966; + case 109: goto tr1967; + case 115: goto st652; + case 119: goto tr1969; + } + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr1964; + goto tr1904; +tr1965: + { if (s->number64 <= (UINT32_MAX / 86400)) { + s->number64 *= 86400; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st652; +tr1966: + { if (s->number64 <= (UINT32_MAX / 3600)) { + s->number64 *= 3600; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st652; +tr1967: + { if (s->number64 <= (UINT32_MAX / 60)) { + s->number64 *= 60; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st652; +tr1969: + { if (s->number64 <= (UINT32_MAX / 604800)) { + s->number64 *= 604800; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st652; +st652: + if ( ++p == pe ) + goto _test_eof652; +case 652: + switch( (*p) ) { + case 32: goto tr1970; + case 59: goto tr1970; + } + if ( (*p) < 40 ) { + if ( 9 <= (*p) && (*p) <= 10 ) + goto tr1970; + } else if ( (*p) > 41 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr1971; + } else + goto tr1970; + goto tr1904; +tr1955: + { + s->buffer_length = 0; + } + goto st653; +tr1972: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st653; +tr1949: + { + if (s->number64 <= UINT32_MAX) { + uint32_t num32 = htonl((uint32_t)s->number64); + memcpy(rdata_tail, &num32, 4); + rdata_tail += 4; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st653; +tr1986: + { + if (s->number64 + s->number64_tmp < UINT32_MAX) { + s->number64 += s->number64_tmp; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= UINT32_MAX) { + uint32_t num32 = htonl((uint32_t)s->number64); + memcpy(rdata_tail, &num32, 4); + rdata_tail += 4; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st653; +st653: + if ( ++p == pe ) + goto _test_eof653; +case 653: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr1973; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr1972; + goto tr71; +tr1943: + { if (s->number64 <= (UINT32_MAX / 86400)) { + s->number64 *= 86400; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st654; +tr1944: + { if (s->number64 <= (UINT32_MAX / 3600)) { + s->number64 *= 3600; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st654; +tr1945: + { if (s->number64 <= (UINT32_MAX / 60)) { + s->number64 *= 60; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st654; +tr1947: + { if (s->number64 <= (UINT32_MAX / 604800)) { + s->number64 *= 604800; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st654; +st654: + if ( ++p == pe ) + goto _test_eof654; +case 654: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr1939; + case 32: goto tr1939; + case 40: goto tr1940; + case 41: goto tr1941; + case 1034: goto tr1948; + case 1083: goto tr1949; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr1974; + goto tr1904; +tr1975: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st655; +tr1974: + { + s->number64_tmp = s->number64; + } + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st655; +tr1984: + { + if (s->number64 + s->number64_tmp < UINT32_MAX) { + s->number64 += s->number64_tmp; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + s->number64_tmp = s->number64; + } + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st655; +st655: + if ( ++p == pe ) + goto _test_eof655; +case 655: + switch( (*p) ) { + case 68: goto tr1976; + case 72: goto tr1977; + case 77: goto tr1978; + case 83: goto st656; + case 87: goto tr1980; + case 100: goto tr1976; + case 104: goto tr1977; + case 109: goto tr1978; + case 115: goto st656; + case 119: goto tr1980; + } + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr1975; + goto tr1904; +tr1976: + { if (s->number64 <= (UINT32_MAX / 86400)) { + s->number64 *= 86400; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st656; +tr1977: + { if (s->number64 <= (UINT32_MAX / 3600)) { + s->number64 *= 3600; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st656; +tr1978: + { if (s->number64 <= (UINT32_MAX / 60)) { + s->number64 *= 60; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st656; +tr1980: + { if (s->number64 <= (UINT32_MAX / 604800)) { + s->number64 *= 604800; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st656; +st656: + if ( ++p == pe ) + goto _test_eof656; +case 656: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr1981; + case 32: goto tr1981; + case 40: goto tr1982; + case 41: goto tr1983; + case 1034: goto tr1985; + case 1083: goto tr1986; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr1984; + goto tr1904; +tr1938: + { + s->buffer_length = 0; + } + goto st657; +tr1987: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st657; +tr1932: + { + if (s->number64 <= UINT32_MAX) { + uint32_t num32 = htonl((uint32_t)s->number64); + memcpy(rdata_tail, &num32, 4); + rdata_tail += 4; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st657; +tr2001: + { + if (s->number64 + s->number64_tmp < UINT32_MAX) { + s->number64 += s->number64_tmp; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= UINT32_MAX) { + uint32_t num32 = htonl((uint32_t)s->number64); + memcpy(rdata_tail, &num32, 4); + rdata_tail += 4; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st657; +st657: + if ( ++p == pe ) + goto _test_eof657; +case 657: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr1988; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr1987; + goto tr71; +tr1926: + { if (s->number64 <= (UINT32_MAX / 86400)) { + s->number64 *= 86400; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st658; +tr1927: + { if (s->number64 <= (UINT32_MAX / 3600)) { + s->number64 *= 3600; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st658; +tr1928: + { if (s->number64 <= (UINT32_MAX / 60)) { + s->number64 *= 60; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st658; +tr1930: + { if (s->number64 <= (UINT32_MAX / 604800)) { + s->number64 *= 604800; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st658; +st658: + if ( ++p == pe ) + goto _test_eof658; +case 658: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr1922; + case 32: goto tr1922; + case 40: goto tr1923; + case 41: goto tr1924; + case 1034: goto tr1931; + case 1083: goto tr1932; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr1989; + goto tr1904; +tr1990: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st659; +tr1989: + { + s->number64_tmp = s->number64; + } + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st659; +tr1999: + { + if (s->number64 + s->number64_tmp < UINT32_MAX) { + s->number64 += s->number64_tmp; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + s->number64_tmp = s->number64; + } + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st659; +st659: + if ( ++p == pe ) + goto _test_eof659; +case 659: + switch( (*p) ) { + case 68: goto tr1991; + case 72: goto tr1992; + case 77: goto tr1993; + case 83: goto st660; + case 87: goto tr1995; + case 100: goto tr1991; + case 104: goto tr1992; + case 109: goto tr1993; + case 115: goto st660; + case 119: goto tr1995; + } + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr1990; + goto tr1904; +tr1991: + { if (s->number64 <= (UINT32_MAX / 86400)) { + s->number64 *= 86400; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st660; +tr1992: + { if (s->number64 <= (UINT32_MAX / 3600)) { + s->number64 *= 3600; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st660; +tr1993: + { if (s->number64 <= (UINT32_MAX / 60)) { + s->number64 *= 60; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st660; +tr1995: + { if (s->number64 <= (UINT32_MAX / 604800)) { + s->number64 *= 604800; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st660; +st660: + if ( ++p == pe ) + goto _test_eof660; +case 660: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr1996; + case 32: goto tr1996; + case 40: goto tr1997; + case 41: goto tr1998; + case 1034: goto tr2000; + case 1083: goto tr2001; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr1999; + goto tr1904; +tr1921: + { + s->buffer_length = 0; + } + goto st661; +tr2002: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st661; +tr1915: + { + if (s->number64 <= UINT32_MAX) { + uint32_t num32 = htonl((uint32_t)s->number64); + memcpy(rdata_tail, &num32, 4); + rdata_tail += 4; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st661; +tr2016: + { + if (s->number64 + s->number64_tmp < UINT32_MAX) { + s->number64 += s->number64_tmp; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= UINT32_MAX) { + uint32_t num32 = htonl((uint32_t)s->number64); + memcpy(rdata_tail, &num32, 4); + rdata_tail += 4; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st661; +st661: + if ( ++p == pe ) + goto _test_eof661; +case 661: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2003; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2002; + goto tr71; +tr1909: + { if (s->number64 <= (UINT32_MAX / 86400)) { + s->number64 *= 86400; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st662; +tr1910: + { if (s->number64 <= (UINT32_MAX / 3600)) { + s->number64 *= 3600; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st662; +tr1911: + { if (s->number64 <= (UINT32_MAX / 60)) { + s->number64 *= 60; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st662; +tr1913: + { if (s->number64 <= (UINT32_MAX / 604800)) { + s->number64 *= 604800; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st662; +st662: + if ( ++p == pe ) + goto _test_eof662; +case 662: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr1905; + case 32: goto tr1905; + case 40: goto tr1906; + case 41: goto tr1907; + case 1034: goto tr1914; + case 1083: goto tr1915; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2004; + goto tr1904; +tr2005: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st663; +tr2004: + { + s->number64_tmp = s->number64; + } + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st663; +tr2014: + { + if (s->number64 + s->number64_tmp < UINT32_MAX) { + s->number64 += s->number64_tmp; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + s->number64_tmp = s->number64; + } + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st663; +st663: + if ( ++p == pe ) + goto _test_eof663; +case 663: + switch( (*p) ) { + case 68: goto tr2006; + case 72: goto tr2007; + case 77: goto tr2008; + case 83: goto st664; + case 87: goto tr2010; + case 100: goto tr2006; + case 104: goto tr2007; + case 109: goto tr2008; + case 115: goto st664; + case 119: goto tr2010; + } + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr2005; + goto tr1904; +tr2006: + { if (s->number64 <= (UINT32_MAX / 86400)) { + s->number64 *= 86400; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st664; +tr2007: + { if (s->number64 <= (UINT32_MAX / 3600)) { + s->number64 *= 3600; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st664; +tr2008: + { if (s->number64 <= (UINT32_MAX / 60)) { + s->number64 *= 60; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st664; +tr2010: + { if (s->number64 <= (UINT32_MAX / 604800)) { + s->number64 *= 604800; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st664; +st664: + if ( ++p == pe ) + goto _test_eof664; +case 664: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr2011; + case 32: goto tr2011; + case 40: goto tr2012; + case 41: goto tr2013; + case 1034: goto tr2015; + case 1083: goto tr2016; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2014; + goto tr1904; +tr1903: + { + s->buffer_length = 0; + } + goto st665; +tr2017: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st665; +tr1897: + { + if (s->number64 <= UINT32_MAX) { + uint32_t num32 = htonl((uint32_t)s->number64); + memcpy(rdata_tail, &num32, 4); + rdata_tail += 4; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st665; +st665: + if ( ++p == pe ) + goto _test_eof665; +case 665: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2018; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2017; + goto tr71; +tr1891: + { + s->buffer_length = 0; + } + goto st666; +tr2019: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st666; +tr1884: + { + rdata_tail += s->dname_tmp_length; + } + { + s->buffer_length = 0; + } + goto st666; +st666: + if ( ++p == pe ) + goto _test_eof666; +case 666: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2020; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2019; + goto tr71; +tr1879: + { + s->buffer_length = 0; + } + goto st667; +tr2021: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st667; +tr1873: + { + rdata_tail += s->dname_tmp_length; + } + { + s->buffer_length = 0; + } + goto st667; +st667: + if ( ++p == pe ) + goto _test_eof667; +case 667: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2022; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2021; + goto tr71; +st668: + if ( ++p == pe ) + goto _test_eof668; +case 668: + switch( (*p) ) { + case 32: goto tr71; + case 59: goto tr71; + } + if ( (*p) > 10 ) { + if ( 40 <= (*p) && (*p) <= 41 ) + goto tr71; + } else if ( (*p) >= 9 ) + goto tr71; + goto tr2023; +tr2023: + { + if (rdata_tail <= rdata_stop) { + s->item_length_location = rdata_tail++; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + { p--; {stack[top++] = 669;goto st279;} } + goto st669; +st669: + if ( ++p == pe ) + goto _test_eof669; +case 669: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr2024; + case 32: goto tr2024; + case 40: goto tr2025; + case 41: goto tr2026; + case 1034: goto tr2027; + case 1083: goto tr2028; + } + goto tr71; +tr2031: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st670; +tr2032: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st670; +tr2033: + { + s->line_counter++; + } + goto st670; +tr2037: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st670; +tr2024: + { + s->item_length = rdata_tail - s->item_length_location - 1; + + if (s->item_length <= MAX_ITEM_LENGTH) { + *(s->item_length_location) = (uint8_t)(s->item_length); + } else { + WARN(ZS_ITEM_OVERFLOW); + p--; {goto st268;} + } + } + goto st670; +tr2025: + { + s->item_length = rdata_tail - s->item_length_location - 1; + + if (s->item_length <= MAX_ITEM_LENGTH) { + *(s->item_length_location) = (uint8_t)(s->item_length); + } else { + WARN(ZS_ITEM_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st670; +tr2026: + { + s->item_length = rdata_tail - s->item_length_location - 1; + + if (s->item_length <= MAX_ITEM_LENGTH) { + *(s->item_length_location) = (uint8_t)(s->item_length); + } else { + WARN(ZS_ITEM_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st670; +tr2027: + { + s->item_length = rdata_tail - s->item_length_location - 1; + + if (s->item_length <= MAX_ITEM_LENGTH) { + *(s->item_length_location) = (uint8_t)(s->item_length); + } else { + WARN(ZS_ITEM_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st670; +st670: + if ( ++p == pe ) + goto _test_eof670; +case 670: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st670; + case 32: goto st670; + case 40: goto tr2031; + case 41: goto tr2032; + case 1034: goto tr2033; + case 1083: goto tr2034; + } + if ( _widec < 11 ) { + if ( _widec <= 8 ) + goto tr2029; + } else if ( _widec > 58 ) { + if ( 60 <= _widec ) + goto tr2029; + } else + goto tr2029; + goto tr71; +tr2029: + { + if (rdata_tail <= rdata_stop) { + s->item_length_location = rdata_tail++; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + { p--; {stack[top++] = 671;goto st279;} } + goto st671; +st671: + if ( ++p == pe ) + goto _test_eof671; +case 671: + switch( (*p) ) { + case 32: goto tr2035; + case 59: goto tr2035; + } + if ( (*p) > 10 ) { + if ( 40 <= (*p) && (*p) <= 41 ) + goto tr2035; + } else if ( (*p) >= 9 ) + goto tr2035; + goto tr71; +tr2035: + { + s->item_length = rdata_tail - s->item_length_location - 1; + + if (s->item_length <= MAX_ITEM_LENGTH) { + *(s->item_length_location) = (uint8_t)(s->item_length); + } else { + WARN(ZS_ITEM_OVERFLOW); + p--; {goto st268;} + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1164; +st1164: + if ( ++p == pe ) + goto _test_eof1164; +case 1164: + goto st0; +tr2034: + { + s->buffer_length = 0; + } + goto st672; +tr2036: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st672; +tr2028: + { + s->item_length = rdata_tail - s->item_length_location - 1; + + if (s->item_length <= MAX_ITEM_LENGTH) { + *(s->item_length_location) = (uint8_t)(s->item_length); + } else { + WARN(ZS_ITEM_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st672; +st672: + if ( ++p == pe ) + goto _test_eof672; +case 672: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2037; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2036; + goto tr71; +st673: + if ( ++p == pe ) + goto _test_eof673; +case 673: + switch( (*p) ) { + case 42: goto tr2038; + case 92: goto tr2038; + case 95: goto tr2038; + } + if ( (*p) < 64 ) { + if ( 45 <= (*p) && (*p) <= 57 ) + goto tr2038; + } else if ( (*p) > 90 ) { + if ( 97 <= (*p) && (*p) <= 122 ) + goto tr2038; + } else + goto tr2038; + goto tr71; +tr2038: + { + s->dname = rdata_tail; + } + { p--; {stack[top++] = 674;goto st270;} } + goto st674; +st674: + if ( ++p == pe ) + goto _test_eof674; +case 674: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr2039; + case 32: goto tr2039; + case 40: goto tr2040; + case 41: goto tr2041; + case 1034: goto tr2042; + case 1083: goto tr2043; + } + goto tr71; +tr2045: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st675; +tr2046: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st675; +tr2048: + { + s->line_counter++; + } + goto st675; +tr2052: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st675; +tr2039: + { + rdata_tail += s->dname_tmp_length; + } + goto st675; +tr2040: + { + rdata_tail += s->dname_tmp_length; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st675; +tr2041: + { + rdata_tail += s->dname_tmp_length; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st675; +tr2042: + { + rdata_tail += s->dname_tmp_length; + } + { + s->line_counter++; + } + goto st675; +st675: + if ( ++p == pe ) + goto _test_eof675; +case 675: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st675; + case 32: goto st675; + case 40: goto tr2045; + case 41: goto tr2046; + case 42: goto tr2047; + case 92: goto tr2047; + case 95: goto tr2047; + case 1034: goto tr2048; + case 1083: goto tr2049; + } + if ( _widec < 64 ) { + if ( 45 <= _widec && _widec <= 57 ) + goto tr2047; + } else if ( _widec > 90 ) { + if ( 97 <= _widec && _widec <= 122 ) + goto tr2047; + } else + goto tr2047; + goto tr71; +tr2047: + { + s->dname = rdata_tail; + } + { p--; {stack[top++] = 676;goto st270;} } + goto st676; +st676: + if ( ++p == pe ) + goto _test_eof676; +case 676: + switch( (*p) ) { + case 32: goto tr2050; + case 59: goto tr2050; + } + if ( (*p) > 10 ) { + if ( 40 <= (*p) && (*p) <= 41 ) + goto tr2050; + } else if ( (*p) >= 9 ) + goto tr2050; + goto tr71; +tr2050: + { + rdata_tail += s->dname_tmp_length; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1165; +st1165: + if ( ++p == pe ) + goto _test_eof1165; +case 1165: + goto st0; +tr2049: + { + s->buffer_length = 0; + } + goto st677; +tr2051: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st677; +tr2043: + { + rdata_tail += s->dname_tmp_length; + } + { + s->buffer_length = 0; + } + goto st677; +st677: + if ( ++p == pe ) + goto _test_eof677; +case 677: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2052; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2051; + goto tr71; +st678: + if ( ++p == pe ) + goto _test_eof678; +case 678: + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr2053; + goto tr1885; +tr2053: + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st679; +tr2057: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st679; +st679: + if ( ++p == pe ) + goto _test_eof679; +case 679: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr2054; + case 32: goto tr2054; + case 40: goto tr2055; + case 41: goto tr2056; + case 1034: goto tr2058; + case 1083: goto tr2059; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2057; + goto tr1885; +tr2061: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st680; +tr2062: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st680; +tr2064: + { + s->line_counter++; + } + goto st680; +tr2068: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st680; +tr2054: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + goto st680; +tr2055: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st680; +tr2056: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st680; +tr2058: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st680; +st680: + if ( ++p == pe ) + goto _test_eof680; +case 680: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st680; + case 32: goto st680; + case 40: goto tr2061; + case 41: goto tr2062; + case 42: goto tr2063; + case 92: goto tr2063; + case 95: goto tr2063; + case 1034: goto tr2064; + case 1083: goto tr2065; + } + if ( _widec < 64 ) { + if ( 45 <= _widec && _widec <= 57 ) + goto tr2063; + } else if ( _widec > 90 ) { + if ( 97 <= _widec && _widec <= 122 ) + goto tr2063; + } else + goto tr2063; + goto tr71; +tr2063: + { + s->dname = rdata_tail; + } + { p--; {stack[top++] = 681;goto st270;} } + goto st681; +st681: + if ( ++p == pe ) + goto _test_eof681; +case 681: + switch( (*p) ) { + case 32: goto tr2066; + case 59: goto tr2066; + } + if ( (*p) > 10 ) { + if ( 40 <= (*p) && (*p) <= 41 ) + goto tr2066; + } else if ( (*p) >= 9 ) + goto tr2066; + goto tr71; +tr2066: + { + rdata_tail += s->dname_tmp_length; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1166; +st1166: + if ( ++p == pe ) + goto _test_eof1166; +case 1166: + goto st0; +tr2065: + { + s->buffer_length = 0; + } + goto st682; +tr2067: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st682; +tr2059: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st682; +st682: + if ( ++p == pe ) + goto _test_eof682; +case 682: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2068; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2067; + goto tr71; +st683: + if ( ++p == pe ) + goto _test_eof683; +case 683: + switch( (*p) ) { + case 32: goto tr2070; + case 59: goto tr2070; + } + if ( (*p) > 10 ) { + if ( 40 <= (*p) && (*p) <= 41 ) + goto tr2070; + } else if ( (*p) >= 9 ) + goto tr2070; + goto tr2069; +tr2079: + { + if (rdata_tail <= rdata_stop) { + s->item_length_location = rdata_tail++; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + { p--; {stack[top++] = 684;goto st279;} } + goto st684; +tr2069: + { + s->long_string = true; + } + { + if (rdata_tail <= rdata_stop) { + s->item_length_location = rdata_tail++; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + { p--; {stack[top++] = 684;goto st279;} } + goto st684; +st684: + if ( ++p == pe ) + goto _test_eof684; +case 684: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr2071; + case 32: goto tr2071; + case 40: goto tr2072; + case 41: goto tr2073; + case 2058: goto tr2074; + case 2107: goto tr2075; + case 2314: goto tr2076; + case 2363: goto tr2076; + case 2570: goto tr2077; + case 2619: goto tr2078; + } + goto tr2070; +tr2081: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st685; +tr2082: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st685; +tr2083: + { + s->line_counter++; + } + goto st685; +tr2089: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st685; +tr2071: + { + s->item_length = rdata_tail - s->item_length_location - 1; + + if (s->item_length <= MAX_ITEM_LENGTH) { + *(s->item_length_location) = (uint8_t)(s->item_length); + } else { + WARN(ZS_ITEM_OVERFLOW); + p--; {goto st268;} + } + } + goto st685; +tr2072: + { + s->item_length = rdata_tail - s->item_length_location - 1; + + if (s->item_length <= MAX_ITEM_LENGTH) { + *(s->item_length_location) = (uint8_t)(s->item_length); + } else { + WARN(ZS_ITEM_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st685; +tr2073: + { + s->item_length = rdata_tail - s->item_length_location - 1; + + if (s->item_length <= MAX_ITEM_LENGTH) { + *(s->item_length_location) = (uint8_t)(s->item_length); + } else { + WARN(ZS_ITEM_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st685; +tr2074: + { + s->item_length = rdata_tail - s->item_length_location - 1; + + if (s->item_length <= MAX_ITEM_LENGTH) { + *(s->item_length_location) = (uint8_t)(s->item_length); + } else { + WARN(ZS_ITEM_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st685; +st685: + if ( ++p == pe ) + goto _test_eof685; +case 685: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto st685; + case 32: goto st685; + case 40: goto tr2081; + case 41: goto tr2082; + case 2058: goto tr2083; + case 2107: goto tr2084; + case 2314: goto tr2085; + case 2363: goto tr2085; + case 2570: goto tr2086; + case 2619: goto tr2087; + } + if ( _widec < 11 ) { + if ( _widec <= 8 ) + goto tr2079; + } else if ( _widec > 58 ) { + if ( 60 <= _widec ) + goto tr2079; + } else + goto tr2079; + goto tr2070; +tr2084: + { + s->buffer_length = 0; + } + goto st686; +tr2088: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st686; +tr2075: + { + s->item_length = rdata_tail - s->item_length_location - 1; + + if (s->item_length <= MAX_ITEM_LENGTH) { + *(s->item_length_location) = (uint8_t)(s->item_length); + } else { + WARN(ZS_ITEM_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st686; +st686: + if ( ++p == pe ) + goto _test_eof686; +case 686: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2089; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2088; + goto tr2070; +tr2076: + { + s->item_length = rdata_tail - s->item_length_location - 1; + + if (s->item_length <= MAX_ITEM_LENGTH) { + *(s->item_length_location) = (uint8_t)(s->item_length); + } else { + WARN(ZS_ITEM_OVERFLOW); + p--; {goto st268;} + } + } + { + s->long_string = false; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1167; +tr2085: + { + s->long_string = false; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1167; +st1167: + if ( ++p == pe ) + goto _test_eof1167; +case 1167: + goto st0; +tr2077: + { + s->item_length = rdata_tail - s->item_length_location - 1; + + if (s->item_length <= MAX_ITEM_LENGTH) { + *(s->item_length_location) = (uint8_t)(s->item_length); + } else { + WARN(ZS_ITEM_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + { + s->long_string = false; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1168; +tr2086: + { + s->line_counter++; + } + { + s->long_string = false; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1168; +st1168: + if ( ++p == pe ) + goto _test_eof1168; +case 1168: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto st685; + case 32: goto st685; + case 40: goto tr2081; + case 41: goto tr2082; + case 2058: goto tr2083; + case 2107: goto tr2084; + case 2314: goto tr2085; + case 2363: goto tr2085; + case 2570: goto tr2086; + case 2619: goto tr2087; + } + if ( _widec < 11 ) { + if ( _widec <= 8 ) + goto tr2079; + } else if ( _widec > 58 ) { + if ( 60 <= _widec ) + goto tr2079; + } else + goto tr2079; + goto tr2070; +tr2078: + { + s->item_length = rdata_tail - s->item_length_location - 1; + + if (s->item_length <= MAX_ITEM_LENGTH) { + *(s->item_length_location) = (uint8_t)(s->item_length); + } else { + WARN(ZS_ITEM_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + { + s->long_string = false; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1169; +tr2087: + { + s->buffer_length = 0; + } + { + s->long_string = false; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1169; +st1169: + if ( ++p == pe ) + goto _test_eof1169; +case 1169: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2089; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2088; + goto tr2070; +st687: + if ( ++p == pe ) + goto _test_eof687; +case 687: + if ( (*p) == 46 ) + goto tr2090; + if ( (*p) < 65 ) { + if ( 48 <= (*p) && (*p) <= 58 ) + goto tr2090; + } else if ( (*p) > 70 ) { + if ( 97 <= (*p) && (*p) <= 102 ) + goto tr2090; + } else + goto tr2090; + goto tr1862; +tr2090: + { + s->buffer_length = 0; + } + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + goto st688; +tr2092: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + goto st688; +st688: + if ( ++p == pe ) + goto _test_eof688; +case 688: + switch( (*p) ) { + case 32: goto tr2091; + case 46: goto tr2092; + case 59: goto tr2091; + } + if ( (*p) < 48 ) { + if ( (*p) > 10 ) { + if ( 40 <= (*p) && (*p) <= 41 ) + goto tr2091; + } else if ( (*p) >= 9 ) + goto tr2091; + } else if ( (*p) > 58 ) { + if ( (*p) > 70 ) { + if ( 97 <= (*p) && (*p) <= 102 ) + goto tr2092; + } else if ( (*p) >= 65 ) + goto tr2092; + } else + goto tr2092; + goto tr1862; +tr2091: + { + s->buffer[s->buffer_length] = 0; + + if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) { + WARN(ZS_BAD_IPV6); + p--; {goto st268;} + } + } + { + memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH); + rdata_tail += ZS_INET6_ADDR_LENGTH; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1170; +st1170: + if ( ++p == pe ) + goto _test_eof1170; +case 1170: + goto st0; +st689: + if ( ++p == pe ) + goto _test_eof689; +case 689: + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr2094; + goto tr2093; +tr2098: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st690; +tr2094: + { + memset(&(s->loc), 0, sizeof(s->loc)); + // Defaults. + s->loc.siz = 100; + s->loc.vp = 1000; + s->loc.hp = 1000000; + s->loc.lat_sign = 1; + s->loc.long_sign = 1; + s->loc.alt_sign = 1; + } + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st690; +st690: + if ( ++p == pe ) + goto _test_eof690; +case 690: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr2095; + case 32: goto tr2095; + case 40: goto tr2096; + case 41: goto tr2097; + case 1034: goto tr2099; + case 1083: goto tr2100; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2098; + goto tr2093; +tr2102: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st691; +tr2103: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st691; +tr2107: + { + s->line_counter++; + } + goto st691; +tr2350: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st691; +tr2095: + { + if (s->number64 <= 90) { + s->loc.d1 = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + goto st691; +tr2096: + { + if (s->number64 <= 90) { + s->loc.d1 = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st691; +tr2097: + { + if (s->number64 <= 90) { + s->loc.d1 = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st691; +tr2099: + { + if (s->number64 <= 90) { + s->loc.d1 = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st691; +st691: + if ( ++p == pe ) + goto _test_eof691; +case 691: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st691; + case 32: goto st691; + case 40: goto tr2102; + case 41: goto tr2103; + case 78: goto st696; + case 83: goto st738; + case 1034: goto tr2107; + case 1083: goto tr2108; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2104; + goto tr2093; +tr2104: + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st692; +tr2112: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st692; +st692: + if ( ++p == pe ) + goto _test_eof692; +case 692: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr2109; + case 32: goto tr2109; + case 40: goto tr2110; + case 41: goto tr2111; + case 1034: goto tr2113; + case 1083: goto tr2114; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2112; + goto tr2093; +tr2116: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st693; +tr2117: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st693; +tr2119: + { + s->line_counter++; + } + goto st693; +tr2348: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st693; +tr2109: + { + if (s->number64 <= 59) { + s->loc.m1 = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + goto st693; +tr2110: + { + if (s->number64 <= 59) { + s->loc.m1 = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st693; +tr2111: + { + if (s->number64 <= 59) { + s->loc.m1 = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st693; +tr2113: + { + if (s->number64 <= 59) { + s->loc.m1 = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st693; +st693: + if ( ++p == pe ) + goto _test_eof693; +case 693: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st693; + case 32: goto st693; + case 40: goto tr2116; + case 41: goto tr2117; + case 78: goto st696; + case 83: goto st738; + case 1034: goto tr2119; + case 1083: goto tr2120; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2118; + goto tr2093; +tr2125: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st694; +tr2118: + { + s->decimals = 3; + } + { + s->decimal_counter = 0; + } + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st694; +st694: + if ( ++p == pe ) + goto _test_eof694; +case 694: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr2121; + case 32: goto tr2121; + case 40: goto tr2122; + case 41: goto tr2123; + case 46: goto st740; + case 1034: goto tr2126; + case 1083: goto tr2127; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2125; + goto tr2093; +tr2129: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st695; +tr2130: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st695; +tr2131: + { + s->line_counter++; + } + goto st695; +tr2339: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st695; +tr2121: + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 59999) { + s->loc.s1 = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + goto st695; +tr2122: + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 59999) { + s->loc.s1 = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st695; +tr2123: + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 59999) { + s->loc.s1 = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st695; +tr2126: + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 59999) { + s->loc.s1 = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st695; +tr2340: + { + s->number64_tmp = s->number64; + } + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 59999) { + s->loc.s1 = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + goto st695; +tr2341: + { + s->number64_tmp = s->number64; + } + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 59999) { + s->loc.s1 = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st695; +tr2342: + { + s->number64_tmp = s->number64; + } + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 59999) { + s->loc.s1 = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st695; +tr2344: + { + s->number64_tmp = s->number64; + } + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 59999) { + s->loc.s1 = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st695; +st695: + if ( ++p == pe ) + goto _test_eof695; +case 695: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st695; + case 32: goto st695; + case 40: goto tr2129; + case 41: goto tr2130; + case 78: goto st696; + case 83: goto st738; + case 1034: goto tr2131; + case 1083: goto tr2132; + } + goto tr2093; +st696: + if ( ++p == pe ) + goto _test_eof696; +case 696: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st697; + case 32: goto st697; + case 40: goto tr2134; + case 41: goto tr2135; + case 1034: goto tr2136; + case 1083: goto tr2137; + } + goto tr2093; +tr2134: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st697; +tr2135: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st697; +tr2136: + { + s->line_counter++; + } + goto st697; +tr2332: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st697; +tr2333: + { + s->loc.lat_sign = -1; + } + goto st697; +tr2334: + { + s->loc.lat_sign = -1; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st697; +tr2335: + { + s->loc.lat_sign = -1; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st697; +tr2336: + { + s->loc.lat_sign = -1; + } + { + s->line_counter++; + } + goto st697; +st697: + if ( ++p == pe ) + goto _test_eof697; +case 697: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st697; + case 32: goto st697; + case 40: goto tr2134; + case 41: goto tr2135; + case 1034: goto tr2136; + case 1083: goto tr2137; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2138; + goto tr2093; +tr2138: + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st698; +tr2142: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st698; +st698: + if ( ++p == pe ) + goto _test_eof698; +case 698: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr2139; + case 32: goto tr2139; + case 40: goto tr2140; + case 41: goto tr2141; + case 1034: goto tr2143; + case 1083: goto tr2144; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2142; + goto tr2093; +tr2146: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st699; +tr2147: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st699; +tr2151: + { + s->line_counter++; + } + goto st699; +tr2330: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st699; +tr2139: + { + if (s->number64 <= 180) { + s->loc.d2 = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + goto st699; +tr2140: + { + if (s->number64 <= 180) { + s->loc.d2 = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st699; +tr2141: + { + if (s->number64 <= 180) { + s->loc.d2 = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st699; +tr2143: + { + if (s->number64 <= 180) { + s->loc.d2 = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st699; +st699: + if ( ++p == pe ) + goto _test_eof699; +case 699: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st699; + case 32: goto st699; + case 40: goto tr2146; + case 41: goto tr2147; + case 69: goto st704; + case 87: goto st731; + case 1034: goto tr2151; + case 1083: goto tr2152; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2148; + goto tr2093; +tr2148: + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st700; +tr2156: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st700; +st700: + if ( ++p == pe ) + goto _test_eof700; +case 700: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr2153; + case 32: goto tr2153; + case 40: goto tr2154; + case 41: goto tr2155; + case 1034: goto tr2157; + case 1083: goto tr2158; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2156; + goto tr2093; +tr2160: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st701; +tr2161: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st701; +tr2163: + { + s->line_counter++; + } + goto st701; +tr2328: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st701; +tr2153: + { + if (s->number64 <= 59) { + s->loc.m2 = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + goto st701; +tr2154: + { + if (s->number64 <= 59) { + s->loc.m2 = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st701; +tr2155: + { + if (s->number64 <= 59) { + s->loc.m2 = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st701; +tr2157: + { + if (s->number64 <= 59) { + s->loc.m2 = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st701; +st701: + if ( ++p == pe ) + goto _test_eof701; +case 701: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st701; + case 32: goto st701; + case 40: goto tr2160; + case 41: goto tr2161; + case 69: goto st704; + case 87: goto st731; + case 1034: goto tr2163; + case 1083: goto tr2164; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2162; + goto tr2093; +tr2169: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st702; +tr2162: + { + s->decimals = 3; + } + { + s->decimal_counter = 0; + } + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st702; +st702: + if ( ++p == pe ) + goto _test_eof702; +case 702: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr2165; + case 32: goto tr2165; + case 40: goto tr2166; + case 41: goto tr2167; + case 46: goto st733; + case 1034: goto tr2170; + case 1083: goto tr2171; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2169; + goto tr2093; +tr2173: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st703; +tr2174: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st703; +tr2175: + { + s->line_counter++; + } + goto st703; +tr2319: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st703; +tr2165: + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 59999) { + s->loc.s2 = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + goto st703; +tr2166: + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 59999) { + s->loc.s2 = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st703; +tr2167: + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 59999) { + s->loc.s2 = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st703; +tr2170: + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 59999) { + s->loc.s2 = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st703; +tr2320: + { + s->number64_tmp = s->number64; + } + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 59999) { + s->loc.s2 = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + goto st703; +tr2321: + { + s->number64_tmp = s->number64; + } + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 59999) { + s->loc.s2 = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st703; +tr2322: + { + s->number64_tmp = s->number64; + } + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 59999) { + s->loc.s2 = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st703; +tr2324: + { + s->number64_tmp = s->number64; + } + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 59999) { + s->loc.s2 = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st703; +st703: + if ( ++p == pe ) + goto _test_eof703; +case 703: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st703; + case 32: goto st703; + case 40: goto tr2173; + case 41: goto tr2174; + case 69: goto st704; + case 87: goto st731; + case 1034: goto tr2175; + case 1083: goto tr2176; + } + goto tr2093; +st704: + if ( ++p == pe ) + goto _test_eof704; +case 704: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st705; + case 32: goto st705; + case 40: goto tr2178; + case 41: goto tr2179; + case 1034: goto tr2180; + case 1083: goto tr2181; + } + goto tr2093; +tr2178: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st705; +tr2179: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st705; +tr2180: + { + s->line_counter++; + } + goto st705; +tr2312: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st705; +tr2313: + { + s->loc.long_sign = -1; + } + goto st705; +tr2314: + { + s->loc.long_sign = -1; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st705; +tr2315: + { + s->loc.long_sign = -1; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st705; +tr2316: + { + s->loc.long_sign = -1; + } + { + s->line_counter++; + } + goto st705; +st705: + if ( ++p == pe ) + goto _test_eof705; +case 705: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st705; + case 32: goto st705; + case 40: goto tr2178; + case 41: goto tr2179; + case 45: goto st706; + case 1034: goto tr2180; + case 1083: goto tr2181; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2183; + goto tr2093; +st706: + if ( ++p == pe ) + goto _test_eof706; +case 706: + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr2184; + goto tr2093; +tr2189: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st707; +tr2183: + { + s->decimals = 2; + } + { + s->decimal_counter = 0; + } + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st707; +tr2184: + { + s->loc.alt_sign = -1; + } + { + s->decimals = 2; + } + { + s->decimal_counter = 0; + } + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st707; +st707: + if ( ++p == pe ) + goto _test_eof707; +case 707: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr2185; + case 32: goto tr2185; + case 40: goto tr2186; + case 41: goto tr2187; + case 46: goto st727; + case 109: goto tr2190; + case 2058: goto tr2191; + case 2107: goto tr2192; + case 2314: goto tr2193; + case 2363: goto tr2193; + case 2570: goto tr2194; + case 2619: goto tr2195; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2189; + goto tr2093; +tr2197: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st708; +tr2198: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st708; +tr2200: + { + s->line_counter++; + } + goto st708; +tr2299: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st708; +tr2185: + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if ((s->loc.alt_sign == 1 && s->number64 <= 4284967295) || + (s->loc.alt_sign == -1 && s->number64 <= 10000000)) + { + s->loc.alt = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + goto st708; +tr2186: + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if ((s->loc.alt_sign == 1 && s->number64 <= 4284967295) || + (s->loc.alt_sign == -1 && s->number64 <= 10000000)) + { + s->loc.alt = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st708; +tr2187: + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if ((s->loc.alt_sign == 1 && s->number64 <= 4284967295) || + (s->loc.alt_sign == -1 && s->number64 <= 10000000)) + { + s->loc.alt = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st708; +tr2191: + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if ((s->loc.alt_sign == 1 && s->number64 <= 4284967295) || + (s->loc.alt_sign == -1 && s->number64 <= 10000000)) + { + s->loc.alt = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st708; +tr2300: + { + s->number64_tmp = s->number64; + } + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if ((s->loc.alt_sign == 1 && s->number64 <= 4284967295) || + (s->loc.alt_sign == -1 && s->number64 <= 10000000)) + { + s->loc.alt = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + goto st708; +tr2301: + { + s->number64_tmp = s->number64; + } + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if ((s->loc.alt_sign == 1 && s->number64 <= 4284967295) || + (s->loc.alt_sign == -1 && s->number64 <= 10000000)) + { + s->loc.alt = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st708; +tr2302: + { + s->number64_tmp = s->number64; + } + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if ((s->loc.alt_sign == 1 && s->number64 <= 4284967295) || + (s->loc.alt_sign == -1 && s->number64 <= 10000000)) + { + s->loc.alt = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st708; +tr2305: + { + s->number64_tmp = s->number64; + } + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if ((s->loc.alt_sign == 1 && s->number64 <= 4284967295) || + (s->loc.alt_sign == -1 && s->number64 <= 10000000)) + { + s->loc.alt = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st708; +st708: + if ( ++p == pe ) + goto _test_eof708; +case 708: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto st708; + case 32: goto st708; + case 40: goto tr2197; + case 41: goto tr2198; + case 2058: goto tr2200; + case 2107: goto tr2201; + case 2314: goto tr2202; + case 2363: goto tr2202; + case 2570: goto tr2203; + case 2619: goto tr2204; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2199; + goto tr2093; +tr2209: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st709; +tr2199: + { + s->decimals = 2; + } + { + s->decimal_counter = 0; + } + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st709; +st709: + if ( ++p == pe ) + goto _test_eof709; +case 709: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr2205; + case 32: goto tr2205; + case 40: goto tr2206; + case 41: goto tr2207; + case 46: goto st723; + case 109: goto tr2210; + case 2058: goto tr2211; + case 2107: goto tr2212; + case 2314: goto tr2213; + case 2363: goto tr2213; + case 2570: goto tr2214; + case 2619: goto tr2215; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2209; + goto tr2093; +tr2217: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st710; +tr2218: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st710; +tr2220: + { + s->line_counter++; + } + goto st710; +tr2286: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st710; +tr2205: + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 9000000000ULL) { + s->loc.siz = s->number64; + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + goto st710; +tr2206: + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 9000000000ULL) { + s->loc.siz = s->number64; + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st710; +tr2207: + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 9000000000ULL) { + s->loc.siz = s->number64; + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st710; +tr2211: + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 9000000000ULL) { + s->loc.siz = s->number64; + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st710; +tr2287: + { + s->number64_tmp = s->number64; + } + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 9000000000ULL) { + s->loc.siz = s->number64; + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + goto st710; +tr2288: + { + s->number64_tmp = s->number64; + } + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 9000000000ULL) { + s->loc.siz = s->number64; + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st710; +tr2289: + { + s->number64_tmp = s->number64; + } + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 9000000000ULL) { + s->loc.siz = s->number64; + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st710; +tr2292: + { + s->number64_tmp = s->number64; + } + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 9000000000ULL) { + s->loc.siz = s->number64; + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st710; +st710: + if ( ++p == pe ) + goto _test_eof710; +case 710: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto st710; + case 32: goto st710; + case 40: goto tr2217; + case 41: goto tr2218; + case 2058: goto tr2220; + case 2107: goto tr2221; + case 2314: goto tr2202; + case 2363: goto tr2202; + case 2570: goto tr2222; + case 2619: goto tr2223; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2219; + goto tr2093; +tr2228: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st711; +tr2219: + { + s->decimals = 2; + } + { + s->decimal_counter = 0; + } + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st711; +st711: + if ( ++p == pe ) + goto _test_eof711; +case 711: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr2224; + case 32: goto tr2224; + case 40: goto tr2225; + case 41: goto tr2226; + case 46: goto st719; + case 109: goto tr2229; + case 2058: goto tr2230; + case 2107: goto tr2231; + case 2314: goto tr2232; + case 2363: goto tr2232; + case 2570: goto tr2233; + case 2619: goto tr2234; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2228; + goto tr2093; +tr2236: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st712; +tr2237: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st712; +tr2239: + { + s->line_counter++; + } + goto st712; +tr2273: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st712; +tr2224: + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 9000000000ULL) { + s->loc.hp = s->number64; + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + goto st712; +tr2225: + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 9000000000ULL) { + s->loc.hp = s->number64; + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st712; +tr2226: + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 9000000000ULL) { + s->loc.hp = s->number64; + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st712; +tr2230: + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 9000000000ULL) { + s->loc.hp = s->number64; + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st712; +tr2274: + { + s->number64_tmp = s->number64; + } + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 9000000000ULL) { + s->loc.hp = s->number64; + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + goto st712; +tr2275: + { + s->number64_tmp = s->number64; + } + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 9000000000ULL) { + s->loc.hp = s->number64; + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st712; +tr2276: + { + s->number64_tmp = s->number64; + } + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 9000000000ULL) { + s->loc.hp = s->number64; + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st712; +tr2279: + { + s->number64_tmp = s->number64; + } + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 9000000000ULL) { + s->loc.hp = s->number64; + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st712; +st712: + if ( ++p == pe ) + goto _test_eof712; +case 712: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto st712; + case 32: goto st712; + case 40: goto tr2236; + case 41: goto tr2237; + case 2058: goto tr2239; + case 2107: goto tr2240; + case 2314: goto tr2202; + case 2363: goto tr2202; + case 2570: goto tr2241; + case 2619: goto tr2242; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2238; + goto tr2093; +tr2247: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st713; +tr2238: + { + s->decimals = 2; + } + { + s->decimal_counter = 0; + } + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st713; +st713: + if ( ++p == pe ) + goto _test_eof713; +case 713: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr2243; + case 32: goto tr2243; + case 40: goto tr2244; + case 41: goto tr2245; + case 46: goto st716; + case 109: goto tr2243; + case 2058: goto tr2248; + case 2107: goto tr2249; + case 2314: goto tr2250; + case 2363: goto tr2250; + case 2570: goto tr2251; + case 2619: goto tr2252; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2247; + goto tr2093; +tr2254: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st714; +tr2255: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st714; +tr2256: + { + s->line_counter++; + } + goto st714; +tr2261: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st714; +tr2243: + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 9000000000ULL) { + s->loc.vp = s->number64; + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + goto st714; +tr2244: + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 9000000000ULL) { + s->loc.vp = s->number64; + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st714; +tr2245: + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 9000000000ULL) { + s->loc.vp = s->number64; + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st714; +tr2248: + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 9000000000ULL) { + s->loc.vp = s->number64; + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st714; +tr2262: + { + s->number64_tmp = s->number64; + } + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 9000000000ULL) { + s->loc.vp = s->number64; + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + goto st714; +tr2263: + { + s->number64_tmp = s->number64; + } + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 9000000000ULL) { + s->loc.vp = s->number64; + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st714; +tr2264: + { + s->number64_tmp = s->number64; + } + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 9000000000ULL) { + s->loc.vp = s->number64; + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st714; +tr2266: + { + s->number64_tmp = s->number64; + } + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 9000000000ULL) { + s->loc.vp = s->number64; + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st714; +st714: + if ( ++p == pe ) + goto _test_eof714; +case 714: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto st714; + case 32: goto st714; + case 40: goto tr2254; + case 41: goto tr2255; + case 2058: goto tr2256; + case 2107: goto tr2257; + case 2314: goto tr2202; + case 2363: goto tr2202; + case 2570: goto tr2258; + case 2619: goto tr2259; + } + goto tr2093; +tr2257: + { + s->buffer_length = 0; + } + goto st715; +tr2260: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st715; +tr2249: + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 9000000000ULL) { + s->loc.vp = s->number64; + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st715; +tr2267: + { + s->number64_tmp = s->number64; + } + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 9000000000ULL) { + s->loc.vp = s->number64; + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st715; +st715: + if ( ++p == pe ) + goto _test_eof715; +case 715: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2261; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2260; + goto tr2093; +tr2193: + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if ((s->loc.alt_sign == 1 && s->number64 <= 4284967295) || + (s->loc.alt_sign == -1 && s->number64 <= 10000000)) + { + s->loc.alt = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + // Write version. + *(rdata_tail) = 0; + rdata_tail += 1; + // Write size. + *(rdata_tail) = loc64to8(s->loc.siz); + rdata_tail += 1; + // Write horizontal precision. + *(rdata_tail) = loc64to8(s->loc.hp); + rdata_tail += 1; + // Write vertical precision. + *(rdata_tail) = loc64to8(s->loc.vp); + rdata_tail += 1; + // Write latitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign * + (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1)); + rdata_tail += 4; + // Write longitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign * + (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2)); + rdata_tail += 4; + // Write altitude. + *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign * + (s->loc.alt)); + rdata_tail += 4; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1171; +tr2202: + { + // Write version. + *(rdata_tail) = 0; + rdata_tail += 1; + // Write size. + *(rdata_tail) = loc64to8(s->loc.siz); + rdata_tail += 1; + // Write horizontal precision. + *(rdata_tail) = loc64to8(s->loc.hp); + rdata_tail += 1; + // Write vertical precision. + *(rdata_tail) = loc64to8(s->loc.vp); + rdata_tail += 1; + // Write latitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign * + (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1)); + rdata_tail += 4; + // Write longitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign * + (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2)); + rdata_tail += 4; + // Write altitude. + *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign * + (s->loc.alt)); + rdata_tail += 4; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1171; +tr2213: + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 9000000000ULL) { + s->loc.siz = s->number64; + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + // Write version. + *(rdata_tail) = 0; + rdata_tail += 1; + // Write size. + *(rdata_tail) = loc64to8(s->loc.siz); + rdata_tail += 1; + // Write horizontal precision. + *(rdata_tail) = loc64to8(s->loc.hp); + rdata_tail += 1; + // Write vertical precision. + *(rdata_tail) = loc64to8(s->loc.vp); + rdata_tail += 1; + // Write latitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign * + (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1)); + rdata_tail += 4; + // Write longitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign * + (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2)); + rdata_tail += 4; + // Write altitude. + *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign * + (s->loc.alt)); + rdata_tail += 4; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1171; +tr2232: + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 9000000000ULL) { + s->loc.hp = s->number64; + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + // Write version. + *(rdata_tail) = 0; + rdata_tail += 1; + // Write size. + *(rdata_tail) = loc64to8(s->loc.siz); + rdata_tail += 1; + // Write horizontal precision. + *(rdata_tail) = loc64to8(s->loc.hp); + rdata_tail += 1; + // Write vertical precision. + *(rdata_tail) = loc64to8(s->loc.vp); + rdata_tail += 1; + // Write latitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign * + (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1)); + rdata_tail += 4; + // Write longitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign * + (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2)); + rdata_tail += 4; + // Write altitude. + *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign * + (s->loc.alt)); + rdata_tail += 4; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1171; +tr2250: + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 9000000000ULL) { + s->loc.vp = s->number64; + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + // Write version. + *(rdata_tail) = 0; + rdata_tail += 1; + // Write size. + *(rdata_tail) = loc64to8(s->loc.siz); + rdata_tail += 1; + // Write horizontal precision. + *(rdata_tail) = loc64to8(s->loc.hp); + rdata_tail += 1; + // Write vertical precision. + *(rdata_tail) = loc64to8(s->loc.vp); + rdata_tail += 1; + // Write latitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign * + (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1)); + rdata_tail += 4; + // Write longitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign * + (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2)); + rdata_tail += 4; + // Write altitude. + *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign * + (s->loc.alt)); + rdata_tail += 4; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1171; +tr2268: + { + s->number64_tmp = s->number64; + } + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 9000000000ULL) { + s->loc.vp = s->number64; + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + // Write version. + *(rdata_tail) = 0; + rdata_tail += 1; + // Write size. + *(rdata_tail) = loc64to8(s->loc.siz); + rdata_tail += 1; + // Write horizontal precision. + *(rdata_tail) = loc64to8(s->loc.hp); + rdata_tail += 1; + // Write vertical precision. + *(rdata_tail) = loc64to8(s->loc.vp); + rdata_tail += 1; + // Write latitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign * + (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1)); + rdata_tail += 4; + // Write longitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign * + (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2)); + rdata_tail += 4; + // Write altitude. + *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign * + (s->loc.alt)); + rdata_tail += 4; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1171; +tr2281: + { + s->number64_tmp = s->number64; + } + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 9000000000ULL) { + s->loc.hp = s->number64; + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + // Write version. + *(rdata_tail) = 0; + rdata_tail += 1; + // Write size. + *(rdata_tail) = loc64to8(s->loc.siz); + rdata_tail += 1; + // Write horizontal precision. + *(rdata_tail) = loc64to8(s->loc.hp); + rdata_tail += 1; + // Write vertical precision. + *(rdata_tail) = loc64to8(s->loc.vp); + rdata_tail += 1; + // Write latitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign * + (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1)); + rdata_tail += 4; + // Write longitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign * + (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2)); + rdata_tail += 4; + // Write altitude. + *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign * + (s->loc.alt)); + rdata_tail += 4; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1171; +tr2294: + { + s->number64_tmp = s->number64; + } + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 9000000000ULL) { + s->loc.siz = s->number64; + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + // Write version. + *(rdata_tail) = 0; + rdata_tail += 1; + // Write size. + *(rdata_tail) = loc64to8(s->loc.siz); + rdata_tail += 1; + // Write horizontal precision. + *(rdata_tail) = loc64to8(s->loc.hp); + rdata_tail += 1; + // Write vertical precision. + *(rdata_tail) = loc64to8(s->loc.vp); + rdata_tail += 1; + // Write latitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign * + (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1)); + rdata_tail += 4; + // Write longitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign * + (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2)); + rdata_tail += 4; + // Write altitude. + *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign * + (s->loc.alt)); + rdata_tail += 4; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1171; +tr2307: + { + s->number64_tmp = s->number64; + } + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if ((s->loc.alt_sign == 1 && s->number64 <= 4284967295) || + (s->loc.alt_sign == -1 && s->number64 <= 10000000)) + { + s->loc.alt = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + // Write version. + *(rdata_tail) = 0; + rdata_tail += 1; + // Write size. + *(rdata_tail) = loc64to8(s->loc.siz); + rdata_tail += 1; + // Write horizontal precision. + *(rdata_tail) = loc64to8(s->loc.hp); + rdata_tail += 1; + // Write vertical precision. + *(rdata_tail) = loc64to8(s->loc.vp); + rdata_tail += 1; + // Write latitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign * + (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1)); + rdata_tail += 4; + // Write longitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign * + (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2)); + rdata_tail += 4; + // Write altitude. + *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign * + (s->loc.alt)); + rdata_tail += 4; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1171; +st1171: + if ( ++p == pe ) + goto _test_eof1171; +case 1171: + goto st0; +tr2258: + { + s->line_counter++; + } + { + // Write version. + *(rdata_tail) = 0; + rdata_tail += 1; + // Write size. + *(rdata_tail) = loc64to8(s->loc.siz); + rdata_tail += 1; + // Write horizontal precision. + *(rdata_tail) = loc64to8(s->loc.hp); + rdata_tail += 1; + // Write vertical precision. + *(rdata_tail) = loc64to8(s->loc.vp); + rdata_tail += 1; + // Write latitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign * + (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1)); + rdata_tail += 4; + // Write longitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign * + (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2)); + rdata_tail += 4; + // Write altitude. + *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign * + (s->loc.alt)); + rdata_tail += 4; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1172; +tr2251: + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 9000000000ULL) { + s->loc.vp = s->number64; + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + { + // Write version. + *(rdata_tail) = 0; + rdata_tail += 1; + // Write size. + *(rdata_tail) = loc64to8(s->loc.siz); + rdata_tail += 1; + // Write horizontal precision. + *(rdata_tail) = loc64to8(s->loc.hp); + rdata_tail += 1; + // Write vertical precision. + *(rdata_tail) = loc64to8(s->loc.vp); + rdata_tail += 1; + // Write latitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign * + (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1)); + rdata_tail += 4; + // Write longitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign * + (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2)); + rdata_tail += 4; + // Write altitude. + *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign * + (s->loc.alt)); + rdata_tail += 4; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1172; +tr2269: + { + s->number64_tmp = s->number64; + } + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 9000000000ULL) { + s->loc.vp = s->number64; + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + { + // Write version. + *(rdata_tail) = 0; + rdata_tail += 1; + // Write size. + *(rdata_tail) = loc64to8(s->loc.siz); + rdata_tail += 1; + // Write horizontal precision. + *(rdata_tail) = loc64to8(s->loc.hp); + rdata_tail += 1; + // Write vertical precision. + *(rdata_tail) = loc64to8(s->loc.vp); + rdata_tail += 1; + // Write latitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign * + (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1)); + rdata_tail += 4; + // Write longitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign * + (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2)); + rdata_tail += 4; + // Write altitude. + *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign * + (s->loc.alt)); + rdata_tail += 4; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1172; +st1172: + if ( ++p == pe ) + goto _test_eof1172; +case 1172: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto st714; + case 32: goto st714; + case 40: goto tr2254; + case 41: goto tr2255; + case 2058: goto tr2256; + case 2107: goto tr2257; + case 2314: goto tr2202; + case 2363: goto tr2202; + case 2570: goto tr2258; + case 2619: goto tr2259; + } + goto tr2093; +tr2259: + { + s->buffer_length = 0; + } + { + // Write version. + *(rdata_tail) = 0; + rdata_tail += 1; + // Write size. + *(rdata_tail) = loc64to8(s->loc.siz); + rdata_tail += 1; + // Write horizontal precision. + *(rdata_tail) = loc64to8(s->loc.hp); + rdata_tail += 1; + // Write vertical precision. + *(rdata_tail) = loc64to8(s->loc.vp); + rdata_tail += 1; + // Write latitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign * + (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1)); + rdata_tail += 4; + // Write longitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign * + (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2)); + rdata_tail += 4; + // Write altitude. + *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign * + (s->loc.alt)); + rdata_tail += 4; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1173; +tr2252: + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 9000000000ULL) { + s->loc.vp = s->number64; + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + { + // Write version. + *(rdata_tail) = 0; + rdata_tail += 1; + // Write size. + *(rdata_tail) = loc64to8(s->loc.siz); + rdata_tail += 1; + // Write horizontal precision. + *(rdata_tail) = loc64to8(s->loc.hp); + rdata_tail += 1; + // Write vertical precision. + *(rdata_tail) = loc64to8(s->loc.vp); + rdata_tail += 1; + // Write latitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign * + (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1)); + rdata_tail += 4; + // Write longitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign * + (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2)); + rdata_tail += 4; + // Write altitude. + *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign * + (s->loc.alt)); + rdata_tail += 4; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1173; +tr2270: + { + s->number64_tmp = s->number64; + } + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 9000000000ULL) { + s->loc.vp = s->number64; + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + { + // Write version. + *(rdata_tail) = 0; + rdata_tail += 1; + // Write size. + *(rdata_tail) = loc64to8(s->loc.siz); + rdata_tail += 1; + // Write horizontal precision. + *(rdata_tail) = loc64to8(s->loc.hp); + rdata_tail += 1; + // Write vertical precision. + *(rdata_tail) = loc64to8(s->loc.vp); + rdata_tail += 1; + // Write latitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign * + (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1)); + rdata_tail += 4; + // Write longitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign * + (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2)); + rdata_tail += 4; + // Write altitude. + *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign * + (s->loc.alt)); + rdata_tail += 4; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1173; +st1173: + if ( ++p == pe ) + goto _test_eof1173; +case 1173: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2261; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2260; + goto tr2093; +st716: + if ( ++p == pe ) + goto _test_eof716; +case 716: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr2262; + case 32: goto tr2262; + case 40: goto tr2263; + case 41: goto tr2264; + case 109: goto tr2262; + case 2058: goto tr2266; + case 2107: goto tr2267; + case 2314: goto tr2268; + case 2363: goto tr2268; + case 2570: goto tr2269; + case 2619: goto tr2270; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2265; + goto tr2093; +tr2265: + { + s->number64_tmp = s->number64; + } + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + { + s->decimal_counter++; + } + goto st717; +tr2271: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + { + s->decimal_counter++; + } + goto st717; +st717: + if ( ++p == pe ) + goto _test_eof717; +case 717: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr2243; + case 32: goto tr2243; + case 40: goto tr2244; + case 41: goto tr2245; + case 109: goto tr2243; + case 2058: goto tr2248; + case 2107: goto tr2249; + case 2314: goto tr2250; + case 2363: goto tr2250; + case 2570: goto tr2251; + case 2619: goto tr2252; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2271; + goto tr2093; +tr2240: + { + s->buffer_length = 0; + } + goto st718; +tr2272: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st718; +tr2231: + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 9000000000ULL) { + s->loc.hp = s->number64; + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st718; +tr2280: + { + s->number64_tmp = s->number64; + } + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 9000000000ULL) { + s->loc.hp = s->number64; + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st718; +st718: + if ( ++p == pe ) + goto _test_eof718; +case 718: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2273; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2272; + goto tr2093; +tr2241: + { + s->line_counter++; + } + { + // Write version. + *(rdata_tail) = 0; + rdata_tail += 1; + // Write size. + *(rdata_tail) = loc64to8(s->loc.siz); + rdata_tail += 1; + // Write horizontal precision. + *(rdata_tail) = loc64to8(s->loc.hp); + rdata_tail += 1; + // Write vertical precision. + *(rdata_tail) = loc64to8(s->loc.vp); + rdata_tail += 1; + // Write latitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign * + (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1)); + rdata_tail += 4; + // Write longitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign * + (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2)); + rdata_tail += 4; + // Write altitude. + *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign * + (s->loc.alt)); + rdata_tail += 4; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1174; +tr2233: + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 9000000000ULL) { + s->loc.hp = s->number64; + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + { + // Write version. + *(rdata_tail) = 0; + rdata_tail += 1; + // Write size. + *(rdata_tail) = loc64to8(s->loc.siz); + rdata_tail += 1; + // Write horizontal precision. + *(rdata_tail) = loc64to8(s->loc.hp); + rdata_tail += 1; + // Write vertical precision. + *(rdata_tail) = loc64to8(s->loc.vp); + rdata_tail += 1; + // Write latitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign * + (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1)); + rdata_tail += 4; + // Write longitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign * + (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2)); + rdata_tail += 4; + // Write altitude. + *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign * + (s->loc.alt)); + rdata_tail += 4; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1174; +tr2282: + { + s->number64_tmp = s->number64; + } + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 9000000000ULL) { + s->loc.hp = s->number64; + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + { + // Write version. + *(rdata_tail) = 0; + rdata_tail += 1; + // Write size. + *(rdata_tail) = loc64to8(s->loc.siz); + rdata_tail += 1; + // Write horizontal precision. + *(rdata_tail) = loc64to8(s->loc.hp); + rdata_tail += 1; + // Write vertical precision. + *(rdata_tail) = loc64to8(s->loc.vp); + rdata_tail += 1; + // Write latitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign * + (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1)); + rdata_tail += 4; + // Write longitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign * + (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2)); + rdata_tail += 4; + // Write altitude. + *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign * + (s->loc.alt)); + rdata_tail += 4; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1174; +st1174: + if ( ++p == pe ) + goto _test_eof1174; +case 1174: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto st712; + case 32: goto st712; + case 40: goto tr2236; + case 41: goto tr2237; + case 2058: goto tr2239; + case 2107: goto tr2240; + case 2314: goto tr2202; + case 2363: goto tr2202; + case 2570: goto tr2241; + case 2619: goto tr2242; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2238; + goto tr2093; +tr2242: + { + s->buffer_length = 0; + } + { + // Write version. + *(rdata_tail) = 0; + rdata_tail += 1; + // Write size. + *(rdata_tail) = loc64to8(s->loc.siz); + rdata_tail += 1; + // Write horizontal precision. + *(rdata_tail) = loc64to8(s->loc.hp); + rdata_tail += 1; + // Write vertical precision. + *(rdata_tail) = loc64to8(s->loc.vp); + rdata_tail += 1; + // Write latitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign * + (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1)); + rdata_tail += 4; + // Write longitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign * + (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2)); + rdata_tail += 4; + // Write altitude. + *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign * + (s->loc.alt)); + rdata_tail += 4; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1175; +tr2234: + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 9000000000ULL) { + s->loc.hp = s->number64; + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + { + // Write version. + *(rdata_tail) = 0; + rdata_tail += 1; + // Write size. + *(rdata_tail) = loc64to8(s->loc.siz); + rdata_tail += 1; + // Write horizontal precision. + *(rdata_tail) = loc64to8(s->loc.hp); + rdata_tail += 1; + // Write vertical precision. + *(rdata_tail) = loc64to8(s->loc.vp); + rdata_tail += 1; + // Write latitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign * + (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1)); + rdata_tail += 4; + // Write longitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign * + (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2)); + rdata_tail += 4; + // Write altitude. + *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign * + (s->loc.alt)); + rdata_tail += 4; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1175; +tr2283: + { + s->number64_tmp = s->number64; + } + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 9000000000ULL) { + s->loc.hp = s->number64; + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + { + // Write version. + *(rdata_tail) = 0; + rdata_tail += 1; + // Write size. + *(rdata_tail) = loc64to8(s->loc.siz); + rdata_tail += 1; + // Write horizontal precision. + *(rdata_tail) = loc64to8(s->loc.hp); + rdata_tail += 1; + // Write vertical precision. + *(rdata_tail) = loc64to8(s->loc.vp); + rdata_tail += 1; + // Write latitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign * + (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1)); + rdata_tail += 4; + // Write longitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign * + (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2)); + rdata_tail += 4; + // Write altitude. + *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign * + (s->loc.alt)); + rdata_tail += 4; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1175; +st1175: + if ( ++p == pe ) + goto _test_eof1175; +case 1175: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2273; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2272; + goto tr2093; +st719: + if ( ++p == pe ) + goto _test_eof719; +case 719: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr2274; + case 32: goto tr2274; + case 40: goto tr2275; + case 41: goto tr2276; + case 109: goto tr2278; + case 2058: goto tr2279; + case 2107: goto tr2280; + case 2314: goto tr2281; + case 2363: goto tr2281; + case 2570: goto tr2282; + case 2619: goto tr2283; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2277; + goto tr2093; +tr2277: + { + s->number64_tmp = s->number64; + } + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + { + s->decimal_counter++; + } + goto st720; +tr2284: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + { + s->decimal_counter++; + } + goto st720; +st720: + if ( ++p == pe ) + goto _test_eof720; +case 720: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr2224; + case 32: goto tr2224; + case 40: goto tr2225; + case 41: goto tr2226; + case 109: goto tr2229; + case 2058: goto tr2230; + case 2107: goto tr2231; + case 2314: goto tr2232; + case 2363: goto tr2232; + case 2570: goto tr2233; + case 2619: goto tr2234; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2284; + goto tr2093; +tr2229: + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 9000000000ULL) { + s->loc.hp = s->number64; + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + goto st721; +tr2278: + { + s->number64_tmp = s->number64; + } + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 9000000000ULL) { + s->loc.hp = s->number64; + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + goto st721; +st721: + if ( ++p == pe ) + goto _test_eof721; +case 721: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto st712; + case 32: goto st712; + case 40: goto tr2236; + case 41: goto tr2237; + case 2058: goto tr2239; + case 2107: goto tr2240; + case 2314: goto tr2202; + case 2363: goto tr2202; + case 2570: goto tr2241; + case 2619: goto tr2242; + } + goto tr2093; +tr2221: + { + s->buffer_length = 0; + } + goto st722; +tr2285: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st722; +tr2212: + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 9000000000ULL) { + s->loc.siz = s->number64; + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st722; +tr2293: + { + s->number64_tmp = s->number64; + } + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 9000000000ULL) { + s->loc.siz = s->number64; + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st722; +st722: + if ( ++p == pe ) + goto _test_eof722; +case 722: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2286; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2285; + goto tr2093; +tr2222: + { + s->line_counter++; + } + { + // Write version. + *(rdata_tail) = 0; + rdata_tail += 1; + // Write size. + *(rdata_tail) = loc64to8(s->loc.siz); + rdata_tail += 1; + // Write horizontal precision. + *(rdata_tail) = loc64to8(s->loc.hp); + rdata_tail += 1; + // Write vertical precision. + *(rdata_tail) = loc64to8(s->loc.vp); + rdata_tail += 1; + // Write latitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign * + (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1)); + rdata_tail += 4; + // Write longitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign * + (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2)); + rdata_tail += 4; + // Write altitude. + *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign * + (s->loc.alt)); + rdata_tail += 4; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1176; +tr2214: + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 9000000000ULL) { + s->loc.siz = s->number64; + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + { + // Write version. + *(rdata_tail) = 0; + rdata_tail += 1; + // Write size. + *(rdata_tail) = loc64to8(s->loc.siz); + rdata_tail += 1; + // Write horizontal precision. + *(rdata_tail) = loc64to8(s->loc.hp); + rdata_tail += 1; + // Write vertical precision. + *(rdata_tail) = loc64to8(s->loc.vp); + rdata_tail += 1; + // Write latitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign * + (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1)); + rdata_tail += 4; + // Write longitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign * + (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2)); + rdata_tail += 4; + // Write altitude. + *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign * + (s->loc.alt)); + rdata_tail += 4; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1176; +tr2295: + { + s->number64_tmp = s->number64; + } + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 9000000000ULL) { + s->loc.siz = s->number64; + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + { + // Write version. + *(rdata_tail) = 0; + rdata_tail += 1; + // Write size. + *(rdata_tail) = loc64to8(s->loc.siz); + rdata_tail += 1; + // Write horizontal precision. + *(rdata_tail) = loc64to8(s->loc.hp); + rdata_tail += 1; + // Write vertical precision. + *(rdata_tail) = loc64to8(s->loc.vp); + rdata_tail += 1; + // Write latitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign * + (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1)); + rdata_tail += 4; + // Write longitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign * + (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2)); + rdata_tail += 4; + // Write altitude. + *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign * + (s->loc.alt)); + rdata_tail += 4; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1176; +st1176: + if ( ++p == pe ) + goto _test_eof1176; +case 1176: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto st710; + case 32: goto st710; + case 40: goto tr2217; + case 41: goto tr2218; + case 2058: goto tr2220; + case 2107: goto tr2221; + case 2314: goto tr2202; + case 2363: goto tr2202; + case 2570: goto tr2222; + case 2619: goto tr2223; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2219; + goto tr2093; +tr2223: + { + s->buffer_length = 0; + } + { + // Write version. + *(rdata_tail) = 0; + rdata_tail += 1; + // Write size. + *(rdata_tail) = loc64to8(s->loc.siz); + rdata_tail += 1; + // Write horizontal precision. + *(rdata_tail) = loc64to8(s->loc.hp); + rdata_tail += 1; + // Write vertical precision. + *(rdata_tail) = loc64to8(s->loc.vp); + rdata_tail += 1; + // Write latitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign * + (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1)); + rdata_tail += 4; + // Write longitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign * + (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2)); + rdata_tail += 4; + // Write altitude. + *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign * + (s->loc.alt)); + rdata_tail += 4; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1177; +tr2215: + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 9000000000ULL) { + s->loc.siz = s->number64; + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + { + // Write version. + *(rdata_tail) = 0; + rdata_tail += 1; + // Write size. + *(rdata_tail) = loc64to8(s->loc.siz); + rdata_tail += 1; + // Write horizontal precision. + *(rdata_tail) = loc64to8(s->loc.hp); + rdata_tail += 1; + // Write vertical precision. + *(rdata_tail) = loc64to8(s->loc.vp); + rdata_tail += 1; + // Write latitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign * + (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1)); + rdata_tail += 4; + // Write longitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign * + (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2)); + rdata_tail += 4; + // Write altitude. + *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign * + (s->loc.alt)); + rdata_tail += 4; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1177; +tr2296: + { + s->number64_tmp = s->number64; + } + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 9000000000ULL) { + s->loc.siz = s->number64; + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + { + // Write version. + *(rdata_tail) = 0; + rdata_tail += 1; + // Write size. + *(rdata_tail) = loc64to8(s->loc.siz); + rdata_tail += 1; + // Write horizontal precision. + *(rdata_tail) = loc64to8(s->loc.hp); + rdata_tail += 1; + // Write vertical precision. + *(rdata_tail) = loc64to8(s->loc.vp); + rdata_tail += 1; + // Write latitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign * + (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1)); + rdata_tail += 4; + // Write longitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign * + (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2)); + rdata_tail += 4; + // Write altitude. + *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign * + (s->loc.alt)); + rdata_tail += 4; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1177; +st1177: + if ( ++p == pe ) + goto _test_eof1177; +case 1177: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2286; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2285; + goto tr2093; +st723: + if ( ++p == pe ) + goto _test_eof723; +case 723: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr2287; + case 32: goto tr2287; + case 40: goto tr2288; + case 41: goto tr2289; + case 109: goto tr2291; + case 2058: goto tr2292; + case 2107: goto tr2293; + case 2314: goto tr2294; + case 2363: goto tr2294; + case 2570: goto tr2295; + case 2619: goto tr2296; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2290; + goto tr2093; +tr2290: + { + s->number64_tmp = s->number64; + } + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + { + s->decimal_counter++; + } + goto st724; +tr2297: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + { + s->decimal_counter++; + } + goto st724; +st724: + if ( ++p == pe ) + goto _test_eof724; +case 724: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr2205; + case 32: goto tr2205; + case 40: goto tr2206; + case 41: goto tr2207; + case 109: goto tr2210; + case 2058: goto tr2211; + case 2107: goto tr2212; + case 2314: goto tr2213; + case 2363: goto tr2213; + case 2570: goto tr2214; + case 2619: goto tr2215; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2297; + goto tr2093; +tr2210: + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 9000000000ULL) { + s->loc.siz = s->number64; + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + goto st725; +tr2291: + { + s->number64_tmp = s->number64; + } + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 9000000000ULL) { + s->loc.siz = s->number64; + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + goto st725; +st725: + if ( ++p == pe ) + goto _test_eof725; +case 725: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto st710; + case 32: goto st710; + case 40: goto tr2217; + case 41: goto tr2218; + case 2058: goto tr2220; + case 2107: goto tr2221; + case 2314: goto tr2202; + case 2363: goto tr2202; + case 2570: goto tr2222; + case 2619: goto tr2223; + } + goto tr2093; +tr2201: + { + s->buffer_length = 0; + } + goto st726; +tr2298: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st726; +tr2192: + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if ((s->loc.alt_sign == 1 && s->number64 <= 4284967295) || + (s->loc.alt_sign == -1 && s->number64 <= 10000000)) + { + s->loc.alt = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st726; +tr2306: + { + s->number64_tmp = s->number64; + } + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if ((s->loc.alt_sign == 1 && s->number64 <= 4284967295) || + (s->loc.alt_sign == -1 && s->number64 <= 10000000)) + { + s->loc.alt = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st726; +st726: + if ( ++p == pe ) + goto _test_eof726; +case 726: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2299; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2298; + goto tr2093; +tr2194: + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if ((s->loc.alt_sign == 1 && s->number64 <= 4284967295) || + (s->loc.alt_sign == -1 && s->number64 <= 10000000)) + { + s->loc.alt = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + { + // Write version. + *(rdata_tail) = 0; + rdata_tail += 1; + // Write size. + *(rdata_tail) = loc64to8(s->loc.siz); + rdata_tail += 1; + // Write horizontal precision. + *(rdata_tail) = loc64to8(s->loc.hp); + rdata_tail += 1; + // Write vertical precision. + *(rdata_tail) = loc64to8(s->loc.vp); + rdata_tail += 1; + // Write latitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign * + (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1)); + rdata_tail += 4; + // Write longitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign * + (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2)); + rdata_tail += 4; + // Write altitude. + *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign * + (s->loc.alt)); + rdata_tail += 4; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1178; +tr2203: + { + s->line_counter++; + } + { + // Write version. + *(rdata_tail) = 0; + rdata_tail += 1; + // Write size. + *(rdata_tail) = loc64to8(s->loc.siz); + rdata_tail += 1; + // Write horizontal precision. + *(rdata_tail) = loc64to8(s->loc.hp); + rdata_tail += 1; + // Write vertical precision. + *(rdata_tail) = loc64to8(s->loc.vp); + rdata_tail += 1; + // Write latitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign * + (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1)); + rdata_tail += 4; + // Write longitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign * + (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2)); + rdata_tail += 4; + // Write altitude. + *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign * + (s->loc.alt)); + rdata_tail += 4; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1178; +tr2308: + { + s->number64_tmp = s->number64; + } + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if ((s->loc.alt_sign == 1 && s->number64 <= 4284967295) || + (s->loc.alt_sign == -1 && s->number64 <= 10000000)) + { + s->loc.alt = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + { + // Write version. + *(rdata_tail) = 0; + rdata_tail += 1; + // Write size. + *(rdata_tail) = loc64to8(s->loc.siz); + rdata_tail += 1; + // Write horizontal precision. + *(rdata_tail) = loc64to8(s->loc.hp); + rdata_tail += 1; + // Write vertical precision. + *(rdata_tail) = loc64to8(s->loc.vp); + rdata_tail += 1; + // Write latitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign * + (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1)); + rdata_tail += 4; + // Write longitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign * + (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2)); + rdata_tail += 4; + // Write altitude. + *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign * + (s->loc.alt)); + rdata_tail += 4; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1178; +st1178: + if ( ++p == pe ) + goto _test_eof1178; +case 1178: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto st708; + case 32: goto st708; + case 40: goto tr2197; + case 41: goto tr2198; + case 2058: goto tr2200; + case 2107: goto tr2201; + case 2314: goto tr2202; + case 2363: goto tr2202; + case 2570: goto tr2203; + case 2619: goto tr2204; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2199; + goto tr2093; +tr2195: + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if ((s->loc.alt_sign == 1 && s->number64 <= 4284967295) || + (s->loc.alt_sign == -1 && s->number64 <= 10000000)) + { + s->loc.alt = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + { + // Write version. + *(rdata_tail) = 0; + rdata_tail += 1; + // Write size. + *(rdata_tail) = loc64to8(s->loc.siz); + rdata_tail += 1; + // Write horizontal precision. + *(rdata_tail) = loc64to8(s->loc.hp); + rdata_tail += 1; + // Write vertical precision. + *(rdata_tail) = loc64to8(s->loc.vp); + rdata_tail += 1; + // Write latitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign * + (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1)); + rdata_tail += 4; + // Write longitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign * + (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2)); + rdata_tail += 4; + // Write altitude. + *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign * + (s->loc.alt)); + rdata_tail += 4; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1179; +tr2204: + { + s->buffer_length = 0; + } + { + // Write version. + *(rdata_tail) = 0; + rdata_tail += 1; + // Write size. + *(rdata_tail) = loc64to8(s->loc.siz); + rdata_tail += 1; + // Write horizontal precision. + *(rdata_tail) = loc64to8(s->loc.hp); + rdata_tail += 1; + // Write vertical precision. + *(rdata_tail) = loc64to8(s->loc.vp); + rdata_tail += 1; + // Write latitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign * + (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1)); + rdata_tail += 4; + // Write longitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign * + (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2)); + rdata_tail += 4; + // Write altitude. + *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign * + (s->loc.alt)); + rdata_tail += 4; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1179; +tr2309: + { + s->number64_tmp = s->number64; + } + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if ((s->loc.alt_sign == 1 && s->number64 <= 4284967295) || + (s->loc.alt_sign == -1 && s->number64 <= 10000000)) + { + s->loc.alt = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + { + // Write version. + *(rdata_tail) = 0; + rdata_tail += 1; + // Write size. + *(rdata_tail) = loc64to8(s->loc.siz); + rdata_tail += 1; + // Write horizontal precision. + *(rdata_tail) = loc64to8(s->loc.hp); + rdata_tail += 1; + // Write vertical precision. + *(rdata_tail) = loc64to8(s->loc.vp); + rdata_tail += 1; + // Write latitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign * + (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1)); + rdata_tail += 4; + // Write longitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign * + (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2)); + rdata_tail += 4; + // Write altitude. + *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign * + (s->loc.alt)); + rdata_tail += 4; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1179; +st1179: + if ( ++p == pe ) + goto _test_eof1179; +case 1179: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2299; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2298; + goto tr2093; +st727: + if ( ++p == pe ) + goto _test_eof727; +case 727: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr2300; + case 32: goto tr2300; + case 40: goto tr2301; + case 41: goto tr2302; + case 109: goto tr2304; + case 2058: goto tr2305; + case 2107: goto tr2306; + case 2314: goto tr2307; + case 2363: goto tr2307; + case 2570: goto tr2308; + case 2619: goto tr2309; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2303; + goto tr2093; +tr2303: + { + s->number64_tmp = s->number64; + } + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + { + s->decimal_counter++; + } + goto st728; +tr2310: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + { + s->decimal_counter++; + } + goto st728; +st728: + if ( ++p == pe ) + goto _test_eof728; +case 728: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr2185; + case 32: goto tr2185; + case 40: goto tr2186; + case 41: goto tr2187; + case 109: goto tr2190; + case 2058: goto tr2191; + case 2107: goto tr2192; + case 2314: goto tr2193; + case 2363: goto tr2193; + case 2570: goto tr2194; + case 2619: goto tr2195; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2310; + goto tr2093; +tr2190: + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if ((s->loc.alt_sign == 1 && s->number64 <= 4284967295) || + (s->loc.alt_sign == -1 && s->number64 <= 10000000)) + { + s->loc.alt = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + goto st729; +tr2304: + { + s->number64_tmp = s->number64; + } + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if ((s->loc.alt_sign == 1 && s->number64 <= 4284967295) || + (s->loc.alt_sign == -1 && s->number64 <= 10000000)) + { + s->loc.alt = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + goto st729; +st729: + if ( ++p == pe ) + goto _test_eof729; +case 729: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto st708; + case 32: goto st708; + case 40: goto tr2197; + case 41: goto tr2198; + case 2058: goto tr2200; + case 2107: goto tr2201; + case 2314: goto tr2202; + case 2363: goto tr2202; + case 2570: goto tr2203; + case 2619: goto tr2204; + } + goto tr2093; +tr2181: + { + s->buffer_length = 0; + } + goto st730; +tr2311: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st730; +tr2317: + { + s->loc.long_sign = -1; + } + { + s->buffer_length = 0; + } + goto st730; +st730: + if ( ++p == pe ) + goto _test_eof730; +case 730: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2312; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2311; + goto tr2093; +st731: + if ( ++p == pe ) + goto _test_eof731; +case 731: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr2313; + case 32: goto tr2313; + case 40: goto tr2314; + case 41: goto tr2315; + case 1034: goto tr2316; + case 1083: goto tr2317; + } + goto tr2093; +tr2176: + { + s->buffer_length = 0; + } + goto st732; +tr2318: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st732; +tr2171: + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 59999) { + s->loc.s2 = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st732; +tr2325: + { + s->number64_tmp = s->number64; + } + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 59999) { + s->loc.s2 = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st732; +st732: + if ( ++p == pe ) + goto _test_eof732; +case 732: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2319; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2318; + goto tr2093; +st733: + if ( ++p == pe ) + goto _test_eof733; +case 733: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr2320; + case 32: goto tr2320; + case 40: goto tr2321; + case 41: goto tr2322; + case 1034: goto tr2324; + case 1083: goto tr2325; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2323; + goto tr2093; +tr2323: + { + s->number64_tmp = s->number64; + } + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + { + s->decimal_counter++; + } + goto st734; +tr2326: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + { + s->decimal_counter++; + } + goto st734; +st734: + if ( ++p == pe ) + goto _test_eof734; +case 734: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr2165; + case 32: goto tr2165; + case 40: goto tr2166; + case 41: goto tr2167; + case 1034: goto tr2170; + case 1083: goto tr2171; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2326; + goto tr2093; +tr2164: + { + s->buffer_length = 0; + } + goto st735; +tr2327: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st735; +tr2158: + { + if (s->number64 <= 59) { + s->loc.m2 = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st735; +st735: + if ( ++p == pe ) + goto _test_eof735; +case 735: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2328; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2327; + goto tr2093; +tr2152: + { + s->buffer_length = 0; + } + goto st736; +tr2329: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st736; +tr2144: + { + if (s->number64 <= 180) { + s->loc.d2 = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st736; +st736: + if ( ++p == pe ) + goto _test_eof736; +case 736: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2330; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2329; + goto tr2093; +tr2137: + { + s->buffer_length = 0; + } + goto st737; +tr2331: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st737; +tr2337: + { + s->loc.lat_sign = -1; + } + { + s->buffer_length = 0; + } + goto st737; +st737: + if ( ++p == pe ) + goto _test_eof737; +case 737: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2332; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2331; + goto tr2093; +st738: + if ( ++p == pe ) + goto _test_eof738; +case 738: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr2333; + case 32: goto tr2333; + case 40: goto tr2334; + case 41: goto tr2335; + case 1034: goto tr2336; + case 1083: goto tr2337; + } + goto tr2093; +tr2132: + { + s->buffer_length = 0; + } + goto st739; +tr2338: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st739; +tr2127: + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 59999) { + s->loc.s1 = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st739; +tr2345: + { + s->number64_tmp = s->number64; + } + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->number64 <= 59999) { + s->loc.s1 = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st739; +st739: + if ( ++p == pe ) + goto _test_eof739; +case 739: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2339; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2338; + goto tr2093; +st740: + if ( ++p == pe ) + goto _test_eof740; +case 740: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr2340; + case 32: goto tr2340; + case 40: goto tr2341; + case 41: goto tr2342; + case 1034: goto tr2344; + case 1083: goto tr2345; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2343; + goto tr2093; +tr2343: + { + s->number64_tmp = s->number64; + } + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + { + s->decimal_counter++; + } + goto st741; +tr2346: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + { + s->decimal_counter++; + } + goto st741; +st741: + if ( ++p == pe ) + goto _test_eof741; +case 741: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr2121; + case 32: goto tr2121; + case 40: goto tr2122; + case 41: goto tr2123; + case 1034: goto tr2126; + case 1083: goto tr2127; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2346; + goto tr2093; +tr2120: + { + s->buffer_length = 0; + } + goto st742; +tr2347: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st742; +tr2114: + { + if (s->number64 <= 59) { + s->loc.m1 = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st742; +st742: + if ( ++p == pe ) + goto _test_eof742; +case 742: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2348; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2347; + goto tr2093; +tr2108: + { + s->buffer_length = 0; + } + goto st743; +tr2349: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st743; +tr2100: + { + if (s->number64 <= 90) { + s->loc.d1 = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st743; +st743: + if ( ++p == pe ) + goto _test_eof743; +case 743: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2350; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2349; + goto tr2093; +st744: + if ( ++p == pe ) + goto _test_eof744; +case 744: + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr2351; + goto tr1885; +tr2351: + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st745; +tr2355: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st745; +st745: + if ( ++p == pe ) + goto _test_eof745; +case 745: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr2352; + case 32: goto tr2352; + case 40: goto tr2353; + case 41: goto tr2354; + case 1034: goto tr2356; + case 1083: goto tr2357; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2355; + goto tr1885; +tr2359: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st746; +tr2360: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st746; +tr2362: + { + s->line_counter++; + } + goto st746; +tr2394: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st746; +tr2352: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + goto st746; +tr2353: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st746; +tr2354: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st746; +tr2356: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st746; +st746: + if ( ++p == pe ) + goto _test_eof746; +case 746: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st746; + case 32: goto st746; + case 40: goto tr2359; + case 41: goto tr2360; + case 1034: goto tr2362; + case 1083: goto tr2363; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2361; + goto tr1885; +tr2361: + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st747; +tr2367: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st747; +st747: + if ( ++p == pe ) + goto _test_eof747; +case 747: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr2364; + case 32: goto tr2364; + case 40: goto tr2365; + case 41: goto tr2366; + case 1034: goto tr2368; + case 1083: goto tr2369; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2367; + goto tr1885; +tr2371: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st748; +tr2372: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st748; +tr2374: + { + s->line_counter++; + } + goto st748; +tr2392: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st748; +tr2364: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + goto st748; +tr2365: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st748; +tr2366: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st748; +tr2368: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st748; +st748: + if ( ++p == pe ) + goto _test_eof748; +case 748: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st748; + case 32: goto st748; + case 40: goto tr2371; + case 41: goto tr2372; + case 1034: goto tr2374; + case 1083: goto tr2375; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2373; + goto tr1885; +tr2373: + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st749; +tr2379: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st749; +st749: + if ( ++p == pe ) + goto _test_eof749; +case 749: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr2376; + case 32: goto tr2376; + case 40: goto tr2377; + case 41: goto tr2378; + case 1034: goto tr2380; + case 1083: goto tr2381; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2379; + goto tr1885; +tr2383: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st750; +tr2384: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st750; +tr2386: + { + s->line_counter++; + } + goto st750; +tr2390: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st750; +tr2376: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + goto st750; +tr2377: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st750; +tr2378: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st750; +tr2380: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st750; +st750: + if ( ++p == pe ) + goto _test_eof750; +case 750: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st750; + case 32: goto st750; + case 40: goto tr2383; + case 41: goto tr2384; + case 42: goto tr2385; + case 92: goto tr2385; + case 95: goto tr2385; + case 1034: goto tr2386; + case 1083: goto tr2387; + } + if ( _widec < 64 ) { + if ( 45 <= _widec && _widec <= 57 ) + goto tr2385; + } else if ( _widec > 90 ) { + if ( 97 <= _widec && _widec <= 122 ) + goto tr2385; + } else + goto tr2385; + goto tr71; +tr2385: + { + s->dname = rdata_tail; + } + { p--; {stack[top++] = 751;goto st270;} } + goto st751; +st751: + if ( ++p == pe ) + goto _test_eof751; +case 751: + switch( (*p) ) { + case 32: goto tr2388; + case 59: goto tr2388; + } + if ( (*p) > 10 ) { + if ( 40 <= (*p) && (*p) <= 41 ) + goto tr2388; + } else if ( (*p) >= 9 ) + goto tr2388; + goto tr71; +tr2388: + { + rdata_tail += s->dname_tmp_length; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1180; +st1180: + if ( ++p == pe ) + goto _test_eof1180; +case 1180: + goto st0; +tr2387: + { + s->buffer_length = 0; + } + goto st752; +tr2389: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st752; +tr2381: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st752; +st752: + if ( ++p == pe ) + goto _test_eof752; +case 752: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2390; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2389; + goto tr71; +tr2375: + { + s->buffer_length = 0; + } + goto st753; +tr2391: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st753; +tr2369: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st753; +st753: + if ( ++p == pe ) + goto _test_eof753; +case 753: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2392; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2391; + goto tr71; +tr2363: + { + s->buffer_length = 0; + } + goto st754; +tr2393: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st754; +tr2357: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st754; +st754: + if ( ++p == pe ) + goto _test_eof754; +case 754: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2394; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2393; + goto tr71; +st755: + if ( ++p == pe ) + goto _test_eof755; +case 755: + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr2395; + goto tr1885; +tr2395: + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st756; +tr2399: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st756; +st756: + if ( ++p == pe ) + goto _test_eof756; +case 756: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr2396; + case 32: goto tr2396; + case 40: goto tr2397; + case 41: goto tr2398; + case 1034: goto tr2400; + case 1083: goto tr2401; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2399; + goto tr1885; +tr2403: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st757; +tr2404: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st757; +tr2406: + { + s->line_counter++; + } + goto st757; +tr2463: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st757; +tr2396: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + goto st757; +tr2397: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st757; +tr2398: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st757; +tr2400: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st757; +st757: + if ( ++p == pe ) + goto _test_eof757; +case 757: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st757; + case 32: goto st757; + case 40: goto tr2403; + case 41: goto tr2404; + case 1034: goto tr2406; + case 1083: goto tr2407; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2405; + goto tr1885; +tr2405: + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st758; +tr2411: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st758; +st758: + if ( ++p == pe ) + goto _test_eof758; +case 758: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr2408; + case 32: goto tr2408; + case 40: goto tr2409; + case 41: goto tr2410; + case 1034: goto tr2412; + case 1083: goto tr2413; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2411; + goto tr1885; +tr2416: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st759; +tr2417: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st759; +tr2418: + { + s->line_counter++; + } + goto st759; +tr2461: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st759; +tr2408: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + goto st759; +tr2409: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st759; +tr2410: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st759; +tr2412: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st759; +st759: + if ( ++p == pe ) + goto _test_eof759; +case 759: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st759; + case 32: goto st759; + case 40: goto tr2416; + case 41: goto tr2417; + case 1034: goto tr2418; + case 1083: goto tr2419; + } + if ( _widec < 11 ) { + if ( _widec <= 8 ) + goto tr2414; + } else if ( _widec > 58 ) { + if ( 60 <= _widec ) + goto tr2414; + } else + goto tr2414; + goto tr71; +tr2414: + { + if (rdata_tail <= rdata_stop) { + s->item_length_location = rdata_tail++; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + { p--; {stack[top++] = 760;goto st279;} } + goto st760; +st760: + if ( ++p == pe ) + goto _test_eof760; +case 760: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr2420; + case 32: goto tr2420; + case 40: goto tr2421; + case 41: goto tr2422; + case 1034: goto tr2423; + case 1083: goto tr2424; + } + goto tr71; +tr2427: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st761; +tr2428: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st761; +tr2429: + { + s->line_counter++; + } + goto st761; +tr2459: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st761; +tr2420: + { + s->item_length = rdata_tail - s->item_length_location - 1; + + if (s->item_length <= MAX_ITEM_LENGTH) { + *(s->item_length_location) = (uint8_t)(s->item_length); + } else { + WARN(ZS_ITEM_OVERFLOW); + p--; {goto st268;} + } + } + goto st761; +tr2421: + { + s->item_length = rdata_tail - s->item_length_location - 1; + + if (s->item_length <= MAX_ITEM_LENGTH) { + *(s->item_length_location) = (uint8_t)(s->item_length); + } else { + WARN(ZS_ITEM_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st761; +tr2422: + { + s->item_length = rdata_tail - s->item_length_location - 1; + + if (s->item_length <= MAX_ITEM_LENGTH) { + *(s->item_length_location) = (uint8_t)(s->item_length); + } else { + WARN(ZS_ITEM_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st761; +tr2423: + { + s->item_length = rdata_tail - s->item_length_location - 1; + + if (s->item_length <= MAX_ITEM_LENGTH) { + *(s->item_length_location) = (uint8_t)(s->item_length); + } else { + WARN(ZS_ITEM_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st761; +st761: + if ( ++p == pe ) + goto _test_eof761; +case 761: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st761; + case 32: goto st761; + case 40: goto tr2427; + case 41: goto tr2428; + case 1034: goto tr2429; + case 1083: goto tr2430; + } + if ( _widec < 11 ) { + if ( _widec <= 8 ) + goto tr2425; + } else if ( _widec > 58 ) { + if ( 60 <= _widec ) + goto tr2425; + } else + goto tr2425; + goto tr71; +tr2425: + { + if (rdata_tail <= rdata_stop) { + s->item_length_location = rdata_tail++; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + { p--; {stack[top++] = 762;goto st279;} } + goto st762; +st762: + if ( ++p == pe ) + goto _test_eof762; +case 762: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr2431; + case 32: goto tr2431; + case 40: goto tr2432; + case 41: goto tr2433; + case 1034: goto tr2434; + case 1083: goto tr2435; + } + goto tr71; +tr2438: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st763; +tr2439: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st763; +tr2440: + { + s->line_counter++; + } + goto st763; +tr2457: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st763; +tr2431: + { + s->item_length = rdata_tail - s->item_length_location - 1; + + if (s->item_length <= MAX_ITEM_LENGTH) { + *(s->item_length_location) = (uint8_t)(s->item_length); + } else { + WARN(ZS_ITEM_OVERFLOW); + p--; {goto st268;} + } + } + goto st763; +tr2432: + { + s->item_length = rdata_tail - s->item_length_location - 1; + + if (s->item_length <= MAX_ITEM_LENGTH) { + *(s->item_length_location) = (uint8_t)(s->item_length); + } else { + WARN(ZS_ITEM_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st763; +tr2433: + { + s->item_length = rdata_tail - s->item_length_location - 1; + + if (s->item_length <= MAX_ITEM_LENGTH) { + *(s->item_length_location) = (uint8_t)(s->item_length); + } else { + WARN(ZS_ITEM_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st763; +tr2434: + { + s->item_length = rdata_tail - s->item_length_location - 1; + + if (s->item_length <= MAX_ITEM_LENGTH) { + *(s->item_length_location) = (uint8_t)(s->item_length); + } else { + WARN(ZS_ITEM_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st763; +st763: + if ( ++p == pe ) + goto _test_eof763; +case 763: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st763; + case 32: goto st763; + case 40: goto tr2438; + case 41: goto tr2439; + case 1034: goto tr2440; + case 1083: goto tr2441; + } + if ( _widec < 11 ) { + if ( _widec <= 8 ) + goto tr2436; + } else if ( _widec > 58 ) { + if ( 60 <= _widec ) + goto tr2436; + } else + goto tr2436; + goto tr71; +tr2436: + { + if (rdata_tail <= rdata_stop) { + s->item_length_location = rdata_tail++; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + { p--; {stack[top++] = 764;goto st279;} } + goto st764; +st764: + if ( ++p == pe ) + goto _test_eof764; +case 764: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr2442; + case 32: goto tr2442; + case 40: goto tr2443; + case 41: goto tr2444; + case 1034: goto tr2445; + case 1083: goto tr2446; + } + goto tr71; +tr2448: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st765; +tr2449: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st765; +tr2451: + { + s->line_counter++; + } + goto st765; +tr2455: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st765; +tr2442: + { + s->item_length = rdata_tail - s->item_length_location - 1; + + if (s->item_length <= MAX_ITEM_LENGTH) { + *(s->item_length_location) = (uint8_t)(s->item_length); + } else { + WARN(ZS_ITEM_OVERFLOW); + p--; {goto st268;} + } + } + goto st765; +tr2443: + { + s->item_length = rdata_tail - s->item_length_location - 1; + + if (s->item_length <= MAX_ITEM_LENGTH) { + *(s->item_length_location) = (uint8_t)(s->item_length); + } else { + WARN(ZS_ITEM_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st765; +tr2444: + { + s->item_length = rdata_tail - s->item_length_location - 1; + + if (s->item_length <= MAX_ITEM_LENGTH) { + *(s->item_length_location) = (uint8_t)(s->item_length); + } else { + WARN(ZS_ITEM_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st765; +tr2445: + { + s->item_length = rdata_tail - s->item_length_location - 1; + + if (s->item_length <= MAX_ITEM_LENGTH) { + *(s->item_length_location) = (uint8_t)(s->item_length); + } else { + WARN(ZS_ITEM_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st765; +st765: + if ( ++p == pe ) + goto _test_eof765; +case 765: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st765; + case 32: goto st765; + case 40: goto tr2448; + case 41: goto tr2449; + case 42: goto tr2450; + case 92: goto tr2450; + case 95: goto tr2450; + case 1034: goto tr2451; + case 1083: goto tr2452; + } + if ( _widec < 64 ) { + if ( 45 <= _widec && _widec <= 57 ) + goto tr2450; + } else if ( _widec > 90 ) { + if ( 97 <= _widec && _widec <= 122 ) + goto tr2450; + } else + goto tr2450; + goto tr71; +tr2450: + { + s->dname = rdata_tail; + } + { p--; {stack[top++] = 766;goto st270;} } + goto st766; +st766: + if ( ++p == pe ) + goto _test_eof766; +case 766: + switch( (*p) ) { + case 32: goto tr2453; + case 59: goto tr2453; + } + if ( (*p) > 10 ) { + if ( 40 <= (*p) && (*p) <= 41 ) + goto tr2453; + } else if ( (*p) >= 9 ) + goto tr2453; + goto tr71; +tr2453: + { + rdata_tail += s->dname_tmp_length; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1181; +st1181: + if ( ++p == pe ) + goto _test_eof1181; +case 1181: + goto st0; +tr2452: + { + s->buffer_length = 0; + } + goto st767; +tr2454: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st767; +tr2446: + { + s->item_length = rdata_tail - s->item_length_location - 1; + + if (s->item_length <= MAX_ITEM_LENGTH) { + *(s->item_length_location) = (uint8_t)(s->item_length); + } else { + WARN(ZS_ITEM_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st767; +st767: + if ( ++p == pe ) + goto _test_eof767; +case 767: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2455; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2454; + goto tr71; +tr2441: + { + s->buffer_length = 0; + } + goto st768; +tr2456: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st768; +tr2435: + { + s->item_length = rdata_tail - s->item_length_location - 1; + + if (s->item_length <= MAX_ITEM_LENGTH) { + *(s->item_length_location) = (uint8_t)(s->item_length); + } else { + WARN(ZS_ITEM_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st768; +st768: + if ( ++p == pe ) + goto _test_eof768; +case 768: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2457; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2456; + goto tr71; +tr2430: + { + s->buffer_length = 0; + } + goto st769; +tr2458: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st769; +tr2424: + { + s->item_length = rdata_tail - s->item_length_location - 1; + + if (s->item_length <= MAX_ITEM_LENGTH) { + *(s->item_length_location) = (uint8_t)(s->item_length); + } else { + WARN(ZS_ITEM_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st769; +st769: + if ( ++p == pe ) + goto _test_eof769; +case 769: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2459; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2458; + goto tr71; +tr2419: + { + s->buffer_length = 0; + } + goto st770; +tr2460: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st770; +tr2413: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st770; +st770: + if ( ++p == pe ) + goto _test_eof770; +case 770: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2461; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2460; + goto tr71; +tr2407: + { + s->buffer_length = 0; + } + goto st771; +tr2462: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st771; +tr2401: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st771; +st771: + if ( ++p == pe ) + goto _test_eof771; +case 771: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2463; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2462; + goto tr71; +st772: + if ( ++p == pe ) + goto _test_eof772; +case 772: + if ( (*p) < 65 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr2464; + } else if ( (*p) > 90 ) { + if ( 97 <= (*p) && (*p) <= 122 ) + goto tr2464; + } else + goto tr2464; + goto tr71; +tr2464: + { p--; {stack[top++] = 773;goto st591;} } + goto st773; +st773: + if ( ++p == pe ) + goto _test_eof773; +case 773: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st774; + case 32: goto st774; + case 40: goto tr2466; + case 41: goto tr2467; + case 1034: goto tr2468; + case 1083: goto tr2469; + } + goto tr71; +tr2466: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st774; +tr2467: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st774; +tr2468: + { + s->line_counter++; + } + goto st774; +tr2495: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st774; +st774: + if ( ++p == pe ) + goto _test_eof774; +case 774: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st774; + case 32: goto st774; + case 40: goto tr2466; + case 41: goto tr2467; + case 1034: goto tr2468; + case 1083: goto tr2469; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2470; + goto tr1885; +tr2470: + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st775; +tr2474: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st775; +st775: + if ( ++p == pe ) + goto _test_eof775; +case 775: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr2471; + case 32: goto tr2471; + case 40: goto tr2472; + case 41: goto tr2473; + case 1034: goto tr2475; + case 1083: goto tr2476; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2474; + goto tr1885; +tr2478: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st776; +tr2479: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st776; +tr2481: + { + s->line_counter++; + } + goto st776; +tr2493: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st776; +tr2471: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + goto st776; +tr2472: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st776; +tr2473: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st776; +tr2475: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st776; +st776: + if ( ++p == pe ) + goto _test_eof776; +case 776: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st776; + case 32: goto st776; + case 40: goto tr2478; + case 41: goto tr2479; + case 1034: goto tr2481; + case 1083: goto tr2482; + } + if ( _widec < 65 ) { + if ( 48 <= _widec && _widec <= 57 ) + goto tr2480; + } else if ( _widec > 90 ) { + if ( 97 <= _widec && _widec <= 122 ) + goto tr2480; + } else + goto tr2480; + goto tr71; +tr2480: + { p--; {stack[top++] = 777;goto st487;} } + goto st777; +st777: + if ( ++p == pe ) + goto _test_eof777; +case 777: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st778; + case 32: goto st778; + case 40: goto tr2484; + case 41: goto tr2485; + case 1034: goto tr2486; + case 1083: goto tr2487; + } + goto tr71; +tr2484: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st778; +tr2485: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st778; +tr2486: + { + s->line_counter++; + } + goto st778; +tr2491: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st778; +st778: + if ( ++p == pe ) + goto _test_eof778; +case 778: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st778; + case 32: goto st778; + case 40: goto tr2484; + case 41: goto tr2485; + case 43: goto tr2488; + case 1034: goto tr2486; + case 1083: goto tr2487; + } + if ( _widec < 65 ) { + if ( 47 <= _widec && _widec <= 57 ) + goto tr2488; + } else if ( _widec > 90 ) { + if ( 97 <= _widec && _widec <= 122 ) + goto tr2488; + } else + goto tr2488; + goto tr71; +tr2488: + { p--; {stack[top++] = 779;goto st329;} } + goto st779; +st779: + if ( ++p == pe ) + goto _test_eof779; +case 779: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1152 + ((*p) - -128)); + if ( + !s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1152 + ((*p) - -128)); + if ( + !s->multiline ) _widec += 256; + } + switch( _widec ) { + case 1546: goto tr2489; + case 1595: goto tr2489; + } + goto tr71; +tr2489: + { + p--; {cs = stack[--top];goto _again;} + } + goto st1182; +st1182: + if ( ++p == pe ) + goto _test_eof1182; +case 1182: + goto st0; +tr2487: + { + s->buffer_length = 0; + } + goto st780; +tr2490: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st780; +st780: + if ( ++p == pe ) + goto _test_eof780; +case 780: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2491; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2490; + goto tr71; +tr2482: + { + s->buffer_length = 0; + } + goto st781; +tr2492: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st781; +tr2476: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st781; +st781: + if ( ++p == pe ) + goto _test_eof781; +case 781: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2493; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2492; + goto tr71; +tr2469: + { + s->buffer_length = 0; + } + goto st782; +tr2494: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st782; +st782: + if ( ++p == pe ) + goto _test_eof782; +case 782: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2495; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2494; + goto tr71; +tr2499: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st783; +tr2500: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st783; +tr2503: + { + s->line_counter++; + } + goto st783; +tr2526: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st783; +tr2516: + { + if ((s->apl.addr_family == 1 && s->number64 <= 32) || + (s->apl.addr_family == 2 && s->number64 <= 128)) { + s->apl.prefix_length = (uint8_t)(s->number64); + } else { + WARN(ZS_BAD_APL); + p--; {goto st268;} + } + } + { + // Copy address to buffer. + uint8_t len; + switch (s->apl.addr_family) { + case 1: + len = ZS_INET4_ADDR_LENGTH; + memcpy(s->buffer, s->addr, len); + break; + case 2: + len = ZS_INET6_ADDR_LENGTH; + memcpy(s->buffer, s->addr, len); + break; + default: + WARN(ZS_BAD_APL); + p--; {goto st268;} + } + // Find prefix without trailing zeroes. + while (len > 0) { + if ((s->buffer[len - 1] & 255) != 0) { + break; + } + len--; + } + // Check for rdata overflow. + if (rdata_tail + 4 + len > rdata_stop) { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + // Write address family. + *((uint16_t *)rdata_tail) = htons(s->apl.addr_family); + rdata_tail += 2; + // Write prefix length in bits. + *(rdata_tail) = s->apl.prefix_length; + rdata_tail += 1; + // Write negation flag + prefix length in bytes. + *(rdata_tail) = len + s->apl.excl_flag; + rdata_tail += 1; + // Write address prefix non-null data. + memcpy(rdata_tail, s->buffer, len); + rdata_tail += len; + } + goto st783; +tr2517: + { + if ((s->apl.addr_family == 1 && s->number64 <= 32) || + (s->apl.addr_family == 2 && s->number64 <= 128)) { + s->apl.prefix_length = (uint8_t)(s->number64); + } else { + WARN(ZS_BAD_APL); + p--; {goto st268;} + } + } + { + // Copy address to buffer. + uint8_t len; + switch (s->apl.addr_family) { + case 1: + len = ZS_INET4_ADDR_LENGTH; + memcpy(s->buffer, s->addr, len); + break; + case 2: + len = ZS_INET6_ADDR_LENGTH; + memcpy(s->buffer, s->addr, len); + break; + default: + WARN(ZS_BAD_APL); + p--; {goto st268;} + } + // Find prefix without trailing zeroes. + while (len > 0) { + if ((s->buffer[len - 1] & 255) != 0) { + break; + } + len--; + } + // Check for rdata overflow. + if (rdata_tail + 4 + len > rdata_stop) { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + // Write address family. + *((uint16_t *)rdata_tail) = htons(s->apl.addr_family); + rdata_tail += 2; + // Write prefix length in bits. + *(rdata_tail) = s->apl.prefix_length; + rdata_tail += 1; + // Write negation flag + prefix length in bytes. + *(rdata_tail) = len + s->apl.excl_flag; + rdata_tail += 1; + // Write address prefix non-null data. + memcpy(rdata_tail, s->buffer, len); + rdata_tail += len; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st783; +tr2518: + { + if ((s->apl.addr_family == 1 && s->number64 <= 32) || + (s->apl.addr_family == 2 && s->number64 <= 128)) { + s->apl.prefix_length = (uint8_t)(s->number64); + } else { + WARN(ZS_BAD_APL); + p--; {goto st268;} + } + } + { + // Copy address to buffer. + uint8_t len; + switch (s->apl.addr_family) { + case 1: + len = ZS_INET4_ADDR_LENGTH; + memcpy(s->buffer, s->addr, len); + break; + case 2: + len = ZS_INET6_ADDR_LENGTH; + memcpy(s->buffer, s->addr, len); + break; + default: + WARN(ZS_BAD_APL); + p--; {goto st268;} + } + // Find prefix without trailing zeroes. + while (len > 0) { + if ((s->buffer[len - 1] & 255) != 0) { + break; + } + len--; + } + // Check for rdata overflow. + if (rdata_tail + 4 + len > rdata_stop) { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + // Write address family. + *((uint16_t *)rdata_tail) = htons(s->apl.addr_family); + rdata_tail += 2; + // Write prefix length in bits. + *(rdata_tail) = s->apl.prefix_length; + rdata_tail += 1; + // Write negation flag + prefix length in bytes. + *(rdata_tail) = len + s->apl.excl_flag; + rdata_tail += 1; + // Write address prefix non-null data. + memcpy(rdata_tail, s->buffer, len); + rdata_tail += len; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st783; +tr2520: + { + if ((s->apl.addr_family == 1 && s->number64 <= 32) || + (s->apl.addr_family == 2 && s->number64 <= 128)) { + s->apl.prefix_length = (uint8_t)(s->number64); + } else { + WARN(ZS_BAD_APL); + p--; {goto st268;} + } + } + { + // Copy address to buffer. + uint8_t len; + switch (s->apl.addr_family) { + case 1: + len = ZS_INET4_ADDR_LENGTH; + memcpy(s->buffer, s->addr, len); + break; + case 2: + len = ZS_INET6_ADDR_LENGTH; + memcpy(s->buffer, s->addr, len); + break; + default: + WARN(ZS_BAD_APL); + p--; {goto st268;} + } + // Find prefix without trailing zeroes. + while (len > 0) { + if ((s->buffer[len - 1] & 255) != 0) { + break; + } + len--; + } + // Check for rdata overflow. + if (rdata_tail + 4 + len > rdata_stop) { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + // Write address family. + *((uint16_t *)rdata_tail) = htons(s->apl.addr_family); + rdata_tail += 2; + // Write prefix length in bits. + *(rdata_tail) = s->apl.prefix_length; + rdata_tail += 1; + // Write negation flag + prefix length in bytes. + *(rdata_tail) = len + s->apl.excl_flag; + rdata_tail += 1; + // Write address prefix non-null data. + memcpy(rdata_tail, s->buffer, len); + rdata_tail += len; + } + { + s->line_counter++; + } + goto st783; +st783: + if ( ++p == pe ) + goto _test_eof783; +case 783: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto st783; + case 32: goto st783; + case 33: goto tr2498; + case 40: goto tr2499; + case 41: goto tr2500; + case 49: goto tr2501; + case 50: goto tr2502; + case 2058: goto tr2503; + case 2107: goto tr2504; + case 2314: goto tr2505; + case 2363: goto tr2505; + case 2570: goto tr2506; + case 2619: goto tr2507; + } + goto tr2496; +tr2498: + { + memset(&(s->apl), 0, sizeof(s->apl)); + } + { + s->apl.excl_flag = 128; // dec 128 = bin 10000000. + } + goto st784; +st784: + if ( ++p == pe ) + goto _test_eof784; +case 784: + switch( (*p) ) { + case 49: goto tr2508; + case 50: goto tr2509; + } + goto tr2496; +tr2501: + { + memset(&(s->apl), 0, sizeof(s->apl)); + } + { + s->apl.addr_family = 1; + } + goto st785; +tr2508: + { + s->apl.addr_family = 1; + } + goto st785; +st785: + if ( ++p == pe ) + goto _test_eof785; +case 785: + if ( (*p) == 58 ) + goto st786; + goto tr2496; +st786: + if ( ++p == pe ) + goto _test_eof786; +case 786: + if ( (*p) == 46 ) + goto tr2512; + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr2512; + goto tr2511; +tr2512: + { + s->buffer_length = 0; + } + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + goto st787; +tr2513: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + goto st787; +st787: + if ( ++p == pe ) + goto _test_eof787; +case 787: + if ( (*p) == 47 ) + goto tr2514; + if ( 46 <= (*p) && (*p) <= 57 ) + goto tr2513; + goto tr2511; +tr2514: + { + s->buffer[s->buffer_length] = 0; + + if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) { + WARN(ZS_BAD_IPV4); + p--; {goto st268;} + } + } + goto st788; +tr2530: + { + s->buffer[s->buffer_length] = 0; + + if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) { + WARN(ZS_BAD_IPV6); + p--; {goto st268;} + } + } + goto st788; +st788: + if ( ++p == pe ) + goto _test_eof788; +case 788: + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr2515; + goto tr2496; +tr2515: + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st789; +tr2519: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st789; +st789: + if ( ++p == pe ) + goto _test_eof789; +case 789: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto tr2516; + case 32: goto tr2516; + case 40: goto tr2517; + case 41: goto tr2518; + case 2058: goto tr2520; + case 2107: goto tr2521; + case 2314: goto tr2522; + case 2363: goto tr2522; + case 2570: goto tr2523; + case 2619: goto tr2524; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2519; + goto tr2496; +tr2504: + { + s->buffer_length = 0; + } + goto st790; +tr2525: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st790; +tr2521: + { + if ((s->apl.addr_family == 1 && s->number64 <= 32) || + (s->apl.addr_family == 2 && s->number64 <= 128)) { + s->apl.prefix_length = (uint8_t)(s->number64); + } else { + WARN(ZS_BAD_APL); + p--; {goto st268;} + } + } + { + // Copy address to buffer. + uint8_t len; + switch (s->apl.addr_family) { + case 1: + len = ZS_INET4_ADDR_LENGTH; + memcpy(s->buffer, s->addr, len); + break; + case 2: + len = ZS_INET6_ADDR_LENGTH; + memcpy(s->buffer, s->addr, len); + break; + default: + WARN(ZS_BAD_APL); + p--; {goto st268;} + } + // Find prefix without trailing zeroes. + while (len > 0) { + if ((s->buffer[len - 1] & 255) != 0) { + break; + } + len--; + } + // Check for rdata overflow. + if (rdata_tail + 4 + len > rdata_stop) { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + // Write address family. + *((uint16_t *)rdata_tail) = htons(s->apl.addr_family); + rdata_tail += 2; + // Write prefix length in bits. + *(rdata_tail) = s->apl.prefix_length; + rdata_tail += 1; + // Write negation flag + prefix length in bytes. + *(rdata_tail) = len + s->apl.excl_flag; + rdata_tail += 1; + // Write address prefix non-null data. + memcpy(rdata_tail, s->buffer, len); + rdata_tail += len; + } + { + s->buffer_length = 0; + } + goto st790; +st790: + if ( ++p == pe ) + goto _test_eof790; +case 790: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2526; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2525; + goto tr71; +tr2505: + { + p--; {cs = stack[--top];goto _again;} + } + goto st1183; +tr2522: + { + if ((s->apl.addr_family == 1 && s->number64 <= 32) || + (s->apl.addr_family == 2 && s->number64 <= 128)) { + s->apl.prefix_length = (uint8_t)(s->number64); + } else { + WARN(ZS_BAD_APL); + p--; {goto st268;} + } + } + { + // Copy address to buffer. + uint8_t len; + switch (s->apl.addr_family) { + case 1: + len = ZS_INET4_ADDR_LENGTH; + memcpy(s->buffer, s->addr, len); + break; + case 2: + len = ZS_INET6_ADDR_LENGTH; + memcpy(s->buffer, s->addr, len); + break; + default: + WARN(ZS_BAD_APL); + p--; {goto st268;} + } + // Find prefix without trailing zeroes. + while (len > 0) { + if ((s->buffer[len - 1] & 255) != 0) { + break; + } + len--; + } + // Check for rdata overflow. + if (rdata_tail + 4 + len > rdata_stop) { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + // Write address family. + *((uint16_t *)rdata_tail) = htons(s->apl.addr_family); + rdata_tail += 2; + // Write prefix length in bits. + *(rdata_tail) = s->apl.prefix_length; + rdata_tail += 1; + // Write negation flag + prefix length in bytes. + *(rdata_tail) = len + s->apl.excl_flag; + rdata_tail += 1; + // Write address prefix non-null data. + memcpy(rdata_tail, s->buffer, len); + rdata_tail += len; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1183; +st1183: + if ( ++p == pe ) + goto _test_eof1183; +case 1183: + goto st0; +tr2506: + { + s->line_counter++; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1184; +tr2523: + { + if ((s->apl.addr_family == 1 && s->number64 <= 32) || + (s->apl.addr_family == 2 && s->number64 <= 128)) { + s->apl.prefix_length = (uint8_t)(s->number64); + } else { + WARN(ZS_BAD_APL); + p--; {goto st268;} + } + } + { + // Copy address to buffer. + uint8_t len; + switch (s->apl.addr_family) { + case 1: + len = ZS_INET4_ADDR_LENGTH; + memcpy(s->buffer, s->addr, len); + break; + case 2: + len = ZS_INET6_ADDR_LENGTH; + memcpy(s->buffer, s->addr, len); + break; + default: + WARN(ZS_BAD_APL); + p--; {goto st268;} + } + // Find prefix without trailing zeroes. + while (len > 0) { + if ((s->buffer[len - 1] & 255) != 0) { + break; + } + len--; + } + // Check for rdata overflow. + if (rdata_tail + 4 + len > rdata_stop) { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + // Write address family. + *((uint16_t *)rdata_tail) = htons(s->apl.addr_family); + rdata_tail += 2; + // Write prefix length in bits. + *(rdata_tail) = s->apl.prefix_length; + rdata_tail += 1; + // Write negation flag + prefix length in bytes. + *(rdata_tail) = len + s->apl.excl_flag; + rdata_tail += 1; + // Write address prefix non-null data. + memcpy(rdata_tail, s->buffer, len); + rdata_tail += len; + } + { + s->line_counter++; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1184; +st1184: + if ( ++p == pe ) + goto _test_eof1184; +case 1184: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto st783; + case 32: goto st783; + case 33: goto tr2498; + case 40: goto tr2499; + case 41: goto tr2500; + case 49: goto tr2501; + case 50: goto tr2502; + case 2058: goto tr2503; + case 2107: goto tr2504; + case 2314: goto tr2505; + case 2363: goto tr2505; + case 2570: goto tr2506; + case 2619: goto tr2507; + } + goto tr2496; +tr2502: + { + memset(&(s->apl), 0, sizeof(s->apl)); + } + { + s->apl.addr_family = 2; + } + goto st791; +tr2509: + { + s->apl.addr_family = 2; + } + goto st791; +st791: + if ( ++p == pe ) + goto _test_eof791; +case 791: + if ( (*p) == 58 ) + goto st792; + goto tr2496; +st792: + if ( ++p == pe ) + goto _test_eof792; +case 792: + if ( (*p) == 46 ) + goto tr2528; + if ( (*p) < 65 ) { + if ( 48 <= (*p) && (*p) <= 58 ) + goto tr2528; + } else if ( (*p) > 70 ) { + if ( 97 <= (*p) && (*p) <= 102 ) + goto tr2528; + } else + goto tr2528; + goto tr2511; +tr2528: + { + s->buffer_length = 0; + } + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + goto st793; +tr2529: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + goto st793; +st793: + if ( ++p == pe ) + goto _test_eof793; +case 793: + if ( (*p) == 47 ) + goto tr2530; + if ( (*p) < 65 ) { + if ( 46 <= (*p) && (*p) <= 58 ) + goto tr2529; + } else if ( (*p) > 70 ) { + if ( 97 <= (*p) && (*p) <= 102 ) + goto tr2529; + } else + goto tr2529; + goto tr2511; +tr2507: + { + s->buffer_length = 0; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1185; +tr2524: + { + if ((s->apl.addr_family == 1 && s->number64 <= 32) || + (s->apl.addr_family == 2 && s->number64 <= 128)) { + s->apl.prefix_length = (uint8_t)(s->number64); + } else { + WARN(ZS_BAD_APL); + p--; {goto st268;} + } + } + { + // Copy address to buffer. + uint8_t len; + switch (s->apl.addr_family) { + case 1: + len = ZS_INET4_ADDR_LENGTH; + memcpy(s->buffer, s->addr, len); + break; + case 2: + len = ZS_INET6_ADDR_LENGTH; + memcpy(s->buffer, s->addr, len); + break; + default: + WARN(ZS_BAD_APL); + p--; {goto st268;} + } + // Find prefix without trailing zeroes. + while (len > 0) { + if ((s->buffer[len - 1] & 255) != 0) { + break; + } + len--; + } + // Check for rdata overflow. + if (rdata_tail + 4 + len > rdata_stop) { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + // Write address family. + *((uint16_t *)rdata_tail) = htons(s->apl.addr_family); + rdata_tail += 2; + // Write prefix length in bits. + *(rdata_tail) = s->apl.prefix_length; + rdata_tail += 1; + // Write negation flag + prefix length in bytes. + *(rdata_tail) = len + s->apl.excl_flag; + rdata_tail += 1; + // Write address prefix non-null data. + memcpy(rdata_tail, s->buffer, len); + rdata_tail += len; + } + { + s->buffer_length = 0; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1185; +st1185: + if ( ++p == pe ) + goto _test_eof1185; +case 1185: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2526; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2525; + goto tr71; +st794: + if ( ++p == pe ) + goto _test_eof794; +case 794: + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr2531; + goto tr1885; +tr2531: + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st795; +tr2535: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st795; +st795: + if ( ++p == pe ) + goto _test_eof795; +case 795: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr2532; + case 32: goto tr2532; + case 40: goto tr2533; + case 41: goto tr2534; + case 1034: goto tr2536; + case 1083: goto tr2537; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2535; + goto tr1885; +tr2539: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st796; +tr2540: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st796; +tr2542: + { + s->line_counter++; + } + goto st796; +tr2579: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st796; +tr2532: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + goto st796; +tr2533: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st796; +tr2534: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st796; +tr2536: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st796; +st796: + if ( ++p == pe ) + goto _test_eof796; +case 796: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st796; + case 32: goto st796; + case 40: goto tr2539; + case 41: goto tr2540; + case 1034: goto tr2542; + case 1083: goto tr2543; + } + if ( _widec < 65 ) { + if ( 48 <= _widec && _widec <= 57 ) + goto tr2541; + } else if ( _widec > 90 ) { + if ( 97 <= _widec && _widec <= 122 ) + goto tr2541; + } else + goto tr2541; + goto tr71; +tr2541: + { p--; {stack[top++] = 797;goto st487;} } + goto st797; +st797: + if ( ++p == pe ) + goto _test_eof797; +case 797: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st798; + case 32: goto st798; + case 40: goto tr2545; + case 41: goto tr2546; + case 1034: goto tr2547; + case 1083: goto tr2548; + } + goto tr71; +tr2545: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st798; +tr2546: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st798; +tr2547: + { + s->line_counter++; + } + goto st798; +tr2577: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st798; +st798: + if ( ++p == pe ) + goto _test_eof798; +case 798: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st798; + case 32: goto st798; + case 40: goto tr2545; + case 41: goto tr2546; + case 1034: goto tr2547; + case 1083: goto tr2548; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2549; + goto tr1885; +tr2549: + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st799; +tr2553: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st799; +st799: + if ( ++p == pe ) + goto _test_eof799; +case 799: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr2550; + case 32: goto tr2550; + case 40: goto tr2551; + case 41: goto tr2552; + case 1034: goto tr2554; + case 1083: goto tr2555; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2553; + goto tr1885; +tr2558: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st800; +tr2559: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st800; +tr2561: + { + s->line_counter++; + } + goto st800; +tr2575: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st800; +tr2550: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + goto st800; +tr2551: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st800; +tr2552: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st800; +tr2554: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st800; +st800: + if ( ++p == pe ) + goto _test_eof800; +case 800: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st800; + case 32: goto st800; + case 40: goto tr2558; + case 41: goto tr2559; + case 1034: goto tr2561; + case 1083: goto tr2562; + } + if ( _widec < 65 ) { + if ( 48 <= _widec && _widec <= 57 ) + goto tr2560; + } else if ( _widec > 70 ) { + if ( 97 <= _widec && _widec <= 102 ) + goto tr2560; + } else + goto tr2560; + goto tr2556; +tr2560: + { + if (rdata_tail <= rdata_stop) { + *rdata_tail = first_hex_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + goto st801; +st801: + if ( ++p == pe ) + goto _test_eof801; +case 801: + if ( (*p) < 65 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr2563; + } else if ( (*p) > 70 ) { + if ( 97 <= (*p) && (*p) <= 102 ) + goto tr2563; + } else + goto tr2563; + goto tr2556; +tr2565: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st802; +tr2566: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st802; +tr2567: + { + s->line_counter++; + } + goto st802; +tr2573: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st802; +tr2563: + { + *rdata_tail += second_hex_to_num[(uint8_t)(*p)]; + rdata_tail++; + } + goto st802; +st802: + if ( ++p == pe ) + goto _test_eof802; +case 802: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto st802; + case 32: goto st802; + case 40: goto tr2565; + case 41: goto tr2566; + case 2058: goto tr2567; + case 2107: goto tr2568; + case 2314: goto tr2569; + case 2363: goto tr2569; + case 2570: goto tr2570; + case 2619: goto tr2571; + } + if ( _widec < 65 ) { + if ( 48 <= _widec && _widec <= 57 ) + goto tr2560; + } else if ( _widec > 70 ) { + if ( 97 <= _widec && _widec <= 102 ) + goto tr2560; + } else + goto tr2560; + goto tr2556; +tr2568: + { + s->buffer_length = 0; + } + goto st803; +tr2572: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st803; +st803: + if ( ++p == pe ) + goto _test_eof803; +case 803: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2573; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2572; + goto tr2556; +tr2569: + { + p--; {cs = stack[--top];goto _again;} + } + goto st1186; +st1186: + if ( ++p == pe ) + goto _test_eof1186; +case 1186: + goto st0; +tr2570: + { + s->line_counter++; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1187; +st1187: + if ( ++p == pe ) + goto _test_eof1187; +case 1187: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto st802; + case 32: goto st802; + case 40: goto tr2565; + case 41: goto tr2566; + case 2058: goto tr2567; + case 2107: goto tr2568; + case 2314: goto tr2569; + case 2363: goto tr2569; + case 2570: goto tr2570; + case 2619: goto tr2571; + } + if ( _widec < 65 ) { + if ( 48 <= _widec && _widec <= 57 ) + goto tr2560; + } else if ( _widec > 70 ) { + if ( 97 <= _widec && _widec <= 102 ) + goto tr2560; + } else + goto tr2560; + goto tr2556; +tr2571: + { + s->buffer_length = 0; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1188; +st1188: + if ( ++p == pe ) + goto _test_eof1188; +case 1188: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2573; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2572; + goto tr2556; +tr2562: + { + s->buffer_length = 0; + } + goto st804; +tr2574: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st804; +tr2555: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st804; +st804: + if ( ++p == pe ) + goto _test_eof804; +case 804: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2575; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2574; + goto tr71; +tr2548: + { + s->buffer_length = 0; + } + goto st805; +tr2576: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st805; +st805: + if ( ++p == pe ) + goto _test_eof805; +case 805: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2577; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2576; + goto tr71; +tr2543: + { + s->buffer_length = 0; + } + goto st806; +tr2578: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st806; +tr2537: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st806; +st806: + if ( ++p == pe ) + goto _test_eof806; +case 806: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2579; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2578; + goto tr71; +st807: + if ( ++p == pe ) + goto _test_eof807; +case 807: + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr2580; + goto tr1885; +tr2580: + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st808; +tr2584: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st808; +st808: + if ( ++p == pe ) + goto _test_eof808; +case 808: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr2581; + case 32: goto tr2581; + case 40: goto tr2582; + case 41: goto tr2583; + case 1034: goto tr2585; + case 1083: goto tr2586; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2584; + goto tr1885; +tr2588: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st809; +tr2589: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st809; +tr2591: + { + s->line_counter++; + } + goto st809; +tr2619: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st809; +tr2581: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + goto st809; +tr2582: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st809; +tr2583: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st809; +tr2585: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st809; +st809: + if ( ++p == pe ) + goto _test_eof809; +case 809: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st809; + case 32: goto st809; + case 40: goto tr2588; + case 41: goto tr2589; + case 1034: goto tr2591; + case 1083: goto tr2592; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2590; + goto tr1885; +tr2590: + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st810; +tr2596: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st810; +st810: + if ( ++p == pe ) + goto _test_eof810; +case 810: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr2593; + case 32: goto tr2593; + case 40: goto tr2594; + case 41: goto tr2595; + case 1034: goto tr2597; + case 1083: goto tr2598; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2596; + goto tr1885; +tr2600: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st811; +tr2601: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st811; +tr2603: + { + s->line_counter++; + } + goto st811; +tr2617: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st811; +tr2593: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + goto st811; +tr2594: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st811; +tr2595: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st811; +tr2597: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st811; +st811: + if ( ++p == pe ) + goto _test_eof811; +case 811: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st811; + case 32: goto st811; + case 40: goto tr2600; + case 41: goto tr2601; + case 1034: goto tr2603; + case 1083: goto tr2604; + } + if ( _widec < 65 ) { + if ( 48 <= _widec && _widec <= 57 ) + goto tr2602; + } else if ( _widec > 70 ) { + if ( 97 <= _widec && _widec <= 102 ) + goto tr2602; + } else + goto tr2602; + goto tr2556; +tr2602: + { + if (rdata_tail <= rdata_stop) { + *rdata_tail = first_hex_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + goto st812; +st812: + if ( ++p == pe ) + goto _test_eof812; +case 812: + if ( (*p) < 65 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr2605; + } else if ( (*p) > 70 ) { + if ( 97 <= (*p) && (*p) <= 102 ) + goto tr2605; + } else + goto tr2605; + goto tr2556; +tr2607: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st813; +tr2608: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st813; +tr2609: + { + s->line_counter++; + } + goto st813; +tr2615: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st813; +tr2605: + { + *rdata_tail += second_hex_to_num[(uint8_t)(*p)]; + rdata_tail++; + } + goto st813; +st813: + if ( ++p == pe ) + goto _test_eof813; +case 813: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto st813; + case 32: goto st813; + case 40: goto tr2607; + case 41: goto tr2608; + case 2058: goto tr2609; + case 2107: goto tr2610; + case 2314: goto tr2611; + case 2363: goto tr2611; + case 2570: goto tr2612; + case 2619: goto tr2613; + } + if ( _widec < 65 ) { + if ( 48 <= _widec && _widec <= 57 ) + goto tr2602; + } else if ( _widec > 70 ) { + if ( 97 <= _widec && _widec <= 102 ) + goto tr2602; + } else + goto tr2602; + goto tr2556; +tr2610: + { + s->buffer_length = 0; + } + goto st814; +tr2614: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st814; +st814: + if ( ++p == pe ) + goto _test_eof814; +case 814: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2615; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2614; + goto tr2556; +tr2611: + { + p--; {cs = stack[--top];goto _again;} + } + goto st1189; +st1189: + if ( ++p == pe ) + goto _test_eof1189; +case 1189: + goto st0; +tr2612: + { + s->line_counter++; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1190; +st1190: + if ( ++p == pe ) + goto _test_eof1190; +case 1190: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto st813; + case 32: goto st813; + case 40: goto tr2607; + case 41: goto tr2608; + case 2058: goto tr2609; + case 2107: goto tr2610; + case 2314: goto tr2611; + case 2363: goto tr2611; + case 2570: goto tr2612; + case 2619: goto tr2613; + } + if ( _widec < 65 ) { + if ( 48 <= _widec && _widec <= 57 ) + goto tr2602; + } else if ( _widec > 70 ) { + if ( 97 <= _widec && _widec <= 102 ) + goto tr2602; + } else + goto tr2602; + goto tr2556; +tr2613: + { + s->buffer_length = 0; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1191; +st1191: + if ( ++p == pe ) + goto _test_eof1191; +case 1191: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2615; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2614; + goto tr2556; +tr2604: + { + s->buffer_length = 0; + } + goto st815; +tr2616: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st815; +tr2598: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st815; +st815: + if ( ++p == pe ) + goto _test_eof815; +case 815: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2617; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2616; + goto tr71; +tr2592: + { + s->buffer_length = 0; + } + goto st816; +tr2618: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st816; +tr2586: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st816; +st816: + if ( ++p == pe ) + goto _test_eof816; +case 816: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2619; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2618; + goto tr71; +st817: + if ( ++p == pe ) + goto _test_eof817; +case 817: + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr2620; + goto tr1885; +tr2620: + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st818; +tr2624: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st818; +st818: + if ( ++p == pe ) + goto _test_eof818; +case 818: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr2621; + case 32: goto tr2621; + case 40: goto tr2622; + case 41: goto tr2623; + case 1034: goto tr2625; + case 1083: goto tr2626; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2624; + goto tr1885; +tr2629: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st819; +tr2630: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st819; +tr2635: + { + s->line_counter++; + } + goto st819; +tr2828: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st819; +tr2621: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + goto st819; +tr2622: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st819; +tr2623: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st819; +tr2625: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st819; +st819: + if ( ++p == pe ) + goto _test_eof819; +case 819: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st819; + case 32: goto st819; + case 40: goto tr2629; + case 41: goto tr2630; + case 48: goto tr2631; + case 49: goto tr2632; + case 50: goto tr2633; + case 51: goto tr2634; + case 1034: goto tr2635; + case 1083: goto tr2636; + } + goto tr2627; +tr2631: + { + *(rdata_tail++) = 0; + } + goto st820; +st820: + if ( ++p == pe ) + goto _test_eof820; +case 820: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st821; + case 32: goto st821; + case 40: goto tr2638; + case 41: goto tr2639; + case 1034: goto tr2640; + case 1083: goto tr2641; + } + goto tr2627; +tr2638: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st821; +tr2639: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st821; +tr2640: + { + s->line_counter++; + } + goto st821; +tr2690: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st821; +st821: + if ( ++p == pe ) + goto _test_eof821; +case 821: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st821; + case 32: goto st821; + case 40: goto tr2638; + case 41: goto tr2639; + case 1034: goto tr2640; + case 1083: goto tr2641; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2643; + goto tr2642; +tr2643: + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st822; +tr2647: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st822; +st822: + if ( ++p == pe ) + goto _test_eof822; +case 822: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr2644; + case 32: goto tr2644; + case 40: goto tr2645; + case 41: goto tr2646; + case 1034: goto tr2648; + case 1083: goto tr2649; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2647; + goto tr2642; +tr2651: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st823; +tr2652: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st823; +tr2654: + { + s->line_counter++; + } + goto st823; +tr2688: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st823; +tr2644: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + goto st823; +tr2645: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st823; +tr2646: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st823; +tr2648: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st823; +st823: + if ( ++p == pe ) + goto _test_eof823; +case 823: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st823; + case 32: goto st823; + case 40: goto tr2651; + case 41: goto tr2652; + case 46: goto st824; + case 1034: goto tr2654; + case 1083: goto tr2655; + } + goto tr2627; +st824: + if ( ++p == pe ) + goto _test_eof824; +case 824: + _widec = (*p); + if ( (*p) < 32 ) { + if ( (*p) > 9 ) { + if ( 10 <= (*p) && (*p) <= 10 ) { + _widec = (short)(8832 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + if ( + s->number64 != 0 ) _widec += 1024; + if ( + s->number64 == 0 ) _widec += 2048; + } + } else if ( (*p) >= 9 ) { + _widec = (short)(5760 + ((*p) - -128)); + if ( + s->number64 != 0 ) _widec += 256; + if ( + s->number64 == 0 ) _widec += 512; + } + } else if ( (*p) > 32 ) { + if ( (*p) < 41 ) { + if ( 40 <= (*p) && (*p) <= 40 ) { + _widec = (short)(5760 + ((*p) - -128)); + if ( + s->number64 != 0 ) _widec += 256; + if ( + s->number64 == 0 ) _widec += 512; + } + } else if ( (*p) > 41 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(8832 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + if ( + s->number64 != 0 ) _widec += 1024; + if ( + s->number64 == 0 ) _widec += 2048; + } + } else { + _widec = (short)(5760 + ((*p) - -128)); + if ( + s->number64 != 0 ) _widec += 256; + if ( + s->number64 == 0 ) _widec += 512; + } + } else { + _widec = (short)(5760 + ((*p) - -128)); + if ( + s->number64 != 0 ) _widec += 256; + if ( + s->number64 == 0 ) _widec += 512; + } + switch( _widec ) { + case 6153: goto st825; + case 6176: goto st825; + case 6184: goto tr2658; + case 6185: goto tr2659; + case 6409: goto st828; + case 6432: goto st828; + case 6440: goto tr2661; + case 6441: goto tr2662; + case 6665: goto st830; + case 6688: goto st830; + case 6696: goto tr2664; + case 6697: goto tr2665; + case 9482: goto tr2666; + case 9531: goto tr2666; + case 9738: goto tr2666; + case 9787: goto tr2666; + case 10250: goto tr2667; + case 10299: goto tr2668; + case 10506: goto tr2666; + case 10555: goto tr2666; + case 10762: goto tr2669; + case 10811: goto tr2670; + case 11274: goto tr2671; + case 11323: goto tr2672; + case 11530: goto tr2666; + case 11579: goto tr2666; + case 11786: goto tr2673; + case 11835: goto tr2674; + case 12298: goto tr2675; + case 12347: goto tr2676; + case 12554: goto tr2666; + case 12603: goto tr2666; + case 12810: goto tr2677; + case 12859: goto tr2678; + } + goto tr2656; +tr2658: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st825; +tr2659: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st825; +tr2667: + { + s->line_counter++; + } + goto st825; +tr2682: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st825; +tr2801: + { + rdata_tail += s->dname_tmp_length; + } + goto st825; +tr2802: + { + rdata_tail += s->dname_tmp_length; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st825; +tr2803: + { + rdata_tail += s->dname_tmp_length; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st825; +tr2811: + { + rdata_tail += s->dname_tmp_length; + } + { + s->line_counter++; + } + goto st825; +tr2712: + { + s->buffer[s->buffer_length] = 0; + + if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) { + WARN(ZS_BAD_IPV4); + p--; {goto st268;} + } + } + { + memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH); + rdata_tail += ZS_INET4_ADDR_LENGTH; + } + goto st825; +tr2713: + { + s->buffer[s->buffer_length] = 0; + + if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) { + WARN(ZS_BAD_IPV4); + p--; {goto st268;} + } + } + { + memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH); + rdata_tail += ZS_INET4_ADDR_LENGTH; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st825; +tr2714: + { + s->buffer[s->buffer_length] = 0; + + if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) { + WARN(ZS_BAD_IPV4); + p--; {goto st268;} + } + } + { + memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH); + rdata_tail += ZS_INET4_ADDR_LENGTH; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st825; +tr2722: + { + s->buffer[s->buffer_length] = 0; + + if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) { + WARN(ZS_BAD_IPV4); + p--; {goto st268;} + } + } + { + memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH); + rdata_tail += ZS_INET4_ADDR_LENGTH; + } + { + s->line_counter++; + } + goto st825; +tr2757: + { + s->buffer[s->buffer_length] = 0; + + if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) { + WARN(ZS_BAD_IPV6); + p--; {goto st268;} + } + } + { + memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH); + rdata_tail += ZS_INET6_ADDR_LENGTH; + } + goto st825; +tr2758: + { + s->buffer[s->buffer_length] = 0; + + if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) { + WARN(ZS_BAD_IPV6); + p--; {goto st268;} + } + } + { + memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH); + rdata_tail += ZS_INET6_ADDR_LENGTH; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st825; +tr2759: + { + s->buffer[s->buffer_length] = 0; + + if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) { + WARN(ZS_BAD_IPV6); + p--; {goto st268;} + } + } + { + memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH); + rdata_tail += ZS_INET6_ADDR_LENGTH; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st825; +tr2767: + { + s->buffer[s->buffer_length] = 0; + + if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) { + WARN(ZS_BAD_IPV6); + p--; {goto st268;} + } + } + { + memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH); + rdata_tail += ZS_INET6_ADDR_LENGTH; + } + { + s->line_counter++; + } + goto st825; +st825: + if ( ++p == pe ) + goto _test_eof825; +case 825: + _widec = (*p); + if ( (*p) < 43 ) { + if ( (*p) < 32 ) { + if ( (*p) > 9 ) { + if ( 10 <= (*p) && (*p) <= 10 ) { + _widec = (short)(3200 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + s->number64 != 0 ) _widec += 512; + } + } else if ( (*p) >= 9 ) { + _widec = (short)(2688 + ((*p) - -128)); + if ( + s->number64 != 0 ) _widec += 256; + } + } else if ( (*p) > 32 ) { + if ( (*p) > 40 ) { + if ( 41 <= (*p) && (*p) <= 41 ) { + _widec = (short)(2688 + ((*p) - -128)); + if ( + s->number64 != 0 ) _widec += 256; + } + } else if ( (*p) >= 40 ) { + _widec = (short)(2688 + ((*p) - -128)); + if ( + s->number64 != 0 ) _widec += 256; + } + } else { + _widec = (short)(2688 + ((*p) - -128)); + if ( + s->number64 != 0 ) _widec += 256; + } + } else if ( (*p) > 43 ) { + if ( (*p) < 59 ) { + if ( (*p) > 47 ) { + if ( 48 <= (*p) && (*p) <= 57 ) { + _widec = (short)(2688 + ((*p) - -128)); + if ( + s->number64 != 0 ) _widec += 256; + } + } else if ( (*p) >= 47 ) { + _widec = (short)(2688 + ((*p) - -128)); + if ( + s->number64 != 0 ) _widec += 256; + } + } else if ( (*p) > 59 ) { + if ( (*p) > 90 ) { + if ( 97 <= (*p) && (*p) <= 122 ) { + _widec = (short)(2688 + ((*p) - -128)); + if ( + s->number64 != 0 ) _widec += 256; + } + } else if ( (*p) >= 65 ) { + _widec = (short)(2688 + ((*p) - -128)); + if ( + s->number64 != 0 ) _widec += 256; + } + } else { + _widec = (short)(3200 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + s->number64 != 0 ) _widec += 512; + } + } else { + _widec = (short)(2688 + ((*p) - -128)); + if ( + s->number64 != 0 ) _widec += 256; + } + switch( _widec ) { + case 3081: goto st825; + case 3104: goto st825; + case 3112: goto tr2658; + case 3113: goto tr2659; + case 3115: goto tr2680; + case 4106: goto tr2667; + case 4155: goto tr2668; + } + if ( _widec < 3137 ) { + if ( 3119 <= _widec && _widec <= 3129 ) + goto tr2680; + } else if ( _widec > 3162 ) { + if ( 3169 <= _widec && _widec <= 3194 ) + goto tr2680; + } else + goto tr2680; + goto tr2679; +tr2680: + { p--; {stack[top++] = 826;goto st329;} } + goto st826; +st826: + if ( ++p == pe ) + goto _test_eof826; +case 826: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1152 + ((*p) - -128)); + if ( + !s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1152 + ((*p) - -128)); + if ( + !s->multiline ) _widec += 256; + } + switch( _widec ) { + case 1546: goto tr2666; + case 1595: goto tr2666; + } + goto tr2679; +tr2666: + { + p--; {cs = stack[--top];goto _again;} + } + goto st1192; +tr2721: + { + s->buffer[s->buffer_length] = 0; + + if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) { + WARN(ZS_BAD_IPV4); + p--; {goto st268;} + } + } + { + memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH); + rdata_tail += ZS_INET4_ADDR_LENGTH; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1192; +tr2810: + { + rdata_tail += s->dname_tmp_length; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1192; +tr2766: + { + s->buffer[s->buffer_length] = 0; + + if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) { + WARN(ZS_BAD_IPV6); + p--; {goto st268;} + } + } + { + memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH); + rdata_tail += ZS_INET6_ADDR_LENGTH; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1192; +st1192: + if ( ++p == pe ) + goto _test_eof1192; +case 1192: + goto st0; +tr2668: + { + s->buffer_length = 0; + } + goto st827; +tr2681: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st827; +tr2812: + { + rdata_tail += s->dname_tmp_length; + } + { + s->buffer_length = 0; + } + goto st827; +tr2723: + { + s->buffer[s->buffer_length] = 0; + + if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) { + WARN(ZS_BAD_IPV4); + p--; {goto st268;} + } + } + { + memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH); + rdata_tail += ZS_INET4_ADDR_LENGTH; + } + { + s->buffer_length = 0; + } + goto st827; +tr2768: + { + s->buffer[s->buffer_length] = 0; + + if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) { + WARN(ZS_BAD_IPV6); + p--; {goto st268;} + } + } + { + memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH); + rdata_tail += ZS_INET6_ADDR_LENGTH; + } + { + s->buffer_length = 0; + } + goto st827; +st827: + if ( ++p == pe ) + goto _test_eof827; +case 827: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(3200 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + s->number64 != 0 ) _widec += 512; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(3200 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + s->number64 != 0 ) _widec += 512; + } + } else { + _widec = (short)(3200 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + s->number64 != 0 ) _widec += 512; + } + if ( _widec == 4106 ) + goto tr2682; + if ( 3968 <= _widec && _widec <= 4223 ) + goto tr2681; + goto tr2679; +tr2661: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st828; +tr2662: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st828; +tr2671: + { + s->line_counter++; + } + goto st828; +tr2684: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st828; +tr2804: + { + rdata_tail += s->dname_tmp_length; + } + goto st828; +tr2805: + { + rdata_tail += s->dname_tmp_length; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st828; +tr2806: + { + rdata_tail += s->dname_tmp_length; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st828; +tr2815: + { + rdata_tail += s->dname_tmp_length; + } + { + s->line_counter++; + } + goto st828; +tr2715: + { + s->buffer[s->buffer_length] = 0; + + if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) { + WARN(ZS_BAD_IPV4); + p--; {goto st268;} + } + } + { + memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH); + rdata_tail += ZS_INET4_ADDR_LENGTH; + } + goto st828; +tr2716: + { + s->buffer[s->buffer_length] = 0; + + if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) { + WARN(ZS_BAD_IPV4); + p--; {goto st268;} + } + } + { + memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH); + rdata_tail += ZS_INET4_ADDR_LENGTH; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st828; +tr2717: + { + s->buffer[s->buffer_length] = 0; + + if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) { + WARN(ZS_BAD_IPV4); + p--; {goto st268;} + } + } + { + memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH); + rdata_tail += ZS_INET4_ADDR_LENGTH; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st828; +tr2726: + { + s->buffer[s->buffer_length] = 0; + + if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) { + WARN(ZS_BAD_IPV4); + p--; {goto st268;} + } + } + { + memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH); + rdata_tail += ZS_INET4_ADDR_LENGTH; + } + { + s->line_counter++; + } + goto st828; +tr2760: + { + s->buffer[s->buffer_length] = 0; + + if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) { + WARN(ZS_BAD_IPV6); + p--; {goto st268;} + } + } + { + memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH); + rdata_tail += ZS_INET6_ADDR_LENGTH; + } + goto st828; +tr2761: + { + s->buffer[s->buffer_length] = 0; + + if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) { + WARN(ZS_BAD_IPV6); + p--; {goto st268;} + } + } + { + memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH); + rdata_tail += ZS_INET6_ADDR_LENGTH; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st828; +tr2762: + { + s->buffer[s->buffer_length] = 0; + + if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) { + WARN(ZS_BAD_IPV6); + p--; {goto st268;} + } + } + { + memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH); + rdata_tail += ZS_INET6_ADDR_LENGTH; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st828; +tr2771: + { + s->buffer[s->buffer_length] = 0; + + if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) { + WARN(ZS_BAD_IPV6); + p--; {goto st268;} + } + } + { + memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH); + rdata_tail += ZS_INET6_ADDR_LENGTH; + } + { + s->line_counter++; + } + goto st828; +st828: + if ( ++p == pe ) + goto _test_eof828; +case 828: + _widec = (*p); + if ( (*p) < 32 ) { + if ( (*p) > 9 ) { + if ( 10 <= (*p) && (*p) <= 10 ) { + _widec = (short)(12928 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + if ( + s->number64 == 0 ) _widec += 1024; + } + } else if ( (*p) >= 9 ) { + _widec = (short)(4224 + ((*p) - -128)); + if ( + s->number64 == 0 ) _widec += 256; + } + } else if ( (*p) > 32 ) { + if ( (*p) < 41 ) { + if ( 40 <= (*p) && (*p) <= 40 ) { + _widec = (short)(4224 + ((*p) - -128)); + if ( + s->number64 == 0 ) _widec += 256; + } + } else if ( (*p) > 41 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(12928 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + if ( + s->number64 == 0 ) _widec += 1024; + } + } else { + _widec = (short)(4224 + ((*p) - -128)); + if ( + s->number64 == 0 ) _widec += 256; + } + } else { + _widec = (short)(4224 + ((*p) - -128)); + if ( + s->number64 == 0 ) _widec += 256; + } + switch( _widec ) { + case 4617: goto st828; + case 4640: goto st828; + case 4648: goto tr2661; + case 4649: goto tr2662; + case 13578: goto tr2666; + case 13627: goto tr2666; + case 13834: goto tr2666; + case 13883: goto tr2666; + case 14346: goto tr2671; + case 14395: goto tr2672; + case 14602: goto tr2666; + case 14651: goto tr2666; + case 14858: goto tr2673; + case 14907: goto tr2674; + } + goto tr2679; +tr2672: + { + s->buffer_length = 0; + } + goto st829; +tr2683: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st829; +tr2816: + { + rdata_tail += s->dname_tmp_length; + } + { + s->buffer_length = 0; + } + goto st829; +tr2727: + { + s->buffer[s->buffer_length] = 0; + + if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) { + WARN(ZS_BAD_IPV4); + p--; {goto st268;} + } + } + { + memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH); + rdata_tail += ZS_INET4_ADDR_LENGTH; + } + { + s->buffer_length = 0; + } + goto st829; +tr2772: + { + s->buffer[s->buffer_length] = 0; + + if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) { + WARN(ZS_BAD_IPV6); + p--; {goto st268;} + } + } + { + memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH); + rdata_tail += ZS_INET6_ADDR_LENGTH; + } + { + s->buffer_length = 0; + } + goto st829; +st829: + if ( ++p == pe ) + goto _test_eof829; +case 829: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(4736 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + s->number64 == 0 ) _widec += 512; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(4736 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + s->number64 == 0 ) _widec += 512; + } + } else { + _widec = (short)(4736 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + s->number64 == 0 ) _widec += 512; + } + if ( _widec == 5642 ) + goto tr2684; + if ( 5504 <= _widec && _widec <= 5759 ) + goto tr2683; + goto tr2679; +tr2673: + { + s->line_counter++; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1193; +tr2728: + { + s->buffer[s->buffer_length] = 0; + + if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) { + WARN(ZS_BAD_IPV4); + p--; {goto st268;} + } + } + { + memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH); + rdata_tail += ZS_INET4_ADDR_LENGTH; + } + { + s->line_counter++; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1193; +tr2773: + { + s->buffer[s->buffer_length] = 0; + + if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) { + WARN(ZS_BAD_IPV6); + p--; {goto st268;} + } + } + { + memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH); + rdata_tail += ZS_INET6_ADDR_LENGTH; + } + { + s->line_counter++; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1193; +tr2817: + { + rdata_tail += s->dname_tmp_length; + } + { + s->line_counter++; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1193; +st1193: + if ( ++p == pe ) + goto _test_eof1193; +case 1193: + _widec = (*p); + if ( (*p) < 32 ) { + if ( (*p) > 9 ) { + if ( 10 <= (*p) && (*p) <= 10 ) { + _widec = (short)(12928 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + if ( + s->number64 == 0 ) _widec += 1024; + } + } else if ( (*p) >= 9 ) { + _widec = (short)(4224 + ((*p) - -128)); + if ( + s->number64 == 0 ) _widec += 256; + } + } else if ( (*p) > 32 ) { + if ( (*p) < 41 ) { + if ( 40 <= (*p) && (*p) <= 40 ) { + _widec = (short)(4224 + ((*p) - -128)); + if ( + s->number64 == 0 ) _widec += 256; + } + } else if ( (*p) > 41 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(12928 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + if ( + s->number64 == 0 ) _widec += 1024; + } + } else { + _widec = (short)(4224 + ((*p) - -128)); + if ( + s->number64 == 0 ) _widec += 256; + } + } else { + _widec = (short)(4224 + ((*p) - -128)); + if ( + s->number64 == 0 ) _widec += 256; + } + switch( _widec ) { + case 4617: goto st828; + case 4640: goto st828; + case 4648: goto tr2661; + case 4649: goto tr2662; + case 13578: goto tr2666; + case 13627: goto tr2666; + case 13834: goto tr2666; + case 13883: goto tr2666; + case 14346: goto tr2671; + case 14395: goto tr2672; + case 14602: goto tr2666; + case 14651: goto tr2666; + case 14858: goto tr2673; + case 14907: goto tr2674; + } + goto tr2679; +tr2674: + { + s->buffer_length = 0; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1194; +tr2729: + { + s->buffer[s->buffer_length] = 0; + + if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) { + WARN(ZS_BAD_IPV4); + p--; {goto st268;} + } + } + { + memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH); + rdata_tail += ZS_INET4_ADDR_LENGTH; + } + { + s->buffer_length = 0; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1194; +tr2774: + { + s->buffer[s->buffer_length] = 0; + + if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) { + WARN(ZS_BAD_IPV6); + p--; {goto st268;} + } + } + { + memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH); + rdata_tail += ZS_INET6_ADDR_LENGTH; + } + { + s->buffer_length = 0; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1194; +tr2818: + { + rdata_tail += s->dname_tmp_length; + } + { + s->buffer_length = 0; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1194; +st1194: + if ( ++p == pe ) + goto _test_eof1194; +case 1194: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(4736 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + s->number64 == 0 ) _widec += 512; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(4736 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + s->number64 == 0 ) _widec += 512; + } + } else { + _widec = (short)(4736 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + s->number64 == 0 ) _widec += 512; + } + if ( _widec == 5642 ) + goto tr2684; + if ( 5504 <= _widec && _widec <= 5759 ) + goto tr2683; + goto tr2679; +tr2664: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st830; +tr2665: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st830; +tr2675: + { + s->line_counter++; + } + goto st830; +tr2686: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st830; +tr2807: + { + rdata_tail += s->dname_tmp_length; + } + goto st830; +tr2808: + { + rdata_tail += s->dname_tmp_length; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st830; +tr2809: + { + rdata_tail += s->dname_tmp_length; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st830; +tr2819: + { + rdata_tail += s->dname_tmp_length; + } + { + s->line_counter++; + } + goto st830; +tr2718: + { + s->buffer[s->buffer_length] = 0; + + if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) { + WARN(ZS_BAD_IPV4); + p--; {goto st268;} + } + } + { + memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH); + rdata_tail += ZS_INET4_ADDR_LENGTH; + } + goto st830; +tr2719: + { + s->buffer[s->buffer_length] = 0; + + if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) { + WARN(ZS_BAD_IPV4); + p--; {goto st268;} + } + } + { + memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH); + rdata_tail += ZS_INET4_ADDR_LENGTH; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st830; +tr2720: + { + s->buffer[s->buffer_length] = 0; + + if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) { + WARN(ZS_BAD_IPV4); + p--; {goto st268;} + } + } + { + memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH); + rdata_tail += ZS_INET4_ADDR_LENGTH; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st830; +tr2730: + { + s->buffer[s->buffer_length] = 0; + + if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) { + WARN(ZS_BAD_IPV4); + p--; {goto st268;} + } + } + { + memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH); + rdata_tail += ZS_INET4_ADDR_LENGTH; + } + { + s->line_counter++; + } + goto st830; +tr2763: + { + s->buffer[s->buffer_length] = 0; + + if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) { + WARN(ZS_BAD_IPV6); + p--; {goto st268;} + } + } + { + memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH); + rdata_tail += ZS_INET6_ADDR_LENGTH; + } + goto st830; +tr2764: + { + s->buffer[s->buffer_length] = 0; + + if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) { + WARN(ZS_BAD_IPV6); + p--; {goto st268;} + } + } + { + memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH); + rdata_tail += ZS_INET6_ADDR_LENGTH; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st830; +tr2765: + { + s->buffer[s->buffer_length] = 0; + + if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) { + WARN(ZS_BAD_IPV6); + p--; {goto st268;} + } + } + { + memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH); + rdata_tail += ZS_INET6_ADDR_LENGTH; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st830; +tr2775: + { + s->buffer[s->buffer_length] = 0; + + if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) { + WARN(ZS_BAD_IPV6); + p--; {goto st268;} + } + } + { + memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH); + rdata_tail += ZS_INET6_ADDR_LENGTH; + } + { + s->line_counter++; + } + goto st830; +st830: + if ( ++p == pe ) + goto _test_eof830; +case 830: + _widec = (*p); + if ( (*p) < 43 ) { + if ( (*p) < 32 ) { + if ( (*p) > 9 ) { + if ( 10 <= (*p) && (*p) <= 10 ) { + _widec = (short)(8832 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + if ( + s->number64 != 0 ) _widec += 1024; + if ( + s->number64 == 0 ) _widec += 2048; + } + } else if ( (*p) >= 9 ) { + _widec = (short)(5760 + ((*p) - -128)); + if ( + s->number64 != 0 ) _widec += 256; + if ( + s->number64 == 0 ) _widec += 512; + } + } else if ( (*p) > 32 ) { + if ( (*p) > 40 ) { + if ( 41 <= (*p) && (*p) <= 41 ) { + _widec = (short)(5760 + ((*p) - -128)); + if ( + s->number64 != 0 ) _widec += 256; + if ( + s->number64 == 0 ) _widec += 512; + } + } else if ( (*p) >= 40 ) { + _widec = (short)(5760 + ((*p) - -128)); + if ( + s->number64 != 0 ) _widec += 256; + if ( + s->number64 == 0 ) _widec += 512; + } + } else { + _widec = (short)(5760 + ((*p) - -128)); + if ( + s->number64 != 0 ) _widec += 256; + if ( + s->number64 == 0 ) _widec += 512; + } + } else if ( (*p) > 43 ) { + if ( (*p) < 59 ) { + if ( (*p) > 47 ) { + if ( 48 <= (*p) && (*p) <= 57 ) { + _widec = (short)(2688 + ((*p) - -128)); + if ( + s->number64 != 0 ) _widec += 256; + } + } else if ( (*p) >= 47 ) { + _widec = (short)(2688 + ((*p) - -128)); + if ( + s->number64 != 0 ) _widec += 256; + } + } else if ( (*p) > 59 ) { + if ( (*p) > 90 ) { + if ( 97 <= (*p) && (*p) <= 122 ) { + _widec = (short)(2688 + ((*p) - -128)); + if ( + s->number64 != 0 ) _widec += 256; + } + } else if ( (*p) >= 65 ) { + _widec = (short)(2688 + ((*p) - -128)); + if ( + s->number64 != 0 ) _widec += 256; + } + } else { + _widec = (short)(8832 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + if ( + s->number64 != 0 ) _widec += 1024; + if ( + s->number64 == 0 ) _widec += 2048; + } + } else { + _widec = (short)(2688 + ((*p) - -128)); + if ( + s->number64 != 0 ) _widec += 256; + } + switch( _widec ) { + case 3115: goto tr2680; + case 6153: goto st825; + case 6176: goto st825; + case 6184: goto tr2658; + case 6185: goto tr2659; + case 6409: goto st828; + case 6432: goto st828; + case 6440: goto tr2661; + case 6441: goto tr2662; + case 6665: goto st830; + case 6688: goto st830; + case 6696: goto tr2664; + case 6697: goto tr2665; + case 9482: goto tr2666; + case 9531: goto tr2666; + case 9738: goto tr2666; + case 9787: goto tr2666; + case 10250: goto tr2667; + case 10299: goto tr2668; + case 10506: goto tr2666; + case 10555: goto tr2666; + case 10762: goto tr2669; + case 10811: goto tr2670; + case 11274: goto tr2671; + case 11323: goto tr2672; + case 11530: goto tr2666; + case 11579: goto tr2666; + case 11786: goto tr2673; + case 11835: goto tr2674; + case 12298: goto tr2675; + case 12347: goto tr2676; + case 12554: goto tr2666; + case 12603: goto tr2666; + case 12810: goto tr2677; + case 12859: goto tr2678; + } + if ( _widec < 3137 ) { + if ( 3119 <= _widec && _widec <= 3129 ) + goto tr2680; + } else if ( _widec > 3162 ) { + if ( 3169 <= _widec && _widec <= 3194 ) + goto tr2680; + } else + goto tr2680; + goto tr2679; +tr2669: + { + s->line_counter++; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1195; +tr2724: + { + s->buffer[s->buffer_length] = 0; + + if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) { + WARN(ZS_BAD_IPV4); + p--; {goto st268;} + } + } + { + memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH); + rdata_tail += ZS_INET4_ADDR_LENGTH; + } + { + s->line_counter++; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1195; +tr2769: + { + s->buffer[s->buffer_length] = 0; + + if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) { + WARN(ZS_BAD_IPV6); + p--; {goto st268;} + } + } + { + memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH); + rdata_tail += ZS_INET6_ADDR_LENGTH; + } + { + s->line_counter++; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1195; +tr2813: + { + rdata_tail += s->dname_tmp_length; + } + { + s->line_counter++; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1195; +st1195: + if ( ++p == pe ) + goto _test_eof1195; +case 1195: + _widec = (*p); + if ( (*p) < 43 ) { + if ( (*p) < 32 ) { + if ( (*p) > 9 ) { + if ( 10 <= (*p) && (*p) <= 10 ) { + _widec = (short)(3200 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + s->number64 != 0 ) _widec += 512; + } + } else if ( (*p) >= 9 ) { + _widec = (short)(2688 + ((*p) - -128)); + if ( + s->number64 != 0 ) _widec += 256; + } + } else if ( (*p) > 32 ) { + if ( (*p) > 40 ) { + if ( 41 <= (*p) && (*p) <= 41 ) { + _widec = (short)(2688 + ((*p) - -128)); + if ( + s->number64 != 0 ) _widec += 256; + } + } else if ( (*p) >= 40 ) { + _widec = (short)(2688 + ((*p) - -128)); + if ( + s->number64 != 0 ) _widec += 256; + } + } else { + _widec = (short)(2688 + ((*p) - -128)); + if ( + s->number64 != 0 ) _widec += 256; + } + } else if ( (*p) > 43 ) { + if ( (*p) < 59 ) { + if ( (*p) > 47 ) { + if ( 48 <= (*p) && (*p) <= 57 ) { + _widec = (short)(2688 + ((*p) - -128)); + if ( + s->number64 != 0 ) _widec += 256; + } + } else if ( (*p) >= 47 ) { + _widec = (short)(2688 + ((*p) - -128)); + if ( + s->number64 != 0 ) _widec += 256; + } + } else if ( (*p) > 59 ) { + if ( (*p) > 90 ) { + if ( 97 <= (*p) && (*p) <= 122 ) { + _widec = (short)(2688 + ((*p) - -128)); + if ( + s->number64 != 0 ) _widec += 256; + } + } else if ( (*p) >= 65 ) { + _widec = (short)(2688 + ((*p) - -128)); + if ( + s->number64 != 0 ) _widec += 256; + } + } else { + _widec = (short)(3200 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + s->number64 != 0 ) _widec += 512; + } + } else { + _widec = (short)(2688 + ((*p) - -128)); + if ( + s->number64 != 0 ) _widec += 256; + } + switch( _widec ) { + case 3081: goto st825; + case 3104: goto st825; + case 3112: goto tr2658; + case 3113: goto tr2659; + case 3115: goto tr2680; + case 4106: goto tr2667; + case 4155: goto tr2668; + } + if ( _widec < 3137 ) { + if ( 3119 <= _widec && _widec <= 3129 ) + goto tr2680; + } else if ( _widec > 3162 ) { + if ( 3169 <= _widec && _widec <= 3194 ) + goto tr2680; + } else + goto tr2680; + goto tr2679; +tr2670: + { + s->buffer_length = 0; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1196; +tr2725: + { + s->buffer[s->buffer_length] = 0; + + if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) { + WARN(ZS_BAD_IPV4); + p--; {goto st268;} + } + } + { + memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH); + rdata_tail += ZS_INET4_ADDR_LENGTH; + } + { + s->buffer_length = 0; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1196; +tr2770: + { + s->buffer[s->buffer_length] = 0; + + if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) { + WARN(ZS_BAD_IPV6); + p--; {goto st268;} + } + } + { + memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH); + rdata_tail += ZS_INET6_ADDR_LENGTH; + } + { + s->buffer_length = 0; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1196; +tr2814: + { + rdata_tail += s->dname_tmp_length; + } + { + s->buffer_length = 0; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1196; +st1196: + if ( ++p == pe ) + goto _test_eof1196; +case 1196: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(3200 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + s->number64 != 0 ) _widec += 512; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(3200 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + s->number64 != 0 ) _widec += 512; + } + } else { + _widec = (short)(3200 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + s->number64 != 0 ) _widec += 512; + } + if ( _widec == 4106 ) + goto tr2682; + if ( 3968 <= _widec && _widec <= 4223 ) + goto tr2681; + goto tr2679; +tr2676: + { + s->buffer_length = 0; + } + goto st831; +tr2685: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st831; +tr2820: + { + rdata_tail += s->dname_tmp_length; + } + { + s->buffer_length = 0; + } + goto st831; +tr2731: + { + s->buffer[s->buffer_length] = 0; + + if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) { + WARN(ZS_BAD_IPV4); + p--; {goto st268;} + } + } + { + memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH); + rdata_tail += ZS_INET4_ADDR_LENGTH; + } + { + s->buffer_length = 0; + } + goto st831; +tr2776: + { + s->buffer[s->buffer_length] = 0; + + if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) { + WARN(ZS_BAD_IPV6); + p--; {goto st268;} + } + } + { + memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH); + rdata_tail += ZS_INET6_ADDR_LENGTH; + } + { + s->buffer_length = 0; + } + goto st831; +st831: + if ( ++p == pe ) + goto _test_eof831; +case 831: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(6784 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + s->number64 != 0 ) _widec += 512; + if ( + s->number64 == 0 ) _widec += 1024; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(6784 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + s->number64 != 0 ) _widec += 512; + if ( + s->number64 == 0 ) _widec += 1024; + } + } else { + _widec = (short)(6784 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + s->number64 != 0 ) _widec += 512; + if ( + s->number64 == 0 ) _widec += 1024; + } + switch( _widec ) { + case 7690: goto tr2682; + case 8202: goto tr2684; + case 8714: goto tr2686; + } + if ( _widec < 8064 ) { + if ( 7552 <= _widec && _widec <= 7807 ) + goto tr2681; + } else if ( _widec > 8319 ) { + if ( 8576 <= _widec && _widec <= 8831 ) + goto tr2685; + } else + goto tr2683; + goto tr2679; +tr2677: + { + s->line_counter++; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1197; +tr2732: + { + s->buffer[s->buffer_length] = 0; + + if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) { + WARN(ZS_BAD_IPV4); + p--; {goto st268;} + } + } + { + memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH); + rdata_tail += ZS_INET4_ADDR_LENGTH; + } + { + s->line_counter++; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1197; +tr2777: + { + s->buffer[s->buffer_length] = 0; + + if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) { + WARN(ZS_BAD_IPV6); + p--; {goto st268;} + } + } + { + memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH); + rdata_tail += ZS_INET6_ADDR_LENGTH; + } + { + s->line_counter++; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1197; +tr2821: + { + rdata_tail += s->dname_tmp_length; + } + { + s->line_counter++; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1197; +st1197: + if ( ++p == pe ) + goto _test_eof1197; +case 1197: + _widec = (*p); + if ( (*p) < 43 ) { + if ( (*p) < 32 ) { + if ( (*p) > 9 ) { + if ( 10 <= (*p) && (*p) <= 10 ) { + _widec = (short)(8832 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + if ( + s->number64 != 0 ) _widec += 1024; + if ( + s->number64 == 0 ) _widec += 2048; + } + } else if ( (*p) >= 9 ) { + _widec = (short)(5760 + ((*p) - -128)); + if ( + s->number64 != 0 ) _widec += 256; + if ( + s->number64 == 0 ) _widec += 512; + } + } else if ( (*p) > 32 ) { + if ( (*p) > 40 ) { + if ( 41 <= (*p) && (*p) <= 41 ) { + _widec = (short)(5760 + ((*p) - -128)); + if ( + s->number64 != 0 ) _widec += 256; + if ( + s->number64 == 0 ) _widec += 512; + } + } else if ( (*p) >= 40 ) { + _widec = (short)(5760 + ((*p) - -128)); + if ( + s->number64 != 0 ) _widec += 256; + if ( + s->number64 == 0 ) _widec += 512; + } + } else { + _widec = (short)(5760 + ((*p) - -128)); + if ( + s->number64 != 0 ) _widec += 256; + if ( + s->number64 == 0 ) _widec += 512; + } + } else if ( (*p) > 43 ) { + if ( (*p) < 59 ) { + if ( (*p) > 47 ) { + if ( 48 <= (*p) && (*p) <= 57 ) { + _widec = (short)(2688 + ((*p) - -128)); + if ( + s->number64 != 0 ) _widec += 256; + } + } else if ( (*p) >= 47 ) { + _widec = (short)(2688 + ((*p) - -128)); + if ( + s->number64 != 0 ) _widec += 256; + } + } else if ( (*p) > 59 ) { + if ( (*p) > 90 ) { + if ( 97 <= (*p) && (*p) <= 122 ) { + _widec = (short)(2688 + ((*p) - -128)); + if ( + s->number64 != 0 ) _widec += 256; + } + } else if ( (*p) >= 65 ) { + _widec = (short)(2688 + ((*p) - -128)); + if ( + s->number64 != 0 ) _widec += 256; + } + } else { + _widec = (short)(8832 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + if ( + s->number64 != 0 ) _widec += 1024; + if ( + s->number64 == 0 ) _widec += 2048; + } + } else { + _widec = (short)(2688 + ((*p) - -128)); + if ( + s->number64 != 0 ) _widec += 256; + } + switch( _widec ) { + case 3115: goto tr2680; + case 6153: goto st825; + case 6176: goto st825; + case 6184: goto tr2658; + case 6185: goto tr2659; + case 6409: goto st828; + case 6432: goto st828; + case 6440: goto tr2661; + case 6441: goto tr2662; + case 6665: goto st830; + case 6688: goto st830; + case 6696: goto tr2664; + case 6697: goto tr2665; + case 9482: goto tr2666; + case 9531: goto tr2666; + case 9738: goto tr2666; + case 9787: goto tr2666; + case 10250: goto tr2667; + case 10299: goto tr2668; + case 10506: goto tr2666; + case 10555: goto tr2666; + case 10762: goto tr2669; + case 10811: goto tr2670; + case 11274: goto tr2671; + case 11323: goto tr2672; + case 11530: goto tr2666; + case 11579: goto tr2666; + case 11786: goto tr2673; + case 11835: goto tr2674; + case 12298: goto tr2675; + case 12347: goto tr2676; + case 12554: goto tr2666; + case 12603: goto tr2666; + case 12810: goto tr2677; + case 12859: goto tr2678; + } + if ( _widec < 3137 ) { + if ( 3119 <= _widec && _widec <= 3129 ) + goto tr2680; + } else if ( _widec > 3162 ) { + if ( 3169 <= _widec && _widec <= 3194 ) + goto tr2680; + } else + goto tr2680; + goto tr2679; +tr2678: + { + s->buffer_length = 0; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1198; +tr2733: + { + s->buffer[s->buffer_length] = 0; + + if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) { + WARN(ZS_BAD_IPV4); + p--; {goto st268;} + } + } + { + memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH); + rdata_tail += ZS_INET4_ADDR_LENGTH; + } + { + s->buffer_length = 0; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1198; +tr2778: + { + s->buffer[s->buffer_length] = 0; + + if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) { + WARN(ZS_BAD_IPV6); + p--; {goto st268;} + } + } + { + memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH); + rdata_tail += ZS_INET6_ADDR_LENGTH; + } + { + s->buffer_length = 0; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1198; +tr2822: + { + rdata_tail += s->dname_tmp_length; + } + { + s->buffer_length = 0; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1198; +st1198: + if ( ++p == pe ) + goto _test_eof1198; +case 1198: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(6784 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + s->number64 != 0 ) _widec += 512; + if ( + s->number64 == 0 ) _widec += 1024; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(6784 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + s->number64 != 0 ) _widec += 512; + if ( + s->number64 == 0 ) _widec += 1024; + } + } else { + _widec = (short)(6784 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + s->number64 != 0 ) _widec += 512; + if ( + s->number64 == 0 ) _widec += 1024; + } + switch( _widec ) { + case 7690: goto tr2682; + case 8202: goto tr2684; + case 8714: goto tr2686; + } + if ( _widec < 8064 ) { + if ( 7552 <= _widec && _widec <= 7807 ) + goto tr2681; + } else if ( _widec > 8319 ) { + if ( 8576 <= _widec && _widec <= 8831 ) + goto tr2685; + } else + goto tr2683; + goto tr2679; +tr2655: + { + s->buffer_length = 0; + } + goto st832; +tr2687: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st832; +tr2649: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st832; +st832: + if ( ++p == pe ) + goto _test_eof832; +case 832: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2688; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2687; + goto tr2627; +tr2641: + { + s->buffer_length = 0; + } + goto st833; +tr2689: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st833; +st833: + if ( ++p == pe ) + goto _test_eof833; +case 833: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2690; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2689; + goto tr2627; +tr2632: + { + *(rdata_tail++) = 1; + } + goto st834; +st834: + if ( ++p == pe ) + goto _test_eof834; +case 834: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st835; + case 32: goto st835; + case 40: goto tr2692; + case 41: goto tr2693; + case 1034: goto tr2694; + case 1083: goto tr2695; + } + goto tr2627; +tr2692: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st835; +tr2693: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st835; +tr2694: + { + s->line_counter++; + } + goto st835; +tr2737: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st835; +st835: + if ( ++p == pe ) + goto _test_eof835; +case 835: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st835; + case 32: goto st835; + case 40: goto tr2692; + case 41: goto tr2693; + case 1034: goto tr2694; + case 1083: goto tr2695; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2696; + goto tr2642; +tr2696: + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st836; +tr2700: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st836; +st836: + if ( ++p == pe ) + goto _test_eof836; +case 836: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr2697; + case 32: goto tr2697; + case 40: goto tr2698; + case 41: goto tr2699; + case 1034: goto tr2701; + case 1083: goto tr2702; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2700; + goto tr2642; +tr2705: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st837; +tr2706: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st837; +tr2708: + { + s->line_counter++; + } + goto st837; +tr2735: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st837; +tr2697: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + goto st837; +tr2698: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st837; +tr2699: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st837; +tr2701: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st837; +st837: + if ( ++p == pe ) + goto _test_eof837; +case 837: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st837; + case 32: goto st837; + case 40: goto tr2705; + case 41: goto tr2706; + case 46: goto tr2707; + case 1034: goto tr2708; + case 1083: goto tr2709; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2707; + goto tr2703; +tr2707: + { + s->buffer_length = 0; + } + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + goto st838; +tr2711: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + goto st838; +st838: + if ( ++p == pe ) + goto _test_eof838; +case 838: + _widec = (*p); + if ( (*p) < 32 ) { + if ( (*p) > 9 ) { + if ( 10 <= (*p) && (*p) <= 10 ) { + _widec = (short)(8832 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + if ( + s->number64 != 0 ) _widec += 1024; + if ( + s->number64 == 0 ) _widec += 2048; + } + } else if ( (*p) >= 9 ) { + _widec = (short)(5760 + ((*p) - -128)); + if ( + s->number64 != 0 ) _widec += 256; + if ( + s->number64 == 0 ) _widec += 512; + } + } else if ( (*p) > 32 ) { + if ( (*p) < 41 ) { + if ( 40 <= (*p) && (*p) <= 40 ) { + _widec = (short)(5760 + ((*p) - -128)); + if ( + s->number64 != 0 ) _widec += 256; + if ( + s->number64 == 0 ) _widec += 512; + } + } else if ( (*p) > 41 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(8832 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + if ( + s->number64 != 0 ) _widec += 1024; + if ( + s->number64 == 0 ) _widec += 2048; + } + } else { + _widec = (short)(5760 + ((*p) - -128)); + if ( + s->number64 != 0 ) _widec += 256; + if ( + s->number64 == 0 ) _widec += 512; + } + } else { + _widec = (short)(5760 + ((*p) - -128)); + if ( + s->number64 != 0 ) _widec += 256; + if ( + s->number64 == 0 ) _widec += 512; + } + switch( _widec ) { + case 46: goto tr2711; + case 6153: goto tr2712; + case 6176: goto tr2712; + case 6184: goto tr2713; + case 6185: goto tr2714; + case 6409: goto tr2715; + case 6432: goto tr2715; + case 6440: goto tr2716; + case 6441: goto tr2717; + case 6665: goto tr2718; + case 6688: goto tr2718; + case 6696: goto tr2719; + case 6697: goto tr2720; + case 9482: goto tr2721; + case 9531: goto tr2721; + case 9738: goto tr2721; + case 9787: goto tr2721; + case 10250: goto tr2722; + case 10299: goto tr2723; + case 10506: goto tr2721; + case 10555: goto tr2721; + case 10762: goto tr2724; + case 10811: goto tr2725; + case 11274: goto tr2726; + case 11323: goto tr2727; + case 11530: goto tr2721; + case 11579: goto tr2721; + case 11786: goto tr2728; + case 11835: goto tr2729; + case 12298: goto tr2730; + case 12347: goto tr2731; + case 12554: goto tr2721; + case 12603: goto tr2721; + case 12810: goto tr2732; + case 12859: goto tr2733; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2711; + goto tr2710; +tr2709: + { + s->buffer_length = 0; + } + goto st839; +tr2734: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st839; +tr2702: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st839; +st839: + if ( ++p == pe ) + goto _test_eof839; +case 839: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2735; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2734; + goto tr2627; +tr2695: + { + s->buffer_length = 0; + } + goto st840; +tr2736: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st840; +st840: + if ( ++p == pe ) + goto _test_eof840; +case 840: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2737; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2736; + goto tr2627; +tr2633: + { + *(rdata_tail++) = 2; + } + goto st841; +st841: + if ( ++p == pe ) + goto _test_eof841; +case 841: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st842; + case 32: goto st842; + case 40: goto tr2739; + case 41: goto tr2740; + case 1034: goto tr2741; + case 1083: goto tr2742; + } + goto tr2627; +tr2739: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st842; +tr2740: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st842; +tr2741: + { + s->line_counter++; + } + goto st842; +tr2782: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st842; +st842: + if ( ++p == pe ) + goto _test_eof842; +case 842: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st842; + case 32: goto st842; + case 40: goto tr2739; + case 41: goto tr2740; + case 1034: goto tr2741; + case 1083: goto tr2742; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2743; + goto tr2642; +tr2743: + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st843; +tr2747: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st843; +st843: + if ( ++p == pe ) + goto _test_eof843; +case 843: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr2744; + case 32: goto tr2744; + case 40: goto tr2745; + case 41: goto tr2746; + case 1034: goto tr2748; + case 1083: goto tr2749; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2747; + goto tr2642; +tr2751: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st844; +tr2752: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st844; +tr2754: + { + s->line_counter++; + } + goto st844; +tr2780: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st844; +tr2744: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + goto st844; +tr2745: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st844; +tr2746: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st844; +tr2748: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st844; +st844: + if ( ++p == pe ) + goto _test_eof844; +case 844: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st844; + case 32: goto st844; + case 40: goto tr2751; + case 41: goto tr2752; + case 46: goto tr2753; + case 1034: goto tr2754; + case 1083: goto tr2755; + } + if ( _widec < 65 ) { + if ( 48 <= _widec && _widec <= 58 ) + goto tr2753; + } else if ( _widec > 70 ) { + if ( 97 <= _widec && _widec <= 102 ) + goto tr2753; + } else + goto tr2753; + goto tr2703; +tr2753: + { + s->buffer_length = 0; + } + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + goto st845; +tr2756: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + goto st845; +st845: + if ( ++p == pe ) + goto _test_eof845; +case 845: + _widec = (*p); + if ( (*p) < 32 ) { + if ( (*p) > 9 ) { + if ( 10 <= (*p) && (*p) <= 10 ) { + _widec = (short)(8832 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + if ( + s->number64 != 0 ) _widec += 1024; + if ( + s->number64 == 0 ) _widec += 2048; + } + } else if ( (*p) >= 9 ) { + _widec = (short)(5760 + ((*p) - -128)); + if ( + s->number64 != 0 ) _widec += 256; + if ( + s->number64 == 0 ) _widec += 512; + } + } else if ( (*p) > 32 ) { + if ( (*p) < 41 ) { + if ( 40 <= (*p) && (*p) <= 40 ) { + _widec = (short)(5760 + ((*p) - -128)); + if ( + s->number64 != 0 ) _widec += 256; + if ( + s->number64 == 0 ) _widec += 512; + } + } else if ( (*p) > 41 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(8832 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + if ( + s->number64 != 0 ) _widec += 1024; + if ( + s->number64 == 0 ) _widec += 2048; + } + } else { + _widec = (short)(5760 + ((*p) - -128)); + if ( + s->number64 != 0 ) _widec += 256; + if ( + s->number64 == 0 ) _widec += 512; + } + } else { + _widec = (short)(5760 + ((*p) - -128)); + if ( + s->number64 != 0 ) _widec += 256; + if ( + s->number64 == 0 ) _widec += 512; + } + switch( _widec ) { + case 46: goto tr2756; + case 6153: goto tr2757; + case 6176: goto tr2757; + case 6184: goto tr2758; + case 6185: goto tr2759; + case 6409: goto tr2760; + case 6432: goto tr2760; + case 6440: goto tr2761; + case 6441: goto tr2762; + case 6665: goto tr2763; + case 6688: goto tr2763; + case 6696: goto tr2764; + case 6697: goto tr2765; + case 9482: goto tr2766; + case 9531: goto tr2766; + case 9738: goto tr2766; + case 9787: goto tr2766; + case 10250: goto tr2767; + case 10299: goto tr2768; + case 10506: goto tr2766; + case 10555: goto tr2766; + case 10762: goto tr2769; + case 10811: goto tr2770; + case 11274: goto tr2771; + case 11323: goto tr2772; + case 11530: goto tr2766; + case 11579: goto tr2766; + case 11786: goto tr2773; + case 11835: goto tr2774; + case 12298: goto tr2775; + case 12347: goto tr2776; + case 12554: goto tr2766; + case 12603: goto tr2766; + case 12810: goto tr2777; + case 12859: goto tr2778; + } + if ( _widec < 65 ) { + if ( 48 <= _widec && _widec <= 58 ) + goto tr2756; + } else if ( _widec > 70 ) { + if ( 97 <= _widec && _widec <= 102 ) + goto tr2756; + } else + goto tr2756; + goto tr2710; +tr2755: + { + s->buffer_length = 0; + } + goto st846; +tr2779: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st846; +tr2749: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st846; +st846: + if ( ++p == pe ) + goto _test_eof846; +case 846: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2780; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2779; + goto tr2627; +tr2742: + { + s->buffer_length = 0; + } + goto st847; +tr2781: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st847; +st847: + if ( ++p == pe ) + goto _test_eof847; +case 847: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2782; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2781; + goto tr2627; +tr2634: + { + *(rdata_tail++) = 3; + } + goto st848; +st848: + if ( ++p == pe ) + goto _test_eof848; +case 848: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st849; + case 32: goto st849; + case 40: goto tr2784; + case 41: goto tr2785; + case 1034: goto tr2786; + case 1083: goto tr2787; + } + goto tr2627; +tr2784: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st849; +tr2785: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st849; +tr2786: + { + s->line_counter++; + } + goto st849; +tr2826: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st849; +st849: + if ( ++p == pe ) + goto _test_eof849; +case 849: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st849; + case 32: goto st849; + case 40: goto tr2784; + case 41: goto tr2785; + case 1034: goto tr2786; + case 1083: goto tr2787; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2788; + goto tr2642; +tr2788: + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st850; +tr2792: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st850; +st850: + if ( ++p == pe ) + goto _test_eof850; +case 850: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr2789; + case 32: goto tr2789; + case 40: goto tr2790; + case 41: goto tr2791; + case 1034: goto tr2793; + case 1083: goto tr2794; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2792; + goto tr2642; +tr2796: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st851; +tr2797: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st851; +tr2799: + { + s->line_counter++; + } + goto st851; +tr2824: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st851; +tr2789: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + goto st851; +tr2790: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st851; +tr2791: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st851; +tr2793: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st851; +st851: + if ( ++p == pe ) + goto _test_eof851; +case 851: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st851; + case 32: goto st851; + case 40: goto tr2796; + case 41: goto tr2797; + case 42: goto tr2798; + case 92: goto tr2798; + case 95: goto tr2798; + case 1034: goto tr2799; + case 1083: goto tr2800; + } + if ( _widec < 64 ) { + if ( 45 <= _widec && _widec <= 57 ) + goto tr2798; + } else if ( _widec > 90 ) { + if ( 97 <= _widec && _widec <= 122 ) + goto tr2798; + } else + goto tr2798; + goto tr2627; +tr2798: + { + s->dname = rdata_tail; + } + { p--; {stack[top++] = 852;goto st270;} } + goto st852; +st852: + if ( ++p == pe ) + goto _test_eof852; +case 852: + _widec = (*p); + if ( (*p) < 32 ) { + if ( (*p) > 9 ) { + if ( 10 <= (*p) && (*p) <= 10 ) { + _widec = (short)(8832 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + if ( + s->number64 != 0 ) _widec += 1024; + if ( + s->number64 == 0 ) _widec += 2048; + } + } else if ( (*p) >= 9 ) { + _widec = (short)(5760 + ((*p) - -128)); + if ( + s->number64 != 0 ) _widec += 256; + if ( + s->number64 == 0 ) _widec += 512; + } + } else if ( (*p) > 32 ) { + if ( (*p) < 41 ) { + if ( 40 <= (*p) && (*p) <= 40 ) { + _widec = (short)(5760 + ((*p) - -128)); + if ( + s->number64 != 0 ) _widec += 256; + if ( + s->number64 == 0 ) _widec += 512; + } + } else if ( (*p) > 41 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(8832 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + if ( + s->number64 != 0 ) _widec += 1024; + if ( + s->number64 == 0 ) _widec += 2048; + } + } else { + _widec = (short)(5760 + ((*p) - -128)); + if ( + s->number64 != 0 ) _widec += 256; + if ( + s->number64 == 0 ) _widec += 512; + } + } else { + _widec = (short)(5760 + ((*p) - -128)); + if ( + s->number64 != 0 ) _widec += 256; + if ( + s->number64 == 0 ) _widec += 512; + } + switch( _widec ) { + case 6153: goto tr2801; + case 6176: goto tr2801; + case 6184: goto tr2802; + case 6185: goto tr2803; + case 6409: goto tr2804; + case 6432: goto tr2804; + case 6440: goto tr2805; + case 6441: goto tr2806; + case 6665: goto tr2807; + case 6688: goto tr2807; + case 6696: goto tr2808; + case 6697: goto tr2809; + case 9482: goto tr2810; + case 9531: goto tr2810; + case 9738: goto tr2810; + case 9787: goto tr2810; + case 10250: goto tr2811; + case 10299: goto tr2812; + case 10506: goto tr2810; + case 10555: goto tr2810; + case 10762: goto tr2813; + case 10811: goto tr2814; + case 11274: goto tr2815; + case 11323: goto tr2816; + case 11530: goto tr2810; + case 11579: goto tr2810; + case 11786: goto tr2817; + case 11835: goto tr2818; + case 12298: goto tr2819; + case 12347: goto tr2820; + case 12554: goto tr2810; + case 12603: goto tr2810; + case 12810: goto tr2821; + case 12859: goto tr2822; + } + goto tr2656; +tr2800: + { + s->buffer_length = 0; + } + goto st853; +tr2823: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st853; +tr2794: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st853; +st853: + if ( ++p == pe ) + goto _test_eof853; +case 853: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2824; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2823; + goto tr2627; +tr2787: + { + s->buffer_length = 0; + } + goto st854; +tr2825: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st854; +st854: + if ( ++p == pe ) + goto _test_eof854; +case 854: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2826; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2825; + goto tr2627; +tr2636: + { + s->buffer_length = 0; + } + goto st855; +tr2827: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st855; +tr2626: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st855; +st855: + if ( ++p == pe ) + goto _test_eof855; +case 855: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2828; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2827; + goto tr71; +st856: + if ( ++p == pe ) + goto _test_eof856; +case 856: + switch( (*p) ) { + case 65: goto st857; + case 67: goto st891; + case 68: goto st908; + case 69: goto st922; + case 72: goto st929; + case 73: goto st934; + case 75: goto st942; + case 76: goto st946; + case 77: goto st954; + case 78: goto st960; + case 80: goto st976; + case 82: goto st979; + case 83: goto st986; + case 84: goto st997; + case 85: goto st1007; + case 97: goto st857; + case 99: goto st891; + case 100: goto st908; + case 101: goto st922; + case 104: goto st929; + case 105: goto st934; + case 107: goto st942; + case 108: goto st946; + case 109: goto st954; + case 110: goto st960; + case 112: goto st976; + case 114: goto st979; + case 115: goto st986; + case 116: goto st997; + case 117: goto st1007; + } + goto tr2829; +st857: + if ( ++p == pe ) + goto _test_eof857; +case 857: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr2845; + case 32: goto tr2845; + case 40: goto tr2846; + case 41: goto tr2847; + case 65: goto st882; + case 70: goto st885; + case 80: goto st889; + case 97: goto st882; + case 102: goto st885; + case 112: goto st889; + case 1034: goto tr2851; + case 1083: goto tr2852; + } + goto tr2829; +tr2854: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st858; +tr2855: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st858; +tr2857: + { + s->line_counter++; + } + goto st858; +tr2953: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st858; +tr3264: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + goto st858; +tr3265: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st858; +tr3266: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st858; +tr3268: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st858; +tr2845: + { type_num(KNOT_RRTYPE_A, &rdata_tail); } + goto st858; +tr2846: + { type_num(KNOT_RRTYPE_A, &rdata_tail); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st858; +tr2847: + { type_num(KNOT_RRTYPE_A, &rdata_tail); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st858; +tr2851: + { type_num(KNOT_RRTYPE_A, &rdata_tail); } + { + s->line_counter++; + } + goto st858; +tr2956: + { type_num(KNOT_RRTYPE_AAAA, &rdata_tail); } + goto st858; +tr2957: + { type_num(KNOT_RRTYPE_AAAA, &rdata_tail); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st858; +tr2958: + { type_num(KNOT_RRTYPE_AAAA, &rdata_tail); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st858; +tr2959: + { type_num(KNOT_RRTYPE_AAAA, &rdata_tail); } + { + s->line_counter++; + } + goto st858; +tr2964: + { type_num(KNOT_RRTYPE_AFSDB, &rdata_tail); } + goto st858; +tr2965: + { type_num(KNOT_RRTYPE_AFSDB, &rdata_tail); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st858; +tr2966: + { type_num(KNOT_RRTYPE_AFSDB, &rdata_tail); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st858; +tr2967: + { type_num(KNOT_RRTYPE_AFSDB, &rdata_tail); } + { + s->line_counter++; + } + goto st858; +tr2970: + { type_num(KNOT_RRTYPE_APL, &rdata_tail); } + goto st858; +tr2971: + { type_num(KNOT_RRTYPE_APL, &rdata_tail); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st858; +tr2972: + { type_num(KNOT_RRTYPE_APL, &rdata_tail); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st858; +tr2973: + { type_num(KNOT_RRTYPE_APL, &rdata_tail); } + { + s->line_counter++; + } + goto st858; +tr2980: + { type_num(KNOT_RRTYPE_CAA, &rdata_tail); } + goto st858; +tr2981: + { type_num(KNOT_RRTYPE_CAA, &rdata_tail); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st858; +tr2982: + { type_num(KNOT_RRTYPE_CAA, &rdata_tail); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st858; +tr2983: + { type_num(KNOT_RRTYPE_CAA, &rdata_tail); } + { + s->line_counter++; + } + goto st858; +tr2991: + { type_num(KNOT_RRTYPE_CDNSKEY, &rdata_tail); } + goto st858; +tr2992: + { type_num(KNOT_RRTYPE_CDNSKEY, &rdata_tail); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st858; +tr2993: + { type_num(KNOT_RRTYPE_CDNSKEY, &rdata_tail); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st858; +tr2994: + { type_num(KNOT_RRTYPE_CDNSKEY, &rdata_tail); } + { + s->line_counter++; + } + goto st858; +tr2996: + { type_num(KNOT_RRTYPE_CDS, &rdata_tail); } + goto st858; +tr2997: + { type_num(KNOT_RRTYPE_CDS, &rdata_tail); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st858; +tr2998: + { type_num(KNOT_RRTYPE_CDS, &rdata_tail); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st858; +tr2999: + { type_num(KNOT_RRTYPE_CDS, &rdata_tail); } + { + s->line_counter++; + } + goto st858; +tr3003: + { type_num(KNOT_RRTYPE_CERT, &rdata_tail); } + goto st858; +tr3004: + { type_num(KNOT_RRTYPE_CERT, &rdata_tail); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st858; +tr3005: + { type_num(KNOT_RRTYPE_CERT, &rdata_tail); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st858; +tr3006: + { type_num(KNOT_RRTYPE_CERT, &rdata_tail); } + { + s->line_counter++; + } + goto st858; +tr3011: + { type_num(KNOT_RRTYPE_CNAME, &rdata_tail); } + goto st858; +tr3012: + { type_num(KNOT_RRTYPE_CNAME, &rdata_tail); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st858; +tr3013: + { type_num(KNOT_RRTYPE_CNAME, &rdata_tail); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st858; +tr3014: + { type_num(KNOT_RRTYPE_CNAME, &rdata_tail); } + { + s->line_counter++; + } + goto st858; +tr3022: + { type_num(KNOT_RRTYPE_DHCID, &rdata_tail); } + goto st858; +tr3023: + { type_num(KNOT_RRTYPE_DHCID, &rdata_tail); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st858; +tr3024: + { type_num(KNOT_RRTYPE_DHCID, &rdata_tail); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st858; +tr3025: + { type_num(KNOT_RRTYPE_DHCID, &rdata_tail); } + { + s->line_counter++; + } + goto st858; +tr3031: + { type_num(KNOT_RRTYPE_DNAME, &rdata_tail); } + goto st858; +tr3032: + { type_num(KNOT_RRTYPE_DNAME, &rdata_tail); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st858; +tr3033: + { type_num(KNOT_RRTYPE_DNAME, &rdata_tail); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st858; +tr3034: + { type_num(KNOT_RRTYPE_DNAME, &rdata_tail); } + { + s->line_counter++; + } + goto st858; +tr3039: + { type_num(KNOT_RRTYPE_DNSKEY, &rdata_tail); } + goto st858; +tr3040: + { type_num(KNOT_RRTYPE_DNSKEY, &rdata_tail); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st858; +tr3041: + { type_num(KNOT_RRTYPE_DNSKEY, &rdata_tail); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st858; +tr3042: + { type_num(KNOT_RRTYPE_DNSKEY, &rdata_tail); } + { + s->line_counter++; + } + goto st858; +tr3044: + { type_num(KNOT_RRTYPE_DS, &rdata_tail); } + goto st858; +tr3045: + { type_num(KNOT_RRTYPE_DS, &rdata_tail); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st858; +tr3046: + { type_num(KNOT_RRTYPE_DS, &rdata_tail); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st858; +tr3047: + { type_num(KNOT_RRTYPE_DS, &rdata_tail); } + { + s->line_counter++; + } + goto st858; +tr3054: + { type_num(KNOT_RRTYPE_EUI48, &rdata_tail); } + goto st858; +tr3055: + { type_num(KNOT_RRTYPE_EUI48, &rdata_tail); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st858; +tr3056: + { type_num(KNOT_RRTYPE_EUI48, &rdata_tail); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st858; +tr3057: + { type_num(KNOT_RRTYPE_EUI48, &rdata_tail); } + { + s->line_counter++; + } + goto st858; +tr3060: + { type_num(KNOT_RRTYPE_EUI64, &rdata_tail); } + goto st858; +tr3061: + { type_num(KNOT_RRTYPE_EUI64, &rdata_tail); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st858; +tr3062: + { type_num(KNOT_RRTYPE_EUI64, &rdata_tail); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st858; +tr3063: + { type_num(KNOT_RRTYPE_EUI64, &rdata_tail); } + { + s->line_counter++; + } + goto st858; +tr3069: + { type_num(KNOT_RRTYPE_HINFO, &rdata_tail); } + goto st858; +tr3070: + { type_num(KNOT_RRTYPE_HINFO, &rdata_tail); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st858; +tr3071: + { type_num(KNOT_RRTYPE_HINFO, &rdata_tail); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st858; +tr3072: + { type_num(KNOT_RRTYPE_HINFO, &rdata_tail); } + { + s->line_counter++; + } + goto st858; +tr3081: + { type_num(KNOT_RRTYPE_IPSECKEY, &rdata_tail); } + goto st858; +tr3082: + { type_num(KNOT_RRTYPE_IPSECKEY, &rdata_tail); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st858; +tr3083: + { type_num(KNOT_RRTYPE_IPSECKEY, &rdata_tail); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st858; +tr3084: + { type_num(KNOT_RRTYPE_IPSECKEY, &rdata_tail); } + { + s->line_counter++; + } + goto st858; +tr3089: + { type_num(KNOT_RRTYPE_KEY, &rdata_tail); } + goto st858; +tr3090: + { type_num(KNOT_RRTYPE_KEY, &rdata_tail); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st858; +tr3091: + { type_num(KNOT_RRTYPE_KEY, &rdata_tail); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st858; +tr3092: + { type_num(KNOT_RRTYPE_KEY, &rdata_tail); } + { + s->line_counter++; + } + goto st858; +tr3094: + { type_num(KNOT_RRTYPE_KX, &rdata_tail); } + goto st858; +tr3095: + { type_num(KNOT_RRTYPE_KX, &rdata_tail); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st858; +tr3096: + { type_num(KNOT_RRTYPE_KX, &rdata_tail); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st858; +tr3097: + { type_num(KNOT_RRTYPE_KX, &rdata_tail); } + { + s->line_counter++; + } + goto st858; +tr3104: + { type_num(KNOT_RRTYPE_L32, &rdata_tail); } + goto st858; +tr3105: + { type_num(KNOT_RRTYPE_L32, &rdata_tail); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st858; +tr3106: + { type_num(KNOT_RRTYPE_L32, &rdata_tail); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st858; +tr3107: + { type_num(KNOT_RRTYPE_L32, &rdata_tail); } + { + s->line_counter++; + } + goto st858; +tr3110: + { type_num(KNOT_RRTYPE_L64, &rdata_tail); } + goto st858; +tr3111: + { type_num(KNOT_RRTYPE_L64, &rdata_tail); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st858; +tr3112: + { type_num(KNOT_RRTYPE_L64, &rdata_tail); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st858; +tr3113: + { type_num(KNOT_RRTYPE_L64, &rdata_tail); } + { + s->line_counter++; + } + goto st858; +tr3116: + { type_num(KNOT_RRTYPE_LOC, &rdata_tail); } + goto st858; +tr3117: + { type_num(KNOT_RRTYPE_LOC, &rdata_tail); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st858; +tr3118: + { type_num(KNOT_RRTYPE_LOC, &rdata_tail); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st858; +tr3119: + { type_num(KNOT_RRTYPE_LOC, &rdata_tail); } + { + s->line_counter++; + } + goto st858; +tr3121: + { type_num(KNOT_RRTYPE_LP, &rdata_tail); } + goto st858; +tr3122: + { type_num(KNOT_RRTYPE_LP, &rdata_tail); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st858; +tr3123: + { type_num(KNOT_RRTYPE_LP, &rdata_tail); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st858; +tr3124: + { type_num(KNOT_RRTYPE_LP, &rdata_tail); } + { + s->line_counter++; + } + goto st858; +tr3131: + { type_num(KNOT_RRTYPE_MINFO, &rdata_tail); } + goto st858; +tr3132: + { type_num(KNOT_RRTYPE_MINFO, &rdata_tail); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st858; +tr3133: + { type_num(KNOT_RRTYPE_MINFO, &rdata_tail); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st858; +tr3134: + { type_num(KNOT_RRTYPE_MINFO, &rdata_tail); } + { + s->line_counter++; + } + goto st858; +tr3136: + { type_num(KNOT_RRTYPE_MX, &rdata_tail); } + goto st858; +tr3137: + { type_num(KNOT_RRTYPE_MX, &rdata_tail); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st858; +tr3138: + { type_num(KNOT_RRTYPE_MX, &rdata_tail); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st858; +tr3139: + { type_num(KNOT_RRTYPE_MX, &rdata_tail); } + { + s->line_counter++; + } + goto st858; +tr3147: + { type_num(KNOT_RRTYPE_NAPTR, &rdata_tail); } + goto st858; +tr3148: + { type_num(KNOT_RRTYPE_NAPTR, &rdata_tail); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st858; +tr3149: + { type_num(KNOT_RRTYPE_NAPTR, &rdata_tail); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st858; +tr3150: + { type_num(KNOT_RRTYPE_NAPTR, &rdata_tail); } + { + s->line_counter++; + } + goto st858; +tr3153: + { type_num(KNOT_RRTYPE_NID, &rdata_tail); } + goto st858; +tr3154: + { type_num(KNOT_RRTYPE_NID, &rdata_tail); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st858; +tr3155: + { type_num(KNOT_RRTYPE_NID, &rdata_tail); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st858; +tr3156: + { type_num(KNOT_RRTYPE_NID, &rdata_tail); } + { + s->line_counter++; + } + goto st858; +tr3158: + { type_num(KNOT_RRTYPE_NS, &rdata_tail); } + goto st858; +tr3159: + { type_num(KNOT_RRTYPE_NS, &rdata_tail); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st858; +tr3160: + { type_num(KNOT_RRTYPE_NS, &rdata_tail); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st858; +tr3162: + { type_num(KNOT_RRTYPE_NS, &rdata_tail); } + { + s->line_counter++; + } + goto st858; +tr3165: + { type_num(KNOT_RRTYPE_NSEC, &rdata_tail); } + goto st858; +tr3166: + { type_num(KNOT_RRTYPE_NSEC, &rdata_tail); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st858; +tr3167: + { type_num(KNOT_RRTYPE_NSEC, &rdata_tail); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st858; +tr3169: + { type_num(KNOT_RRTYPE_NSEC, &rdata_tail); } + { + s->line_counter++; + } + goto st858; +tr3171: + { type_num(KNOT_RRTYPE_NSEC3, &rdata_tail); } + goto st858; +tr3172: + { type_num(KNOT_RRTYPE_NSEC3, &rdata_tail); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st858; +tr3173: + { type_num(KNOT_RRTYPE_NSEC3, &rdata_tail); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st858; +tr3175: + { type_num(KNOT_RRTYPE_NSEC3, &rdata_tail); } + { + s->line_counter++; + } + goto st858; +tr3181: + { type_num(KNOT_RRTYPE_NSEC3PARAM, &rdata_tail); } + goto st858; +tr3182: + { type_num(KNOT_RRTYPE_NSEC3PARAM, &rdata_tail); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st858; +tr3183: + { type_num(KNOT_RRTYPE_NSEC3PARAM, &rdata_tail); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st858; +tr3184: + { type_num(KNOT_RRTYPE_NSEC3PARAM, &rdata_tail); } + { + s->line_counter++; + } + goto st858; +tr3188: + { type_num(KNOT_RRTYPE_PTR, &rdata_tail); } + goto st858; +tr3189: + { type_num(KNOT_RRTYPE_PTR, &rdata_tail); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st858; +tr3190: + { type_num(KNOT_RRTYPE_PTR, &rdata_tail); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st858; +tr3191: + { type_num(KNOT_RRTYPE_PTR, &rdata_tail); } + { + s->line_counter++; + } + goto st858; +tr3196: + { type_num(KNOT_RRTYPE_RP, &rdata_tail); } + goto st858; +tr3197: + { type_num(KNOT_RRTYPE_RP, &rdata_tail); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st858; +tr3198: + { type_num(KNOT_RRTYPE_RP, &rdata_tail); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st858; +tr3199: + { type_num(KNOT_RRTYPE_RP, &rdata_tail); } + { + s->line_counter++; + } + goto st858; +tr3204: + { type_num(KNOT_RRTYPE_RRSIG, &rdata_tail); } + goto st858; +tr3205: + { type_num(KNOT_RRTYPE_RRSIG, &rdata_tail); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st858; +tr3206: + { type_num(KNOT_RRTYPE_RRSIG, &rdata_tail); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st858; +tr3207: + { type_num(KNOT_RRTYPE_RRSIG, &rdata_tail); } + { + s->line_counter++; + } + goto st858; +tr3209: + { type_num(KNOT_RRTYPE_RT, &rdata_tail); } + goto st858; +tr3210: + { type_num(KNOT_RRTYPE_RT, &rdata_tail); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st858; +tr3211: + { type_num(KNOT_RRTYPE_RT, &rdata_tail); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st858; +tr3212: + { type_num(KNOT_RRTYPE_RT, &rdata_tail); } + { + s->line_counter++; + } + goto st858; +tr3219: + { type_num(KNOT_RRTYPE_SOA, &rdata_tail); } + goto st858; +tr3220: + { type_num(KNOT_RRTYPE_SOA, &rdata_tail); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st858; +tr3221: + { type_num(KNOT_RRTYPE_SOA, &rdata_tail); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st858; +tr3222: + { type_num(KNOT_RRTYPE_SOA, &rdata_tail); } + { + s->line_counter++; + } + goto st858; +tr3225: + { type_num(KNOT_RRTYPE_SPF, &rdata_tail); } + goto st858; +tr3226: + { type_num(KNOT_RRTYPE_SPF, &rdata_tail); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st858; +tr3227: + { type_num(KNOT_RRTYPE_SPF, &rdata_tail); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st858; +tr3228: + { type_num(KNOT_RRTYPE_SPF, &rdata_tail); } + { + s->line_counter++; + } + goto st858; +tr3231: + { type_num(KNOT_RRTYPE_SRV, &rdata_tail); } + goto st858; +tr3232: + { type_num(KNOT_RRTYPE_SRV, &rdata_tail); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st858; +tr3233: + { type_num(KNOT_RRTYPE_SRV, &rdata_tail); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st858; +tr3234: + { type_num(KNOT_RRTYPE_SRV, &rdata_tail); } + { + s->line_counter++; + } + goto st858; +tr3239: + { type_num(KNOT_RRTYPE_SSHFP, &rdata_tail); } + goto st858; +tr3240: + { type_num(KNOT_RRTYPE_SSHFP, &rdata_tail); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st858; +tr3241: + { type_num(KNOT_RRTYPE_SSHFP, &rdata_tail); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st858; +tr3242: + { type_num(KNOT_RRTYPE_SSHFP, &rdata_tail); } + { + s->line_counter++; + } + goto st858; +tr3249: + { type_num(KNOT_RRTYPE_TLSA, &rdata_tail); } + goto st858; +tr3250: + { type_num(KNOT_RRTYPE_TLSA, &rdata_tail); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st858; +tr3251: + { type_num(KNOT_RRTYPE_TLSA, &rdata_tail); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st858; +tr3252: + { type_num(KNOT_RRTYPE_TLSA, &rdata_tail); } + { + s->line_counter++; + } + goto st858; +tr3255: + { type_num(KNOT_RRTYPE_TXT, &rdata_tail); } + goto st858; +tr3256: + { type_num(KNOT_RRTYPE_TXT, &rdata_tail); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st858; +tr3257: + { type_num(KNOT_RRTYPE_TXT, &rdata_tail); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st858; +tr3258: + { type_num(KNOT_RRTYPE_TXT, &rdata_tail); } + { + s->line_counter++; + } + goto st858; +tr3272: + { type_num(KNOT_RRTYPE_URI, &rdata_tail); } + goto st858; +tr3273: + { type_num(KNOT_RRTYPE_URI, &rdata_tail); } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st858; +tr3274: + { type_num(KNOT_RRTYPE_URI, &rdata_tail); } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st858; +tr3275: + { type_num(KNOT_RRTYPE_URI, &rdata_tail); } + { + s->line_counter++; + } + goto st858; +st858: + if ( ++p == pe ) + goto _test_eof858; +case 858: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st858; + case 32: goto st858; + case 40: goto tr2854; + case 41: goto tr2855; + case 1034: goto tr2857; + case 1083: goto tr2858; + } + if ( _widec < 65 ) { + if ( 48 <= _widec && _widec <= 57 ) + goto tr2856; + } else if ( _widec > 90 ) { + if ( 97 <= _widec && _widec <= 122 ) + goto tr2856; + } else + goto tr2856; + goto tr71; +tr2856: + { p--; {stack[top++] = 859;goto st487;} } + goto st859; +st859: + if ( ++p == pe ) + goto _test_eof859; +case 859: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st860; + case 32: goto st860; + case 40: goto tr2860; + case 41: goto tr2861; + case 1034: goto tr2862; + case 1083: goto tr2863; + } + goto tr71; +tr2860: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st860; +tr2861: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st860; +tr2862: + { + s->line_counter++; + } + goto st860; +tr2951: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st860; +st860: + if ( ++p == pe ) + goto _test_eof860; +case 860: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st860; + case 32: goto st860; + case 40: goto tr2860; + case 41: goto tr2861; + case 1034: goto tr2862; + case 1083: goto tr2863; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2864; + goto tr1885; +tr2864: + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st861; +tr2868: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st861; +st861: + if ( ++p == pe ) + goto _test_eof861; +case 861: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr2865; + case 32: goto tr2865; + case 40: goto tr2866; + case 41: goto tr2867; + case 1034: goto tr2869; + case 1083: goto tr2870; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2868; + goto tr1885; +tr2872: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st862; +tr2873: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st862; +tr2875: + { + s->line_counter++; + } + goto st862; +tr2949: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st862; +tr2865: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + goto st862; +tr2866: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st862; +tr2867: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st862; +tr2869: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st862; +st862: + if ( ++p == pe ) + goto _test_eof862; +case 862: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st862; + case 32: goto st862; + case 40: goto tr2872; + case 41: goto tr2873; + case 1034: goto tr2875; + case 1083: goto tr2876; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2874; + goto tr1885; +tr2874: + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st863; +tr2880: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st863; +st863: + if ( ++p == pe ) + goto _test_eof863; +case 863: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr2877; + case 32: goto tr2877; + case 40: goto tr2878; + case 41: goto tr2879; + case 1034: goto tr2881; + case 1083: goto tr2882; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2880; + goto tr1885; +tr2885: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st864; +tr2886: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st864; +tr2888: + { + s->line_counter++; + } + goto st864; +tr2947: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st864; +tr2877: + { + if (s->number64 <= UINT32_MAX) { + uint32_t num32 = htonl((uint32_t)s->number64); + memcpy(rdata_tail, &num32, 4); + rdata_tail += 4; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + goto st864; +tr2878: + { + if (s->number64 <= UINT32_MAX) { + uint32_t num32 = htonl((uint32_t)s->number64); + memcpy(rdata_tail, &num32, 4); + rdata_tail += 4; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st864; +tr2879: + { + if (s->number64 <= UINT32_MAX) { + uint32_t num32 = htonl((uint32_t)s->number64); + memcpy(rdata_tail, &num32, 4); + rdata_tail += 4; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st864; +tr2881: + { + if (s->number64 <= UINT32_MAX) { + uint32_t num32 = htonl((uint32_t)s->number64); + memcpy(rdata_tail, &num32, 4); + rdata_tail += 4; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st864; +st864: + if ( ++p == pe ) + goto _test_eof864; +case 864: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st864; + case 32: goto st864; + case 40: goto tr2885; + case 41: goto tr2886; + case 1034: goto tr2888; + case 1083: goto tr2889; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2887; + goto tr2883; +tr2887: + { + s->buffer_length = 0; + } + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + goto st865; +tr2893: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + goto st865; +st865: + if ( ++p == pe ) + goto _test_eof865; +case 865: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr2890; + case 32: goto tr2890; + case 40: goto tr2891; + case 41: goto tr2892; + case 1034: goto tr2894; + case 1083: goto tr2895; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2893; + goto tr2883; +tr2897: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st866; +tr2898: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st866; +tr2900: + { + s->line_counter++; + } + goto st866; +tr2945: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st866; +tr2890: + { + s->buffer[s->buffer_length] = 0; + + if (s->buffer_length == 14) { // Date; 14 = len("YYYYMMDDHHmmSS"). + uint32_t timestamp; + int ret = date_to_timestamp(s->buffer, ×tamp); + + if (ret == ZS_OK) { + *((uint32_t *)rdata_tail) = htonl(timestamp); + rdata_tail += 4; + } else { + WARN(ret); + p--; {goto st268;} + } + } else if (s->buffer_length <= 10) { // Timestamp format. + char *end; + + s->number64 = strtoull((char *)(s->buffer), &end, 10); + + if (end == (char *)(s->buffer) || *end != '\0') { + WARN(ZS_BAD_TIMESTAMP); + p--; {goto st268;} + } + + if (s->number64 <= UINT32_MAX) { + *((uint32_t *)rdata_tail) = htonl((uint32_t)s->number64); + rdata_tail += 4; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } else { + WARN(ZS_BAD_TIMESTAMP_LENGTH); + p--; {goto st268;} + } + } + goto st866; +tr2891: + { + s->buffer[s->buffer_length] = 0; + + if (s->buffer_length == 14) { // Date; 14 = len("YYYYMMDDHHmmSS"). + uint32_t timestamp; + int ret = date_to_timestamp(s->buffer, ×tamp); + + if (ret == ZS_OK) { + *((uint32_t *)rdata_tail) = htonl(timestamp); + rdata_tail += 4; + } else { + WARN(ret); + p--; {goto st268;} + } + } else if (s->buffer_length <= 10) { // Timestamp format. + char *end; + + s->number64 = strtoull((char *)(s->buffer), &end, 10); + + if (end == (char *)(s->buffer) || *end != '\0') { + WARN(ZS_BAD_TIMESTAMP); + p--; {goto st268;} + } + + if (s->number64 <= UINT32_MAX) { + *((uint32_t *)rdata_tail) = htonl((uint32_t)s->number64); + rdata_tail += 4; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } else { + WARN(ZS_BAD_TIMESTAMP_LENGTH); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st866; +tr2892: + { + s->buffer[s->buffer_length] = 0; + + if (s->buffer_length == 14) { // Date; 14 = len("YYYYMMDDHHmmSS"). + uint32_t timestamp; + int ret = date_to_timestamp(s->buffer, ×tamp); + + if (ret == ZS_OK) { + *((uint32_t *)rdata_tail) = htonl(timestamp); + rdata_tail += 4; + } else { + WARN(ret); + p--; {goto st268;} + } + } else if (s->buffer_length <= 10) { // Timestamp format. + char *end; + + s->number64 = strtoull((char *)(s->buffer), &end, 10); + + if (end == (char *)(s->buffer) || *end != '\0') { + WARN(ZS_BAD_TIMESTAMP); + p--; {goto st268;} + } + + if (s->number64 <= UINT32_MAX) { + *((uint32_t *)rdata_tail) = htonl((uint32_t)s->number64); + rdata_tail += 4; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } else { + WARN(ZS_BAD_TIMESTAMP_LENGTH); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st866; +tr2894: + { + s->buffer[s->buffer_length] = 0; + + if (s->buffer_length == 14) { // Date; 14 = len("YYYYMMDDHHmmSS"). + uint32_t timestamp; + int ret = date_to_timestamp(s->buffer, ×tamp); + + if (ret == ZS_OK) { + *((uint32_t *)rdata_tail) = htonl(timestamp); + rdata_tail += 4; + } else { + WARN(ret); + p--; {goto st268;} + } + } else if (s->buffer_length <= 10) { // Timestamp format. + char *end; + + s->number64 = strtoull((char *)(s->buffer), &end, 10); + + if (end == (char *)(s->buffer) || *end != '\0') { + WARN(ZS_BAD_TIMESTAMP); + p--; {goto st268;} + } + + if (s->number64 <= UINT32_MAX) { + *((uint32_t *)rdata_tail) = htonl((uint32_t)s->number64); + rdata_tail += 4; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } else { + WARN(ZS_BAD_TIMESTAMP_LENGTH); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st866; +st866: + if ( ++p == pe ) + goto _test_eof866; +case 866: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st866; + case 32: goto st866; + case 40: goto tr2897; + case 41: goto tr2898; + case 1034: goto tr2900; + case 1083: goto tr2901; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2899; + goto tr2883; +tr2899: + { + s->buffer_length = 0; + } + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + goto st867; +tr2905: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + goto st867; +st867: + if ( ++p == pe ) + goto _test_eof867; +case 867: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr2902; + case 32: goto tr2902; + case 40: goto tr2903; + case 41: goto tr2904; + case 1034: goto tr2906; + case 1083: goto tr2907; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2905; + goto tr2883; +tr2909: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st868; +tr2910: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st868; +tr2912: + { + s->line_counter++; + } + goto st868; +tr2943: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st868; +tr2902: + { + s->buffer[s->buffer_length] = 0; + + if (s->buffer_length == 14) { // Date; 14 = len("YYYYMMDDHHmmSS"). + uint32_t timestamp; + int ret = date_to_timestamp(s->buffer, ×tamp); + + if (ret == ZS_OK) { + *((uint32_t *)rdata_tail) = htonl(timestamp); + rdata_tail += 4; + } else { + WARN(ret); + p--; {goto st268;} + } + } else if (s->buffer_length <= 10) { // Timestamp format. + char *end; + + s->number64 = strtoull((char *)(s->buffer), &end, 10); + + if (end == (char *)(s->buffer) || *end != '\0') { + WARN(ZS_BAD_TIMESTAMP); + p--; {goto st268;} + } + + if (s->number64 <= UINT32_MAX) { + *((uint32_t *)rdata_tail) = htonl((uint32_t)s->number64); + rdata_tail += 4; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } else { + WARN(ZS_BAD_TIMESTAMP_LENGTH); + p--; {goto st268;} + } + } + goto st868; +tr2903: + { + s->buffer[s->buffer_length] = 0; + + if (s->buffer_length == 14) { // Date; 14 = len("YYYYMMDDHHmmSS"). + uint32_t timestamp; + int ret = date_to_timestamp(s->buffer, ×tamp); + + if (ret == ZS_OK) { + *((uint32_t *)rdata_tail) = htonl(timestamp); + rdata_tail += 4; + } else { + WARN(ret); + p--; {goto st268;} + } + } else if (s->buffer_length <= 10) { // Timestamp format. + char *end; + + s->number64 = strtoull((char *)(s->buffer), &end, 10); + + if (end == (char *)(s->buffer) || *end != '\0') { + WARN(ZS_BAD_TIMESTAMP); + p--; {goto st268;} + } + + if (s->number64 <= UINT32_MAX) { + *((uint32_t *)rdata_tail) = htonl((uint32_t)s->number64); + rdata_tail += 4; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } else { + WARN(ZS_BAD_TIMESTAMP_LENGTH); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st868; +tr2904: + { + s->buffer[s->buffer_length] = 0; + + if (s->buffer_length == 14) { // Date; 14 = len("YYYYMMDDHHmmSS"). + uint32_t timestamp; + int ret = date_to_timestamp(s->buffer, ×tamp); + + if (ret == ZS_OK) { + *((uint32_t *)rdata_tail) = htonl(timestamp); + rdata_tail += 4; + } else { + WARN(ret); + p--; {goto st268;} + } + } else if (s->buffer_length <= 10) { // Timestamp format. + char *end; + + s->number64 = strtoull((char *)(s->buffer), &end, 10); + + if (end == (char *)(s->buffer) || *end != '\0') { + WARN(ZS_BAD_TIMESTAMP); + p--; {goto st268;} + } + + if (s->number64 <= UINT32_MAX) { + *((uint32_t *)rdata_tail) = htonl((uint32_t)s->number64); + rdata_tail += 4; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } else { + WARN(ZS_BAD_TIMESTAMP_LENGTH); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st868; +tr2906: + { + s->buffer[s->buffer_length] = 0; + + if (s->buffer_length == 14) { // Date; 14 = len("YYYYMMDDHHmmSS"). + uint32_t timestamp; + int ret = date_to_timestamp(s->buffer, ×tamp); + + if (ret == ZS_OK) { + *((uint32_t *)rdata_tail) = htonl(timestamp); + rdata_tail += 4; + } else { + WARN(ret); + p--; {goto st268;} + } + } else if (s->buffer_length <= 10) { // Timestamp format. + char *end; + + s->number64 = strtoull((char *)(s->buffer), &end, 10); + + if (end == (char *)(s->buffer) || *end != '\0') { + WARN(ZS_BAD_TIMESTAMP); + p--; {goto st268;} + } + + if (s->number64 <= UINT32_MAX) { + *((uint32_t *)rdata_tail) = htonl((uint32_t)s->number64); + rdata_tail += 4; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } else { + WARN(ZS_BAD_TIMESTAMP_LENGTH); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st868; +st868: + if ( ++p == pe ) + goto _test_eof868; +case 868: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st868; + case 32: goto st868; + case 40: goto tr2909; + case 41: goto tr2910; + case 1034: goto tr2912; + case 1083: goto tr2913; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2911; + goto tr1885; +tr2911: + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st869; +tr2917: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st869; +st869: + if ( ++p == pe ) + goto _test_eof869; +case 869: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr2914; + case 32: goto tr2914; + case 40: goto tr2915; + case 41: goto tr2916; + case 1034: goto tr2918; + case 1083: goto tr2919; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr2917; + goto tr1885; +tr2921: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st870; +tr2922: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st870; +tr2924: + { + s->line_counter++; + } + goto st870; +tr2941: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st870; +tr2914: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + goto st870; +tr2915: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st870; +tr2916: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st870; +tr2918: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st870; +st870: + if ( ++p == pe ) + goto _test_eof870; +case 870: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st870; + case 32: goto st870; + case 40: goto tr2921; + case 41: goto tr2922; + case 42: goto tr2923; + case 92: goto tr2923; + case 95: goto tr2923; + case 1034: goto tr2924; + case 1083: goto tr2925; + } + if ( _widec < 64 ) { + if ( 45 <= _widec && _widec <= 57 ) + goto tr2923; + } else if ( _widec > 90 ) { + if ( 97 <= _widec && _widec <= 122 ) + goto tr2923; + } else + goto tr2923; + goto tr71; +tr2923: + { + s->dname = rdata_tail; + } + { p--; {stack[top++] = 871;goto st270;} } + goto st871; +st871: + if ( ++p == pe ) + goto _test_eof871; +case 871: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr2926; + case 32: goto tr2926; + case 40: goto tr2927; + case 41: goto tr2928; + case 1034: goto tr2929; + case 1083: goto tr2930; + } + goto tr71; +tr2932: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st872; +tr2933: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st872; +tr2935: + { + s->line_counter++; + } + goto st872; +tr2939: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st872; +tr2926: + { + rdata_tail += s->dname_tmp_length; + } + goto st872; +tr2927: + { + rdata_tail += s->dname_tmp_length; + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st872; +tr2928: + { + rdata_tail += s->dname_tmp_length; + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st872; +tr2929: + { + rdata_tail += s->dname_tmp_length; + } + { + s->line_counter++; + } + goto st872; +st872: + if ( ++p == pe ) + goto _test_eof872; +case 872: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st872; + case 32: goto st872; + case 40: goto tr2932; + case 41: goto tr2933; + case 43: goto tr2934; + case 1034: goto tr2935; + case 1083: goto tr2936; + } + if ( _widec < 65 ) { + if ( 47 <= _widec && _widec <= 57 ) + goto tr2934; + } else if ( _widec > 90 ) { + if ( 97 <= _widec && _widec <= 122 ) + goto tr2934; + } else + goto tr2934; + goto tr71; +tr2934: + { p--; {stack[top++] = 873;goto st329;} } + goto st873; +st873: + if ( ++p == pe ) + goto _test_eof873; +case 873: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1152 + ((*p) - -128)); + if ( + !s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1152 + ((*p) - -128)); + if ( + !s->multiline ) _widec += 256; + } + switch( _widec ) { + case 1546: goto tr2937; + case 1595: goto tr2937; + } + goto tr71; +tr2937: + { + p--; {cs = stack[--top];goto _again;} + } + goto st1199; +st1199: + if ( ++p == pe ) + goto _test_eof1199; +case 1199: + goto st0; +tr2936: + { + s->buffer_length = 0; + } + goto st874; +tr2938: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st874; +tr2930: + { + rdata_tail += s->dname_tmp_length; + } + { + s->buffer_length = 0; + } + goto st874; +st874: + if ( ++p == pe ) + goto _test_eof874; +case 874: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2939; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2938; + goto tr71; +tr2925: + { + s->buffer_length = 0; + } + goto st875; +tr2940: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st875; +tr2919: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st875; +st875: + if ( ++p == pe ) + goto _test_eof875; +case 875: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2941; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2940; + goto tr71; +tr2913: + { + s->buffer_length = 0; + } + goto st876; +tr2942: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st876; +tr2907: + { + s->buffer[s->buffer_length] = 0; + + if (s->buffer_length == 14) { // Date; 14 = len("YYYYMMDDHHmmSS"). + uint32_t timestamp; + int ret = date_to_timestamp(s->buffer, ×tamp); + + if (ret == ZS_OK) { + *((uint32_t *)rdata_tail) = htonl(timestamp); + rdata_tail += 4; + } else { + WARN(ret); + p--; {goto st268;} + } + } else if (s->buffer_length <= 10) { // Timestamp format. + char *end; + + s->number64 = strtoull((char *)(s->buffer), &end, 10); + + if (end == (char *)(s->buffer) || *end != '\0') { + WARN(ZS_BAD_TIMESTAMP); + p--; {goto st268;} + } + + if (s->number64 <= UINT32_MAX) { + *((uint32_t *)rdata_tail) = htonl((uint32_t)s->number64); + rdata_tail += 4; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } else { + WARN(ZS_BAD_TIMESTAMP_LENGTH); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st876; +st876: + if ( ++p == pe ) + goto _test_eof876; +case 876: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2943; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2942; + goto tr71; +tr2901: + { + s->buffer_length = 0; + } + goto st877; +tr2944: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st877; +tr2895: + { + s->buffer[s->buffer_length] = 0; + + if (s->buffer_length == 14) { // Date; 14 = len("YYYYMMDDHHmmSS"). + uint32_t timestamp; + int ret = date_to_timestamp(s->buffer, ×tamp); + + if (ret == ZS_OK) { + *((uint32_t *)rdata_tail) = htonl(timestamp); + rdata_tail += 4; + } else { + WARN(ret); + p--; {goto st268;} + } + } else if (s->buffer_length <= 10) { // Timestamp format. + char *end; + + s->number64 = strtoull((char *)(s->buffer), &end, 10); + + if (end == (char *)(s->buffer) || *end != '\0') { + WARN(ZS_BAD_TIMESTAMP); + p--; {goto st268;} + } + + if (s->number64 <= UINT32_MAX) { + *((uint32_t *)rdata_tail) = htonl((uint32_t)s->number64); + rdata_tail += 4; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } else { + WARN(ZS_BAD_TIMESTAMP_LENGTH); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st877; +st877: + if ( ++p == pe ) + goto _test_eof877; +case 877: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2945; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2944; + goto tr71; +tr2889: + { + s->buffer_length = 0; + } + goto st878; +tr2946: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st878; +tr2882: + { + if (s->number64 <= UINT32_MAX) { + uint32_t num32 = htonl((uint32_t)s->number64); + memcpy(rdata_tail, &num32, 4); + rdata_tail += 4; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st878; +st878: + if ( ++p == pe ) + goto _test_eof878; +case 878: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2947; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2946; + goto tr71; +tr2876: + { + s->buffer_length = 0; + } + goto st879; +tr2948: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st879; +tr2870: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st879; +st879: + if ( ++p == pe ) + goto _test_eof879; +case 879: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2949; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2948; + goto tr71; +tr2863: + { + s->buffer_length = 0; + } + goto st880; +tr2950: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st880; +st880: + if ( ++p == pe ) + goto _test_eof880; +case 880: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2951; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2950; + goto tr71; +tr2858: + { + s->buffer_length = 0; + } + goto st881; +tr2952: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st881; +tr3269: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st881; +tr2852: + { type_num(KNOT_RRTYPE_A, &rdata_tail); } + { + s->buffer_length = 0; + } + goto st881; +tr2960: + { type_num(KNOT_RRTYPE_AAAA, &rdata_tail); } + { + s->buffer_length = 0; + } + goto st881; +tr2968: + { type_num(KNOT_RRTYPE_AFSDB, &rdata_tail); } + { + s->buffer_length = 0; + } + goto st881; +tr2974: + { type_num(KNOT_RRTYPE_APL, &rdata_tail); } + { + s->buffer_length = 0; + } + goto st881; +tr2984: + { type_num(KNOT_RRTYPE_CAA, &rdata_tail); } + { + s->buffer_length = 0; + } + goto st881; +tr2995: + { type_num(KNOT_RRTYPE_CDNSKEY, &rdata_tail); } + { + s->buffer_length = 0; + } + goto st881; +tr3000: + { type_num(KNOT_RRTYPE_CDS, &rdata_tail); } + { + s->buffer_length = 0; + } + goto st881; +tr3007: + { type_num(KNOT_RRTYPE_CERT, &rdata_tail); } + { + s->buffer_length = 0; + } + goto st881; +tr3015: + { type_num(KNOT_RRTYPE_CNAME, &rdata_tail); } + { + s->buffer_length = 0; + } + goto st881; +tr3026: + { type_num(KNOT_RRTYPE_DHCID, &rdata_tail); } + { + s->buffer_length = 0; + } + goto st881; +tr3035: + { type_num(KNOT_RRTYPE_DNAME, &rdata_tail); } + { + s->buffer_length = 0; + } + goto st881; +tr3043: + { type_num(KNOT_RRTYPE_DNSKEY, &rdata_tail); } + { + s->buffer_length = 0; + } + goto st881; +tr3048: + { type_num(KNOT_RRTYPE_DS, &rdata_tail); } + { + s->buffer_length = 0; + } + goto st881; +tr3058: + { type_num(KNOT_RRTYPE_EUI48, &rdata_tail); } + { + s->buffer_length = 0; + } + goto st881; +tr3064: + { type_num(KNOT_RRTYPE_EUI64, &rdata_tail); } + { + s->buffer_length = 0; + } + goto st881; +tr3073: + { type_num(KNOT_RRTYPE_HINFO, &rdata_tail); } + { + s->buffer_length = 0; + } + goto st881; +tr3085: + { type_num(KNOT_RRTYPE_IPSECKEY, &rdata_tail); } + { + s->buffer_length = 0; + } + goto st881; +tr3093: + { type_num(KNOT_RRTYPE_KEY, &rdata_tail); } + { + s->buffer_length = 0; + } + goto st881; +tr3098: + { type_num(KNOT_RRTYPE_KX, &rdata_tail); } + { + s->buffer_length = 0; + } + goto st881; +tr3108: + { type_num(KNOT_RRTYPE_L32, &rdata_tail); } + { + s->buffer_length = 0; + } + goto st881; +tr3114: + { type_num(KNOT_RRTYPE_L64, &rdata_tail); } + { + s->buffer_length = 0; + } + goto st881; +tr3120: + { type_num(KNOT_RRTYPE_LOC, &rdata_tail); } + { + s->buffer_length = 0; + } + goto st881; +tr3125: + { type_num(KNOT_RRTYPE_LP, &rdata_tail); } + { + s->buffer_length = 0; + } + goto st881; +tr3135: + { type_num(KNOT_RRTYPE_MINFO, &rdata_tail); } + { + s->buffer_length = 0; + } + goto st881; +tr3140: + { type_num(KNOT_RRTYPE_MX, &rdata_tail); } + { + s->buffer_length = 0; + } + goto st881; +tr3151: + { type_num(KNOT_RRTYPE_NAPTR, &rdata_tail); } + { + s->buffer_length = 0; + } + goto st881; +tr3157: + { type_num(KNOT_RRTYPE_NID, &rdata_tail); } + { + s->buffer_length = 0; + } + goto st881; +tr3163: + { type_num(KNOT_RRTYPE_NS, &rdata_tail); } + { + s->buffer_length = 0; + } + goto st881; +tr3170: + { type_num(KNOT_RRTYPE_NSEC, &rdata_tail); } + { + s->buffer_length = 0; + } + goto st881; +tr3176: + { type_num(KNOT_RRTYPE_NSEC3, &rdata_tail); } + { + s->buffer_length = 0; + } + goto st881; +tr3185: + { type_num(KNOT_RRTYPE_NSEC3PARAM, &rdata_tail); } + { + s->buffer_length = 0; + } + goto st881; +tr3192: + { type_num(KNOT_RRTYPE_PTR, &rdata_tail); } + { + s->buffer_length = 0; + } + goto st881; +tr3200: + { type_num(KNOT_RRTYPE_RP, &rdata_tail); } + { + s->buffer_length = 0; + } + goto st881; +tr3208: + { type_num(KNOT_RRTYPE_RRSIG, &rdata_tail); } + { + s->buffer_length = 0; + } + goto st881; +tr3213: + { type_num(KNOT_RRTYPE_RT, &rdata_tail); } + { + s->buffer_length = 0; + } + goto st881; +tr3223: + { type_num(KNOT_RRTYPE_SOA, &rdata_tail); } + { + s->buffer_length = 0; + } + goto st881; +tr3229: + { type_num(KNOT_RRTYPE_SPF, &rdata_tail); } + { + s->buffer_length = 0; + } + goto st881; +tr3235: + { type_num(KNOT_RRTYPE_SRV, &rdata_tail); } + { + s->buffer_length = 0; + } + goto st881; +tr3243: + { type_num(KNOT_RRTYPE_SSHFP, &rdata_tail); } + { + s->buffer_length = 0; + } + goto st881; +tr3253: + { type_num(KNOT_RRTYPE_TLSA, &rdata_tail); } + { + s->buffer_length = 0; + } + goto st881; +tr3259: + { type_num(KNOT_RRTYPE_TXT, &rdata_tail); } + { + s->buffer_length = 0; + } + goto st881; +tr3276: + { type_num(KNOT_RRTYPE_URI, &rdata_tail); } + { + s->buffer_length = 0; + } + goto st881; +st881: + if ( ++p == pe ) + goto _test_eof881; +case 881: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr2953; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr2952; + goto tr71; +st882: + if ( ++p == pe ) + goto _test_eof882; +case 882: + switch( (*p) ) { + case 65: goto st883; + case 97: goto st883; + } + goto tr2829; +st883: + if ( ++p == pe ) + goto _test_eof883; +case 883: + switch( (*p) ) { + case 65: goto st884; + case 97: goto st884; + } + goto tr2829; +st884: + if ( ++p == pe ) + goto _test_eof884; +case 884: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr2956; + case 32: goto tr2956; + case 40: goto tr2957; + case 41: goto tr2958; + case 1034: goto tr2959; + case 1083: goto tr2960; + } + goto tr2829; +st885: + if ( ++p == pe ) + goto _test_eof885; +case 885: + switch( (*p) ) { + case 83: goto st886; + case 115: goto st886; + } + goto tr2829; +st886: + if ( ++p == pe ) + goto _test_eof886; +case 886: + switch( (*p) ) { + case 68: goto st887; + case 100: goto st887; + } + goto tr2829; +st887: + if ( ++p == pe ) + goto _test_eof887; +case 887: + switch( (*p) ) { + case 66: goto st888; + case 98: goto st888; + } + goto tr2829; +st888: + if ( ++p == pe ) + goto _test_eof888; +case 888: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr2964; + case 32: goto tr2964; + case 40: goto tr2965; + case 41: goto tr2966; + case 1034: goto tr2967; + case 1083: goto tr2968; + } + goto tr2829; +st889: + if ( ++p == pe ) + goto _test_eof889; +case 889: + switch( (*p) ) { + case 76: goto st890; + case 108: goto st890; + } + goto tr2829; +st890: + if ( ++p == pe ) + goto _test_eof890; +case 890: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr2970; + case 32: goto tr2970; + case 40: goto tr2971; + case 41: goto tr2972; + case 1034: goto tr2973; + case 1083: goto tr2974; + } + goto tr2829; +st891: + if ( ++p == pe ) + goto _test_eof891; +case 891: + switch( (*p) ) { + case 65: goto st892; + case 68: goto st894; + case 69: goto st901; + case 78: goto st904; + case 97: goto st892; + case 100: goto st894; + case 101: goto st901; + case 110: goto st904; + } + goto tr2829; +st892: + if ( ++p == pe ) + goto _test_eof892; +case 892: + switch( (*p) ) { + case 65: goto st893; + case 97: goto st893; + } + goto tr2829; +st893: + if ( ++p == pe ) + goto _test_eof893; +case 893: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr2980; + case 32: goto tr2980; + case 40: goto tr2981; + case 41: goto tr2982; + case 1034: goto tr2983; + case 1083: goto tr2984; + } + goto tr2829; +st894: + if ( ++p == pe ) + goto _test_eof894; +case 894: + switch( (*p) ) { + case 78: goto st895; + case 83: goto st900; + case 110: goto st895; + case 115: goto st900; + } + goto tr2829; +st895: + if ( ++p == pe ) + goto _test_eof895; +case 895: + switch( (*p) ) { + case 83: goto st896; + case 115: goto st896; + } + goto tr2829; +st896: + if ( ++p == pe ) + goto _test_eof896; +case 896: + switch( (*p) ) { + case 75: goto st897; + case 107: goto st897; + } + goto tr2829; +st897: + if ( ++p == pe ) + goto _test_eof897; +case 897: + switch( (*p) ) { + case 69: goto st898; + case 101: goto st898; + } + goto tr2829; +st898: + if ( ++p == pe ) + goto _test_eof898; +case 898: + switch( (*p) ) { + case 89: goto st899; + case 121: goto st899; + } + goto tr2829; +st899: + if ( ++p == pe ) + goto _test_eof899; +case 899: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr2991; + case 32: goto tr2991; + case 40: goto tr2992; + case 41: goto tr2993; + case 1034: goto tr2994; + case 1083: goto tr2995; + } + goto tr2829; +st900: + if ( ++p == pe ) + goto _test_eof900; +case 900: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr2996; + case 32: goto tr2996; + case 40: goto tr2997; + case 41: goto tr2998; + case 1034: goto tr2999; + case 1083: goto tr3000; + } + goto tr2829; +st901: + if ( ++p == pe ) + goto _test_eof901; +case 901: + switch( (*p) ) { + case 82: goto st902; + case 114: goto st902; + } + goto tr2829; +st902: + if ( ++p == pe ) + goto _test_eof902; +case 902: + switch( (*p) ) { + case 84: goto st903; + case 116: goto st903; + } + goto tr2829; +st903: + if ( ++p == pe ) + goto _test_eof903; +case 903: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3003; + case 32: goto tr3003; + case 40: goto tr3004; + case 41: goto tr3005; + case 1034: goto tr3006; + case 1083: goto tr3007; + } + goto tr2829; +st904: + if ( ++p == pe ) + goto _test_eof904; +case 904: + switch( (*p) ) { + case 65: goto st905; + case 97: goto st905; + } + goto tr2829; +st905: + if ( ++p == pe ) + goto _test_eof905; +case 905: + switch( (*p) ) { + case 77: goto st906; + case 109: goto st906; + } + goto tr2829; +st906: + if ( ++p == pe ) + goto _test_eof906; +case 906: + switch( (*p) ) { + case 69: goto st907; + case 101: goto st907; + } + goto tr2829; +st907: + if ( ++p == pe ) + goto _test_eof907; +case 907: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3011; + case 32: goto tr3011; + case 40: goto tr3012; + case 41: goto tr3013; + case 1034: goto tr3014; + case 1083: goto tr3015; + } + goto tr2829; +st908: + if ( ++p == pe ) + goto _test_eof908; +case 908: + switch( (*p) ) { + case 72: goto st909; + case 78: goto st913; + case 83: goto st921; + case 104: goto st909; + case 110: goto st913; + case 115: goto st921; + } + goto tr2829; +st909: + if ( ++p == pe ) + goto _test_eof909; +case 909: + switch( (*p) ) { + case 67: goto st910; + case 99: goto st910; + } + goto tr2829; +st910: + if ( ++p == pe ) + goto _test_eof910; +case 910: + switch( (*p) ) { + case 73: goto st911; + case 105: goto st911; + } + goto tr2829; +st911: + if ( ++p == pe ) + goto _test_eof911; +case 911: + switch( (*p) ) { + case 68: goto st912; + case 100: goto st912; + } + goto tr2829; +st912: + if ( ++p == pe ) + goto _test_eof912; +case 912: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3022; + case 32: goto tr3022; + case 40: goto tr3023; + case 41: goto tr3024; + case 1034: goto tr3025; + case 1083: goto tr3026; + } + goto tr2829; +st913: + if ( ++p == pe ) + goto _test_eof913; +case 913: + switch( (*p) ) { + case 65: goto st914; + case 83: goto st917; + case 97: goto st914; + case 115: goto st917; + } + goto tr2829; +st914: + if ( ++p == pe ) + goto _test_eof914; +case 914: + switch( (*p) ) { + case 77: goto st915; + case 109: goto st915; + } + goto tr2829; +st915: + if ( ++p == pe ) + goto _test_eof915; +case 915: + switch( (*p) ) { + case 69: goto st916; + case 101: goto st916; + } + goto tr2829; +st916: + if ( ++p == pe ) + goto _test_eof916; +case 916: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3031; + case 32: goto tr3031; + case 40: goto tr3032; + case 41: goto tr3033; + case 1034: goto tr3034; + case 1083: goto tr3035; + } + goto tr2829; +st917: + if ( ++p == pe ) + goto _test_eof917; +case 917: + switch( (*p) ) { + case 75: goto st918; + case 107: goto st918; + } + goto tr2829; +st918: + if ( ++p == pe ) + goto _test_eof918; +case 918: + switch( (*p) ) { + case 69: goto st919; + case 101: goto st919; + } + goto tr2829; +st919: + if ( ++p == pe ) + goto _test_eof919; +case 919: + switch( (*p) ) { + case 89: goto st920; + case 121: goto st920; + } + goto tr2829; +st920: + if ( ++p == pe ) + goto _test_eof920; +case 920: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3039; + case 32: goto tr3039; + case 40: goto tr3040; + case 41: goto tr3041; + case 1034: goto tr3042; + case 1083: goto tr3043; + } + goto tr2829; +st921: + if ( ++p == pe ) + goto _test_eof921; +case 921: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3044; + case 32: goto tr3044; + case 40: goto tr3045; + case 41: goto tr3046; + case 1034: goto tr3047; + case 1083: goto tr3048; + } + goto tr2829; +st922: + if ( ++p == pe ) + goto _test_eof922; +case 922: + switch( (*p) ) { + case 85: goto st923; + case 117: goto st923; + } + goto tr2829; +st923: + if ( ++p == pe ) + goto _test_eof923; +case 923: + switch( (*p) ) { + case 73: goto st924; + case 105: goto st924; + } + goto tr2829; +st924: + if ( ++p == pe ) + goto _test_eof924; +case 924: + switch( (*p) ) { + case 52: goto st925; + case 54: goto st927; + } + goto tr2829; +st925: + if ( ++p == pe ) + goto _test_eof925; +case 925: + if ( (*p) == 56 ) + goto st926; + goto tr2829; +st926: + if ( ++p == pe ) + goto _test_eof926; +case 926: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3054; + case 32: goto tr3054; + case 40: goto tr3055; + case 41: goto tr3056; + case 1034: goto tr3057; + case 1083: goto tr3058; + } + goto tr2829; +st927: + if ( ++p == pe ) + goto _test_eof927; +case 927: + if ( (*p) == 52 ) + goto st928; + goto tr2829; +st928: + if ( ++p == pe ) + goto _test_eof928; +case 928: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3060; + case 32: goto tr3060; + case 40: goto tr3061; + case 41: goto tr3062; + case 1034: goto tr3063; + case 1083: goto tr3064; + } + goto tr2829; +st929: + if ( ++p == pe ) + goto _test_eof929; +case 929: + switch( (*p) ) { + case 73: goto st930; + case 105: goto st930; + } + goto tr2829; +st930: + if ( ++p == pe ) + goto _test_eof930; +case 930: + switch( (*p) ) { + case 78: goto st931; + case 110: goto st931; + } + goto tr2829; +st931: + if ( ++p == pe ) + goto _test_eof931; +case 931: + switch( (*p) ) { + case 70: goto st932; + case 102: goto st932; + } + goto tr2829; +st932: + if ( ++p == pe ) + goto _test_eof932; +case 932: + switch( (*p) ) { + case 79: goto st933; + case 111: goto st933; + } + goto tr2829; +st933: + if ( ++p == pe ) + goto _test_eof933; +case 933: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3069; + case 32: goto tr3069; + case 40: goto tr3070; + case 41: goto tr3071; + case 1034: goto tr3072; + case 1083: goto tr3073; + } + goto tr2829; +st934: + if ( ++p == pe ) + goto _test_eof934; +case 934: + switch( (*p) ) { + case 80: goto st935; + case 112: goto st935; + } + goto tr2829; +st935: + if ( ++p == pe ) + goto _test_eof935; +case 935: + switch( (*p) ) { + case 83: goto st936; + case 115: goto st936; + } + goto tr2829; +st936: + if ( ++p == pe ) + goto _test_eof936; +case 936: + switch( (*p) ) { + case 69: goto st937; + case 101: goto st937; + } + goto tr2829; +st937: + if ( ++p == pe ) + goto _test_eof937; +case 937: + switch( (*p) ) { + case 67: goto st938; + case 99: goto st938; + } + goto tr2829; +st938: + if ( ++p == pe ) + goto _test_eof938; +case 938: + switch( (*p) ) { + case 75: goto st939; + case 107: goto st939; + } + goto tr2829; +st939: + if ( ++p == pe ) + goto _test_eof939; +case 939: + switch( (*p) ) { + case 69: goto st940; + case 101: goto st940; + } + goto tr2829; +st940: + if ( ++p == pe ) + goto _test_eof940; +case 940: + switch( (*p) ) { + case 89: goto st941; + case 121: goto st941; + } + goto tr2829; +st941: + if ( ++p == pe ) + goto _test_eof941; +case 941: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3081; + case 32: goto tr3081; + case 40: goto tr3082; + case 41: goto tr3083; + case 1034: goto tr3084; + case 1083: goto tr3085; + } + goto tr2829; +st942: + if ( ++p == pe ) + goto _test_eof942; +case 942: + switch( (*p) ) { + case 69: goto st943; + case 88: goto st945; + case 101: goto st943; + case 120: goto st945; + } + goto tr2829; +st943: + if ( ++p == pe ) + goto _test_eof943; +case 943: + switch( (*p) ) { + case 89: goto st944; + case 121: goto st944; + } + goto tr2829; +st944: + if ( ++p == pe ) + goto _test_eof944; +case 944: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3089; + case 32: goto tr3089; + case 40: goto tr3090; + case 41: goto tr3091; + case 1034: goto tr3092; + case 1083: goto tr3093; + } + goto tr2829; +st945: + if ( ++p == pe ) + goto _test_eof945; +case 945: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3094; + case 32: goto tr3094; + case 40: goto tr3095; + case 41: goto tr3096; + case 1034: goto tr3097; + case 1083: goto tr3098; + } + goto tr2829; +st946: + if ( ++p == pe ) + goto _test_eof946; +case 946: + switch( (*p) ) { + case 51: goto st947; + case 54: goto st949; + case 79: goto st951; + case 80: goto st953; + case 111: goto st951; + case 112: goto st953; + } + goto tr2829; +st947: + if ( ++p == pe ) + goto _test_eof947; +case 947: + if ( (*p) == 50 ) + goto st948; + goto tr2829; +st948: + if ( ++p == pe ) + goto _test_eof948; +case 948: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3104; + case 32: goto tr3104; + case 40: goto tr3105; + case 41: goto tr3106; + case 1034: goto tr3107; + case 1083: goto tr3108; + } + goto tr2829; +st949: + if ( ++p == pe ) + goto _test_eof949; +case 949: + if ( (*p) == 52 ) + goto st950; + goto tr2829; +st950: + if ( ++p == pe ) + goto _test_eof950; +case 950: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3110; + case 32: goto tr3110; + case 40: goto tr3111; + case 41: goto tr3112; + case 1034: goto tr3113; + case 1083: goto tr3114; + } + goto tr2829; +st951: + if ( ++p == pe ) + goto _test_eof951; +case 951: + switch( (*p) ) { + case 67: goto st952; + case 99: goto st952; + } + goto tr2829; +st952: + if ( ++p == pe ) + goto _test_eof952; +case 952: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3116; + case 32: goto tr3116; + case 40: goto tr3117; + case 41: goto tr3118; + case 1034: goto tr3119; + case 1083: goto tr3120; + } + goto tr2829; +st953: + if ( ++p == pe ) + goto _test_eof953; +case 953: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3121; + case 32: goto tr3121; + case 40: goto tr3122; + case 41: goto tr3123; + case 1034: goto tr3124; + case 1083: goto tr3125; + } + goto tr2829; +st954: + if ( ++p == pe ) + goto _test_eof954; +case 954: + switch( (*p) ) { + case 73: goto st955; + case 88: goto st959; + case 105: goto st955; + case 120: goto st959; + } + goto tr2829; +st955: + if ( ++p == pe ) + goto _test_eof955; +case 955: + switch( (*p) ) { + case 78: goto st956; + case 110: goto st956; + } + goto tr2829; +st956: + if ( ++p == pe ) + goto _test_eof956; +case 956: + switch( (*p) ) { + case 70: goto st957; + case 102: goto st957; + } + goto tr2829; +st957: + if ( ++p == pe ) + goto _test_eof957; +case 957: + switch( (*p) ) { + case 79: goto st958; + case 111: goto st958; + } + goto tr2829; +st958: + if ( ++p == pe ) + goto _test_eof958; +case 958: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3131; + case 32: goto tr3131; + case 40: goto tr3132; + case 41: goto tr3133; + case 1034: goto tr3134; + case 1083: goto tr3135; + } + goto tr2829; +st959: + if ( ++p == pe ) + goto _test_eof959; +case 959: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3136; + case 32: goto tr3136; + case 40: goto tr3137; + case 41: goto tr3138; + case 1034: goto tr3139; + case 1083: goto tr3140; + } + goto tr2829; +st960: + if ( ++p == pe ) + goto _test_eof960; +case 960: + switch( (*p) ) { + case 65: goto st961; + case 73: goto st965; + case 83: goto st967; + case 97: goto st961; + case 105: goto st965; + case 115: goto st967; + } + goto tr2829; +st961: + if ( ++p == pe ) + goto _test_eof961; +case 961: + switch( (*p) ) { + case 80: goto st962; + case 112: goto st962; + } + goto tr2829; +st962: + if ( ++p == pe ) + goto _test_eof962; +case 962: + switch( (*p) ) { + case 84: goto st963; + case 116: goto st963; + } + goto tr2829; +st963: + if ( ++p == pe ) + goto _test_eof963; +case 963: + switch( (*p) ) { + case 82: goto st964; + case 114: goto st964; + } + goto tr2829; +st964: + if ( ++p == pe ) + goto _test_eof964; +case 964: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3147; + case 32: goto tr3147; + case 40: goto tr3148; + case 41: goto tr3149; + case 1034: goto tr3150; + case 1083: goto tr3151; + } + goto tr2829; +st965: + if ( ++p == pe ) + goto _test_eof965; +case 965: + switch( (*p) ) { + case 68: goto st966; + case 100: goto st966; + } + goto tr2829; +st966: + if ( ++p == pe ) + goto _test_eof966; +case 966: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3153; + case 32: goto tr3153; + case 40: goto tr3154; + case 41: goto tr3155; + case 1034: goto tr3156; + case 1083: goto tr3157; + } + goto tr2829; +st967: + if ( ++p == pe ) + goto _test_eof967; +case 967: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3158; + case 32: goto tr3158; + case 40: goto tr3159; + case 41: goto tr3160; + case 69: goto st968; + case 101: goto st968; + case 1034: goto tr3162; + case 1083: goto tr3163; + } + goto tr2829; +st968: + if ( ++p == pe ) + goto _test_eof968; +case 968: + switch( (*p) ) { + case 67: goto st969; + case 99: goto st969; + } + goto tr2829; +st969: + if ( ++p == pe ) + goto _test_eof969; +case 969: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3165; + case 32: goto tr3165; + case 40: goto tr3166; + case 41: goto tr3167; + case 51: goto st970; + case 1034: goto tr3169; + case 1083: goto tr3170; + } + goto tr2829; +st970: + if ( ++p == pe ) + goto _test_eof970; +case 970: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3171; + case 32: goto tr3171; + case 40: goto tr3172; + case 41: goto tr3173; + case 80: goto st971; + case 112: goto st971; + case 1034: goto tr3175; + case 1083: goto tr3176; + } + goto tr2829; +st971: + if ( ++p == pe ) + goto _test_eof971; +case 971: + switch( (*p) ) { + case 65: goto st972; + case 97: goto st972; + } + goto tr2829; +st972: + if ( ++p == pe ) + goto _test_eof972; +case 972: + switch( (*p) ) { + case 82: goto st973; + case 114: goto st973; + } + goto tr2829; +st973: + if ( ++p == pe ) + goto _test_eof973; +case 973: + switch( (*p) ) { + case 65: goto st974; + case 97: goto st974; + } + goto tr2829; +st974: + if ( ++p == pe ) + goto _test_eof974; +case 974: + switch( (*p) ) { + case 77: goto st975; + case 109: goto st975; + } + goto tr2829; +st975: + if ( ++p == pe ) + goto _test_eof975; +case 975: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3181; + case 32: goto tr3181; + case 40: goto tr3182; + case 41: goto tr3183; + case 1034: goto tr3184; + case 1083: goto tr3185; + } + goto tr2829; +st976: + if ( ++p == pe ) + goto _test_eof976; +case 976: + switch( (*p) ) { + case 84: goto st977; + case 116: goto st977; + } + goto tr2829; +st977: + if ( ++p == pe ) + goto _test_eof977; +case 977: + switch( (*p) ) { + case 82: goto st978; + case 114: goto st978; + } + goto tr2829; +st978: + if ( ++p == pe ) + goto _test_eof978; +case 978: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3188; + case 32: goto tr3188; + case 40: goto tr3189; + case 41: goto tr3190; + case 1034: goto tr3191; + case 1083: goto tr3192; + } + goto tr2829; +st979: + if ( ++p == pe ) + goto _test_eof979; +case 979: + switch( (*p) ) { + case 80: goto st980; + case 82: goto st981; + case 84: goto st985; + case 112: goto st980; + case 114: goto st981; + case 116: goto st985; + } + goto tr2829; +st980: + if ( ++p == pe ) + goto _test_eof980; +case 980: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3196; + case 32: goto tr3196; + case 40: goto tr3197; + case 41: goto tr3198; + case 1034: goto tr3199; + case 1083: goto tr3200; + } + goto tr2829; +st981: + if ( ++p == pe ) + goto _test_eof981; +case 981: + switch( (*p) ) { + case 83: goto st982; + case 115: goto st982; + } + goto tr2829; +st982: + if ( ++p == pe ) + goto _test_eof982; +case 982: + switch( (*p) ) { + case 73: goto st983; + case 105: goto st983; + } + goto tr2829; +st983: + if ( ++p == pe ) + goto _test_eof983; +case 983: + switch( (*p) ) { + case 71: goto st984; + case 103: goto st984; + } + goto tr2829; +st984: + if ( ++p == pe ) + goto _test_eof984; +case 984: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3204; + case 32: goto tr3204; + case 40: goto tr3205; + case 41: goto tr3206; + case 1034: goto tr3207; + case 1083: goto tr3208; + } + goto tr2829; +st985: + if ( ++p == pe ) + goto _test_eof985; +case 985: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3209; + case 32: goto tr3209; + case 40: goto tr3210; + case 41: goto tr3211; + case 1034: goto tr3212; + case 1083: goto tr3213; + } + goto tr2829; +st986: + if ( ++p == pe ) + goto _test_eof986; +case 986: + switch( (*p) ) { + case 79: goto st987; + case 80: goto st989; + case 82: goto st991; + case 83: goto st993; + case 111: goto st987; + case 112: goto st989; + case 114: goto st991; + case 115: goto st993; + } + goto tr2829; +st987: + if ( ++p == pe ) + goto _test_eof987; +case 987: + switch( (*p) ) { + case 65: goto st988; + case 97: goto st988; + } + goto tr2829; +st988: + if ( ++p == pe ) + goto _test_eof988; +case 988: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3219; + case 32: goto tr3219; + case 40: goto tr3220; + case 41: goto tr3221; + case 1034: goto tr3222; + case 1083: goto tr3223; + } + goto tr2829; +st989: + if ( ++p == pe ) + goto _test_eof989; +case 989: + switch( (*p) ) { + case 70: goto st990; + case 102: goto st990; + } + goto tr2829; +st990: + if ( ++p == pe ) + goto _test_eof990; +case 990: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3225; + case 32: goto tr3225; + case 40: goto tr3226; + case 41: goto tr3227; + case 1034: goto tr3228; + case 1083: goto tr3229; + } + goto tr2829; +st991: + if ( ++p == pe ) + goto _test_eof991; +case 991: + switch( (*p) ) { + case 86: goto st992; + case 118: goto st992; + } + goto tr2829; +st992: + if ( ++p == pe ) + goto _test_eof992; +case 992: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3231; + case 32: goto tr3231; + case 40: goto tr3232; + case 41: goto tr3233; + case 1034: goto tr3234; + case 1083: goto tr3235; + } + goto tr2829; +st993: + if ( ++p == pe ) + goto _test_eof993; +case 993: + switch( (*p) ) { + case 72: goto st994; + case 104: goto st994; + } + goto tr2829; +st994: + if ( ++p == pe ) + goto _test_eof994; +case 994: + switch( (*p) ) { + case 70: goto st995; + case 102: goto st995; + } + goto tr2829; +st995: + if ( ++p == pe ) + goto _test_eof995; +case 995: + switch( (*p) ) { + case 80: goto st996; + case 112: goto st996; + } + goto tr2829; +st996: + if ( ++p == pe ) + goto _test_eof996; +case 996: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3239; + case 32: goto tr3239; + case 40: goto tr3240; + case 41: goto tr3241; + case 1034: goto tr3242; + case 1083: goto tr3243; + } + goto tr2829; +st997: + if ( ++p == pe ) + goto _test_eof997; +case 997: + switch( (*p) ) { + case 76: goto st998; + case 88: goto st1001; + case 89: goto st1003; + case 108: goto st998; + case 120: goto st1001; + case 121: goto st1003; + } + goto tr2829; +st998: + if ( ++p == pe ) + goto _test_eof998; +case 998: + switch( (*p) ) { + case 83: goto st999; + case 115: goto st999; + } + goto tr2829; +st999: + if ( ++p == pe ) + goto _test_eof999; +case 999: + switch( (*p) ) { + case 65: goto st1000; + case 97: goto st1000; + } + goto tr2829; +st1000: + if ( ++p == pe ) + goto _test_eof1000; +case 1000: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3249; + case 32: goto tr3249; + case 40: goto tr3250; + case 41: goto tr3251; + case 1034: goto tr3252; + case 1083: goto tr3253; + } + goto tr2829; +st1001: + if ( ++p == pe ) + goto _test_eof1001; +case 1001: + switch( (*p) ) { + case 84: goto st1002; + case 116: goto st1002; + } + goto tr2829; +st1002: + if ( ++p == pe ) + goto _test_eof1002; +case 1002: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3255; + case 32: goto tr3255; + case 40: goto tr3256; + case 41: goto tr3257; + case 1034: goto tr3258; + case 1083: goto tr3259; + } + goto tr2829; +st1003: + if ( ++p == pe ) + goto _test_eof1003; +case 1003: + switch( (*p) ) { + case 80: goto st1004; + case 112: goto st1004; + } + goto tr2829; +st1004: + if ( ++p == pe ) + goto _test_eof1004; +case 1004: + switch( (*p) ) { + case 69: goto st1005; + case 101: goto st1005; + } + goto tr2829; +st1005: + if ( ++p == pe ) + goto _test_eof1005; +case 1005: + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr3263; + goto tr3262; +tr3263: + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st1006; +tr3267: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st1006; +st1006: + if ( ++p == pe ) + goto _test_eof1006; +case 1006: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3264; + case 32: goto tr3264; + case 40: goto tr3265; + case 41: goto tr3266; + case 1034: goto tr3268; + case 1083: goto tr3269; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr3267; + goto tr3262; +st1007: + if ( ++p == pe ) + goto _test_eof1007; +case 1007: + switch( (*p) ) { + case 82: goto st1008; + case 114: goto st1008; + } + goto tr2829; +st1008: + if ( ++p == pe ) + goto _test_eof1008; +case 1008: + switch( (*p) ) { + case 73: goto st1009; + case 105: goto st1009; + } + goto tr2829; +st1009: + if ( ++p == pe ) + goto _test_eof1009; +case 1009: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3272; + case 32: goto tr3272; + case 40: goto tr3273; + case 41: goto tr3274; + case 1034: goto tr3275; + case 1083: goto tr3276; + } + goto tr2829; +st1010: + if ( ++p == pe ) + goto _test_eof1010; +case 1010: + switch( (*p) ) { + case 42: goto tr3277; + case 92: goto tr3277; + case 95: goto tr3277; + } + if ( (*p) < 64 ) { + if ( 45 <= (*p) && (*p) <= 57 ) + goto tr3277; + } else if ( (*p) > 90 ) { + if ( 97 <= (*p) && (*p) <= 122 ) + goto tr3277; + } else + goto tr3277; + goto tr71; +tr3277: + { + s->dname = rdata_tail; + } + { p--; {stack[top++] = 1011;goto st270;} } + goto st1011; +st1011: + if ( ++p == pe ) + goto _test_eof1011; +case 1011: + switch( (*p) ) { + case 32: goto tr3278; + case 59: goto tr3278; + } + if ( (*p) > 10 ) { + if ( 40 <= (*p) && (*p) <= 41 ) + goto tr3278; + } else if ( (*p) >= 9 ) + goto tr3278; + goto tr71; +tr3278: + { + rdata_tail += s->dname_tmp_length; + } + { p--; {stack[top++] = 1012;goto st336;} } + goto st1012; +st1012: + if ( ++p == pe ) + goto _test_eof1012; +case 1012: + switch( (*p) ) { + case 32: goto tr3279; + case 59: goto tr3279; + } + if ( (*p) > 10 ) { + if ( 40 <= (*p) && (*p) <= 41 ) + goto tr3279; + } else if ( (*p) >= 9 ) + goto tr3279; + goto tr71; +tr3279: + { + p--; {cs = stack[--top];goto _again;} + } + goto st1200; +st1200: + if ( ++p == pe ) + goto _test_eof1200; +case 1200: + goto st0; +st1013: + if ( ++p == pe ) + goto _test_eof1013; +case 1013: + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr3280; + goto tr1885; +tr3280: + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st1014; +tr3284: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st1014; +st1014: + if ( ++p == pe ) + goto _test_eof1014; +case 1014: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3281; + case 32: goto tr3281; + case 40: goto tr3282; + case 41: goto tr3283; + case 1034: goto tr3285; + case 1083: goto tr3286; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr3284; + goto tr1885; +tr3288: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st1015; +tr3289: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st1015; +tr3291: + { + s->line_counter++; + } + goto st1015; +tr3317: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st1015; +tr3281: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + goto st1015; +tr3282: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st1015; +tr3283: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st1015; +tr3285: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st1015; +st1015: + if ( ++p == pe ) + goto _test_eof1015; +case 1015: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st1015; + case 32: goto st1015; + case 40: goto tr3288; + case 41: goto tr3289; + case 1034: goto tr3291; + case 1083: goto tr3292; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr3290; + goto tr1885; +tr3290: + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st1016; +tr3296: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st1016; +st1016: + if ( ++p == pe ) + goto _test_eof1016; +case 1016: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3293; + case 32: goto tr3293; + case 40: goto tr3294; + case 41: goto tr3295; + case 1034: goto tr3297; + case 1083: goto tr3298; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr3296; + goto tr1885; +tr3300: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st1017; +tr3301: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st1017; +tr3303: + { + s->line_counter++; + } + goto st1017; +tr3315: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st1017; +tr3293: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + goto st1017; +tr3294: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st1017; +tr3295: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st1017; +tr3297: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st1017; +st1017: + if ( ++p == pe ) + goto _test_eof1017; +case 1017: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st1017; + case 32: goto st1017; + case 40: goto tr3300; + case 41: goto tr3301; + case 1034: goto tr3303; + case 1083: goto tr3304; + } + if ( _widec < 65 ) { + if ( 48 <= _widec && _widec <= 57 ) + goto tr3302; + } else if ( _widec > 90 ) { + if ( 97 <= _widec && _widec <= 122 ) + goto tr3302; + } else + goto tr3302; + goto tr71; +tr3302: + { p--; {stack[top++] = 1018;goto st487;} } + goto st1018; +st1018: + if ( ++p == pe ) + goto _test_eof1018; +case 1018: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st1019; + case 32: goto st1019; + case 40: goto tr3306; + case 41: goto tr3307; + case 1034: goto tr3308; + case 1083: goto tr3309; + } + goto tr71; +tr3306: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st1019; +tr3307: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st1019; +tr3308: + { + s->line_counter++; + } + goto st1019; +tr3313: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st1019; +st1019: + if ( ++p == pe ) + goto _test_eof1019; +case 1019: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st1019; + case 32: goto st1019; + case 40: goto tr3306; + case 41: goto tr3307; + case 43: goto tr3310; + case 1034: goto tr3308; + case 1083: goto tr3309; + } + if ( _widec < 65 ) { + if ( 47 <= _widec && _widec <= 57 ) + goto tr3310; + } else if ( _widec > 90 ) { + if ( 97 <= _widec && _widec <= 122 ) + goto tr3310; + } else + goto tr3310; + goto tr71; +tr3310: + { p--; {stack[top++] = 1020;goto st329;} } + goto st1020; +st1020: + if ( ++p == pe ) + goto _test_eof1020; +case 1020: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1152 + ((*p) - -128)); + if ( + !s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1152 + ((*p) - -128)); + if ( + !s->multiline ) _widec += 256; + } + switch( _widec ) { + case 1546: goto tr3311; + case 1595: goto tr3311; + } + goto tr71; +tr3311: + { + p--; {cs = stack[--top];goto _again;} + } + goto st1201; +st1201: + if ( ++p == pe ) + goto _test_eof1201; +case 1201: + goto st0; +tr3309: + { + s->buffer_length = 0; + } + goto st1021; +tr3312: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st1021; +st1021: + if ( ++p == pe ) + goto _test_eof1021; +case 1021: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr3313; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr3312; + goto tr71; +tr3304: + { + s->buffer_length = 0; + } + goto st1022; +tr3314: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st1022; +tr3298: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st1022; +st1022: + if ( ++p == pe ) + goto _test_eof1022; +case 1022: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr3315; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr3314; + goto tr71; +tr3292: + { + s->buffer_length = 0; + } + goto st1023; +tr3316: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st1023; +tr3286: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st1023; +st1023: + if ( ++p == pe ) + goto _test_eof1023; +case 1023: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr3317; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr3316; + goto tr71; +st1024: + if ( ++p == pe ) + goto _test_eof1024; +case 1024: + if ( (*p) == 43 ) + goto tr3318; + if ( (*p) < 65 ) { + if ( 47 <= (*p) && (*p) <= 57 ) + goto tr3318; + } else if ( (*p) > 90 ) { + if ( 97 <= (*p) && (*p) <= 122 ) + goto tr3318; + } else + goto tr3318; + goto tr71; +tr3318: + { p--; {stack[top++] = 1025;goto st329;} } + goto st1025; +st1025: + if ( ++p == pe ) + goto _test_eof1025; +case 1025: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1152 + ((*p) - -128)); + if ( + !s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1152 + ((*p) - -128)); + if ( + !s->multiline ) _widec += 256; + } + switch( _widec ) { + case 1546: goto tr3319; + case 1595: goto tr3319; + } + goto tr71; +tr3319: + { + p--; {cs = stack[--top];goto _again;} + } + goto st1202; +st1202: + if ( ++p == pe ) + goto _test_eof1202; +case 1202: + goto st0; +st1026: + if ( ++p == pe ) + goto _test_eof1026; +case 1026: + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr3320; + goto tr1885; +tr3320: + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st1027; +tr3324: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st1027; +st1027: + if ( ++p == pe ) + goto _test_eof1027; +case 1027: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3321; + case 32: goto tr3321; + case 40: goto tr3322; + case 41: goto tr3323; + case 1034: goto tr3325; + case 1083: goto tr3326; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr3324; + goto tr1885; +tr3328: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st1028; +tr3329: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st1028; +tr3331: + { + s->line_counter++; + } + goto st1028; +tr3395: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st1028; +tr3321: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + goto st1028; +tr3322: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st1028; +tr3323: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st1028; +tr3325: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st1028; +st1028: + if ( ++p == pe ) + goto _test_eof1028; +case 1028: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st1028; + case 32: goto st1028; + case 40: goto tr3328; + case 41: goto tr3329; + case 1034: goto tr3331; + case 1083: goto tr3332; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr3330; + goto tr1885; +tr3330: + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st1029; +tr3336: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st1029; +st1029: + if ( ++p == pe ) + goto _test_eof1029; +case 1029: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3333; + case 32: goto tr3333; + case 40: goto tr3334; + case 41: goto tr3335; + case 1034: goto tr3337; + case 1083: goto tr3338; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr3336; + goto tr1885; +tr3340: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st1030; +tr3341: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st1030; +tr3343: + { + s->line_counter++; + } + goto st1030; +tr3393: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st1030; +tr3333: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + goto st1030; +tr3334: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st1030; +tr3335: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st1030; +tr3337: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st1030; +st1030: + if ( ++p == pe ) + goto _test_eof1030; +case 1030: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st1030; + case 32: goto st1030; + case 40: goto tr3340; + case 41: goto tr3341; + case 1034: goto tr3343; + case 1083: goto tr3344; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr3342; + goto tr1885; +tr3342: + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st1031; +tr3348: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st1031; +st1031: + if ( ++p == pe ) + goto _test_eof1031; +case 1031: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3345; + case 32: goto tr3345; + case 40: goto tr3346; + case 41: goto tr3347; + case 1034: goto tr3349; + case 1083: goto tr3350; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr3348; + goto tr1885; +tr3352: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st1032; +tr3353: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st1032; +tr3356: + { + s->line_counter++; + } + goto st1032; +tr3391: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st1032; +tr3345: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + goto st1032; +tr3346: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st1032; +tr3347: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st1032; +tr3349: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st1032; +st1032: + if ( ++p == pe ) + goto _test_eof1032; +case 1032: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st1032; + case 32: goto st1032; + case 40: goto tr3352; + case 41: goto tr3353; + case 45: goto tr3354; + case 1034: goto tr3356; + case 1083: goto tr3357; + } + if ( _widec < 65 ) { + if ( 48 <= _widec && _widec <= 57 ) + goto tr3355; + } else if ( _widec > 70 ) { + if ( 97 <= _widec && _widec <= 102 ) + goto tr3355; + } else + goto tr3355; + goto tr2556; +tr3354: + { + if (rdata_tail <= rdata_stop) { + s->item_length_location = rdata_tail++; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + goto st1033; +st1033: + if ( ++p == pe ) + goto _test_eof1033; +case 1033: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3358; + case 32: goto tr3358; + case 40: goto tr3359; + case 41: goto tr3360; + case 1034: goto tr3361; + case 1083: goto tr3362; + } + goto tr2556; +tr3365: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st1034; +tr3366: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st1034; +tr3368: + { + s->line_counter++; + } + goto st1034; +tr3387: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st1034; +tr3358: + { + s->item_length = rdata_tail - s->item_length_location - 1; + + if (s->item_length <= MAX_ITEM_LENGTH) { + *(s->item_length_location) = (uint8_t)(s->item_length); + } else { + WARN(ZS_ITEM_OVERFLOW); + p--; {goto st268;} + } + } + goto st1034; +tr3359: + { + s->item_length = rdata_tail - s->item_length_location - 1; + + if (s->item_length <= MAX_ITEM_LENGTH) { + *(s->item_length_location) = (uint8_t)(s->item_length); + } else { + WARN(ZS_ITEM_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st1034; +tr3360: + { + s->item_length = rdata_tail - s->item_length_location - 1; + + if (s->item_length <= MAX_ITEM_LENGTH) { + *(s->item_length_location) = (uint8_t)(s->item_length); + } else { + WARN(ZS_ITEM_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st1034; +tr3361: + { + s->item_length = rdata_tail - s->item_length_location - 1; + + if (s->item_length <= MAX_ITEM_LENGTH) { + *(s->item_length_location) = (uint8_t)(s->item_length); + } else { + WARN(ZS_ITEM_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st1034; +st1034: + if ( ++p == pe ) + goto _test_eof1034; +case 1034: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st1034; + case 32: goto st1034; + case 40: goto tr3365; + case 41: goto tr3366; + case 1034: goto tr3368; + case 1083: goto tr3369; + } + if ( _widec < 65 ) { + if ( 48 <= _widec && _widec <= 57 ) + goto tr3367; + } else if ( _widec > 86 ) { + if ( 97 <= _widec && _widec <= 118 ) + goto tr3367; + } else + goto tr3367; + goto tr3363; +tr3367: + { + if (rdata_tail <= rdata_stop) { + s->item_length_location = rdata_tail++; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + { + if (rdata_tail <= rdata_stop) { + *rdata_tail = first_base32hex_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + goto st1035; +tr3382: + { + if (rdata_tail <= rdata_stop) { + *rdata_tail = first_base32hex_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + goto st1035; +st1035: + if ( ++p == pe ) + goto _test_eof1035; +case 1035: + if ( (*p) < 65 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr3370; + } else if ( (*p) > 86 ) { + if ( 97 <= (*p) && (*p) <= 118 ) + goto tr3370; + } else + goto tr3370; + goto tr3363; +tr3370: + { + *(rdata_tail++) += second_left_base32hex_to_num[(uint8_t)(*p)]; + + if (rdata_tail <= rdata_stop) { + *rdata_tail = second_right_base32hex_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + goto st1036; +st1036: + if ( ++p == pe ) + goto _test_eof1036; +case 1036: + if ( (*p) == 61 ) + goto st1047; + if ( (*p) < 65 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr3371; + } else if ( (*p) > 86 ) { + if ( 97 <= (*p) && (*p) <= 118 ) + goto tr3371; + } else + goto tr3371; + goto tr3363; +tr3371: + { + *rdata_tail += third_base32hex_to_num[(uint8_t)(*p)]; + } + goto st1037; +st1037: + if ( ++p == pe ) + goto _test_eof1037; +case 1037: + if ( (*p) < 65 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr3373; + } else if ( (*p) > 86 ) { + if ( 97 <= (*p) && (*p) <= 118 ) + goto tr3373; + } else + goto tr3373; + goto tr3363; +tr3373: + { + *(rdata_tail++) += fourth_left_base32hex_to_num[(uint8_t)(*p)]; + + if (rdata_tail <= rdata_stop) { + *rdata_tail = fourth_right_base32hex_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + goto st1038; +st1038: + if ( ++p == pe ) + goto _test_eof1038; +case 1038: + if ( (*p) == 61 ) + goto st1046; + if ( (*p) < 65 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr3374; + } else if ( (*p) > 86 ) { + if ( 97 <= (*p) && (*p) <= 118 ) + goto tr3374; + } else + goto tr3374; + goto tr3363; +tr3374: + { + *(rdata_tail++) += fifth_left_base32hex_to_num[(uint8_t)(*p)]; + + if (rdata_tail <= rdata_stop) { + *rdata_tail = fifth_right_base32hex_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + goto st1039; +st1039: + if ( ++p == pe ) + goto _test_eof1039; +case 1039: + if ( (*p) == 61 ) + goto st1044; + if ( (*p) < 65 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr3376; + } else if ( (*p) > 86 ) { + if ( 97 <= (*p) && (*p) <= 118 ) + goto tr3376; + } else + goto tr3376; + goto tr3363; +tr3376: + { + *rdata_tail += sixth_base32hex_to_num[(uint8_t)(*p)]; + } + goto st1040; +st1040: + if ( ++p == pe ) + goto _test_eof1040; +case 1040: + if ( (*p) < 65 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr3378; + } else if ( (*p) > 86 ) { + if ( 97 <= (*p) && (*p) <= 118 ) + goto tr3378; + } else + goto tr3378; + goto tr3363; +tr3378: + { + *(rdata_tail++) += seventh_left_base32hex_to_num[(uint8_t)(*p)]; + + if (rdata_tail <= rdata_stop) { + *rdata_tail = seventh_right_base32hex_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + goto st1041; +st1041: + if ( ++p == pe ) + goto _test_eof1041; +case 1041: + if ( (*p) == 61 ) + goto st1042; + if ( (*p) < 65 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr3379; + } else if ( (*p) > 86 ) { + if ( 97 <= (*p) && (*p) <= 118 ) + goto tr3379; + } else + goto tr3379; + goto tr3363; +tr3379: + { + *(rdata_tail++) += eighth_base32hex_to_num[(uint8_t)(*p)]; + } + goto st1042; +st1042: + if ( ++p == pe ) + goto _test_eof1042; +case 1042: + switch( (*p) ) { + case 32: goto tr3381; + case 59: goto tr3381; + } + if ( (*p) < 48 ) { + if ( (*p) > 10 ) { + if ( 40 <= (*p) && (*p) <= 41 ) + goto tr3381; + } else if ( (*p) >= 9 ) + goto tr3381; + } else if ( (*p) > 57 ) { + if ( (*p) > 86 ) { + if ( 97 <= (*p) && (*p) <= 118 ) + goto tr3382; + } else if ( (*p) >= 65 ) + goto tr3382; + } else + goto tr3382; + goto tr3363; +tr3381: + { + s->item_length = rdata_tail - s->item_length_location - 1; + + if (s->item_length <= MAX_ITEM_LENGTH) { + *(s->item_length_location) = (uint8_t)(s->item_length); + } else { + WARN(ZS_ITEM_OVERFLOW); + p--; {goto st268;} + } + } + { p--; {stack[top++] = 1043;goto st336;} } + goto st1043; +st1043: + if ( ++p == pe ) + goto _test_eof1043; +case 1043: + switch( (*p) ) { + case 32: goto tr3383; + case 59: goto tr3383; + } + if ( (*p) > 10 ) { + if ( 40 <= (*p) && (*p) <= 41 ) + goto tr3383; + } else if ( (*p) >= 9 ) + goto tr3383; + goto tr71; +tr3383: + { + p--; {cs = stack[--top];goto _again;} + } + goto st1203; +st1203: + if ( ++p == pe ) + goto _test_eof1203; +case 1203: + goto st0; +st1044: + if ( ++p == pe ) + goto _test_eof1044; +case 1044: + if ( (*p) == 61 ) + goto st1045; + goto tr3363; +st1045: + if ( ++p == pe ) + goto _test_eof1045; +case 1045: + if ( (*p) == 61 ) + goto st1042; + goto tr3363; +st1046: + if ( ++p == pe ) + goto _test_eof1046; +case 1046: + if ( (*p) == 61 ) + goto st1044; + goto tr3363; +st1047: + if ( ++p == pe ) + goto _test_eof1047; +case 1047: + if ( (*p) == 61 ) + goto st1048; + goto tr3363; +st1048: + if ( ++p == pe ) + goto _test_eof1048; +case 1048: + if ( (*p) == 61 ) + goto st1046; + goto tr3363; +tr3369: + { + s->buffer_length = 0; + } + goto st1049; +tr3386: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st1049; +tr3362: + { + s->item_length = rdata_tail - s->item_length_location - 1; + + if (s->item_length <= MAX_ITEM_LENGTH) { + *(s->item_length_location) = (uint8_t)(s->item_length); + } else { + WARN(ZS_ITEM_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st1049; +st1049: + if ( ++p == pe ) + goto _test_eof1049; +case 1049: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr3387; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr3386; + goto tr71; +tr3389: + { + if (rdata_tail <= rdata_stop) { + *rdata_tail = first_hex_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + goto st1050; +tr3355: + { + if (rdata_tail <= rdata_stop) { + s->item_length_location = rdata_tail++; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + { + if (rdata_tail <= rdata_stop) { + *rdata_tail = first_hex_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + goto st1050; +st1050: + if ( ++p == pe ) + goto _test_eof1050; +case 1050: + if ( (*p) < 65 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr3388; + } else if ( (*p) > 70 ) { + if ( 97 <= (*p) && (*p) <= 102 ) + goto tr3388; + } else + goto tr3388; + goto tr2556; +tr3388: + { + *rdata_tail += second_hex_to_num[(uint8_t)(*p)]; + rdata_tail++; + } + goto st1051; +st1051: + if ( ++p == pe ) + goto _test_eof1051; +case 1051: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3358; + case 32: goto tr3358; + case 40: goto tr3359; + case 41: goto tr3360; + case 1034: goto tr3361; + case 1083: goto tr3362; + } + if ( _widec < 65 ) { + if ( 48 <= _widec && _widec <= 57 ) + goto tr3389; + } else if ( _widec > 70 ) { + if ( 97 <= _widec && _widec <= 102 ) + goto tr3389; + } else + goto tr3389; + goto tr2556; +tr3357: + { + s->buffer_length = 0; + } + goto st1052; +tr3390: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st1052; +tr3350: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st1052; +st1052: + if ( ++p == pe ) + goto _test_eof1052; +case 1052: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr3391; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr3390; + goto tr71; +tr3344: + { + s->buffer_length = 0; + } + goto st1053; +tr3392: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st1053; +tr3338: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st1053; +st1053: + if ( ++p == pe ) + goto _test_eof1053; +case 1053: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr3393; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr3392; + goto tr71; +tr3332: + { + s->buffer_length = 0; + } + goto st1054; +tr3394: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st1054; +tr3326: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st1054; +st1054: + if ( ++p == pe ) + goto _test_eof1054; +case 1054: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr3395; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr3394; + goto tr71; +st1055: + if ( ++p == pe ) + goto _test_eof1055; +case 1055: + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr3396; + goto tr1885; +tr3396: + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st1056; +tr3400: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st1056; +st1056: + if ( ++p == pe ) + goto _test_eof1056; +case 1056: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3397; + case 32: goto tr3397; + case 40: goto tr3398; + case 41: goto tr3399; + case 1034: goto tr3401; + case 1083: goto tr3402; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr3400; + goto tr1885; +tr3404: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st1057; +tr3405: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st1057; +tr3407: + { + s->line_counter++; + } + goto st1057; +tr3442: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st1057; +tr3397: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + goto st1057; +tr3398: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st1057; +tr3399: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st1057; +tr3401: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st1057; +st1057: + if ( ++p == pe ) + goto _test_eof1057; +case 1057: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st1057; + case 32: goto st1057; + case 40: goto tr3404; + case 41: goto tr3405; + case 1034: goto tr3407; + case 1083: goto tr3408; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr3406; + goto tr1885; +tr3406: + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st1058; +tr3412: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st1058; +st1058: + if ( ++p == pe ) + goto _test_eof1058; +case 1058: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3409; + case 32: goto tr3409; + case 40: goto tr3410; + case 41: goto tr3411; + case 1034: goto tr3413; + case 1083: goto tr3414; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr3412; + goto tr1885; +tr3416: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st1059; +tr3417: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st1059; +tr3419: + { + s->line_counter++; + } + goto st1059; +tr3440: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st1059; +tr3409: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + goto st1059; +tr3410: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st1059; +tr3411: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st1059; +tr3413: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st1059; +st1059: + if ( ++p == pe ) + goto _test_eof1059; +case 1059: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st1059; + case 32: goto st1059; + case 40: goto tr3416; + case 41: goto tr3417; + case 1034: goto tr3419; + case 1083: goto tr3420; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr3418; + goto tr1885; +tr3418: + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st1060; +tr3424: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st1060; +st1060: + if ( ++p == pe ) + goto _test_eof1060; +case 1060: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3421; + case 32: goto tr3421; + case 40: goto tr3422; + case 41: goto tr3423; + case 1034: goto tr3425; + case 1083: goto tr3426; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr3424; + goto tr1885; +tr3428: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st1061; +tr3429: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st1061; +tr3432: + { + s->line_counter++; + } + goto st1061; +tr3438: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st1061; +tr3421: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + goto st1061; +tr3422: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st1061; +tr3423: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st1061; +tr3425: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st1061; +st1061: + if ( ++p == pe ) + goto _test_eof1061; +case 1061: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st1061; + case 32: goto st1061; + case 40: goto tr3428; + case 41: goto tr3429; + case 45: goto tr3430; + case 1034: goto tr3432; + case 1083: goto tr3433; + } + if ( _widec < 65 ) { + if ( 48 <= _widec && _widec <= 57 ) + goto tr3431; + } else if ( _widec > 70 ) { + if ( 97 <= _widec && _widec <= 102 ) + goto tr3431; + } else + goto tr3431; + goto tr2556; +tr3430: + { + if (rdata_tail <= rdata_stop) { + s->item_length_location = rdata_tail++; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + goto st1062; +st1062: + if ( ++p == pe ) + goto _test_eof1062; +case 1062: + switch( (*p) ) { + case 32: goto tr3434; + case 59: goto tr3434; + } + if ( (*p) > 10 ) { + if ( 40 <= (*p) && (*p) <= 41 ) + goto tr3434; + } else if ( (*p) >= 9 ) + goto tr3434; + goto tr2556; +tr3434: + { + s->item_length = rdata_tail - s->item_length_location - 1; + + if (s->item_length <= MAX_ITEM_LENGTH) { + *(s->item_length_location) = (uint8_t)(s->item_length); + } else { + WARN(ZS_ITEM_OVERFLOW); + p--; {goto st268;} + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1204; +st1204: + if ( ++p == pe ) + goto _test_eof1204; +case 1204: + goto st0; +tr3436: + { + if (rdata_tail <= rdata_stop) { + *rdata_tail = first_hex_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + goto st1063; +tr3431: + { + if (rdata_tail <= rdata_stop) { + s->item_length_location = rdata_tail++; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + { + if (rdata_tail <= rdata_stop) { + *rdata_tail = first_hex_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + goto st1063; +st1063: + if ( ++p == pe ) + goto _test_eof1063; +case 1063: + if ( (*p) < 65 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr3435; + } else if ( (*p) > 70 ) { + if ( 97 <= (*p) && (*p) <= 102 ) + goto tr3435; + } else + goto tr3435; + goto tr2556; +tr3435: + { + *rdata_tail += second_hex_to_num[(uint8_t)(*p)]; + rdata_tail++; + } + goto st1064; +st1064: + if ( ++p == pe ) + goto _test_eof1064; +case 1064: + switch( (*p) ) { + case 32: goto tr3434; + case 59: goto tr3434; + } + if ( (*p) < 48 ) { + if ( (*p) > 10 ) { + if ( 40 <= (*p) && (*p) <= 41 ) + goto tr3434; + } else if ( (*p) >= 9 ) + goto tr3434; + } else if ( (*p) > 57 ) { + if ( (*p) > 70 ) { + if ( 97 <= (*p) && (*p) <= 102 ) + goto tr3436; + } else if ( (*p) >= 65 ) + goto tr3436; + } else + goto tr3436; + goto tr2556; +tr3433: + { + s->buffer_length = 0; + } + goto st1065; +tr3437: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st1065; +tr3426: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st1065; +st1065: + if ( ++p == pe ) + goto _test_eof1065; +case 1065: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr3438; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr3437; + goto tr71; +tr3420: + { + s->buffer_length = 0; + } + goto st1066; +tr3439: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st1066; +tr3414: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st1066; +st1066: + if ( ++p == pe ) + goto _test_eof1066; +case 1066: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr3440; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr3439; + goto tr71; +tr3408: + { + s->buffer_length = 0; + } + goto st1067; +tr3441: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st1067; +tr3402: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st1067; +st1067: + if ( ++p == pe ) + goto _test_eof1067; +case 1067: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr3442; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr3441; + goto tr71; +st1068: + if ( ++p == pe ) + goto _test_eof1068; +case 1068: + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr3443; + goto tr1885; +tr3443: + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st1069; +tr3447: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st1069; +st1069: + if ( ++p == pe ) + goto _test_eof1069; +case 1069: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3444; + case 32: goto tr3444; + case 40: goto tr3445; + case 41: goto tr3446; + case 1034: goto tr3448; + case 1083: goto tr3449; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr3447; + goto tr1885; +tr3451: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st1070; +tr3452: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st1070; +tr3454: + { + s->line_counter++; + } + goto st1070; +tr3496: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st1070; +tr3444: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + goto st1070; +tr3445: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st1070; +tr3446: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st1070; +tr3448: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st1070; +st1070: + if ( ++p == pe ) + goto _test_eof1070; +case 1070: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st1070; + case 32: goto st1070; + case 40: goto tr3451; + case 41: goto tr3452; + case 1034: goto tr3454; + case 1083: goto tr3455; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr3453; + goto tr1885; +tr3453: + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st1071; +tr3459: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st1071; +st1071: + if ( ++p == pe ) + goto _test_eof1071; +case 1071: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3456; + case 32: goto tr3456; + case 40: goto tr3457; + case 41: goto tr3458; + case 1034: goto tr3460; + case 1083: goto tr3461; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr3459; + goto tr1885; +tr3463: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st1072; +tr3464: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st1072; +tr3466: + { + s->line_counter++; + } + goto st1072; +tr3494: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st1072; +tr3456: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + goto st1072; +tr3457: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st1072; +tr3458: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st1072; +tr3460: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st1072; +st1072: + if ( ++p == pe ) + goto _test_eof1072; +case 1072: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st1072; + case 32: goto st1072; + case 40: goto tr3463; + case 41: goto tr3464; + case 1034: goto tr3466; + case 1083: goto tr3467; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr3465; + goto tr1885; +tr3465: + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st1073; +tr3471: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st1073; +st1073: + if ( ++p == pe ) + goto _test_eof1073; +case 1073: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3468; + case 32: goto tr3468; + case 40: goto tr3469; + case 41: goto tr3470; + case 1034: goto tr3472; + case 1083: goto tr3473; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr3471; + goto tr1885; +tr3475: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st1074; +tr3476: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st1074; +tr3478: + { + s->line_counter++; + } + goto st1074; +tr3492: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st1074; +tr3468: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + goto st1074; +tr3469: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st1074; +tr3470: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st1074; +tr3472: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st1074; +st1074: + if ( ++p == pe ) + goto _test_eof1074; +case 1074: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st1074; + case 32: goto st1074; + case 40: goto tr3475; + case 41: goto tr3476; + case 1034: goto tr3478; + case 1083: goto tr3479; + } + if ( _widec < 65 ) { + if ( 48 <= _widec && _widec <= 57 ) + goto tr3477; + } else if ( _widec > 70 ) { + if ( 97 <= _widec && _widec <= 102 ) + goto tr3477; + } else + goto tr3477; + goto tr2556; +tr3477: + { + if (rdata_tail <= rdata_stop) { + *rdata_tail = first_hex_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + goto st1075; +st1075: + if ( ++p == pe ) + goto _test_eof1075; +case 1075: + if ( (*p) < 65 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr3480; + } else if ( (*p) > 70 ) { + if ( 97 <= (*p) && (*p) <= 102 ) + goto tr3480; + } else + goto tr3480; + goto tr2556; +tr3482: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st1076; +tr3483: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st1076; +tr3484: + { + s->line_counter++; + } + goto st1076; +tr3490: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st1076; +tr3480: + { + *rdata_tail += second_hex_to_num[(uint8_t)(*p)]; + rdata_tail++; + } + goto st1076; +st1076: + if ( ++p == pe ) + goto _test_eof1076; +case 1076: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto st1076; + case 32: goto st1076; + case 40: goto tr3482; + case 41: goto tr3483; + case 2058: goto tr3484; + case 2107: goto tr3485; + case 2314: goto tr3486; + case 2363: goto tr3486; + case 2570: goto tr3487; + case 2619: goto tr3488; + } + if ( _widec < 65 ) { + if ( 48 <= _widec && _widec <= 57 ) + goto tr3477; + } else if ( _widec > 70 ) { + if ( 97 <= _widec && _widec <= 102 ) + goto tr3477; + } else + goto tr3477; + goto tr2556; +tr3485: + { + s->buffer_length = 0; + } + goto st1077; +tr3489: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st1077; +st1077: + if ( ++p == pe ) + goto _test_eof1077; +case 1077: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr3490; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr3489; + goto tr2556; +tr3486: + { + p--; {cs = stack[--top];goto _again;} + } + goto st1205; +st1205: + if ( ++p == pe ) + goto _test_eof1205; +case 1205: + goto st0; +tr3487: + { + s->line_counter++; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1206; +st1206: + if ( ++p == pe ) + goto _test_eof1206; +case 1206: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + } + switch( _widec ) { + case 9: goto st1076; + case 32: goto st1076; + case 40: goto tr3482; + case 41: goto tr3483; + case 2058: goto tr3484; + case 2107: goto tr3485; + case 2314: goto tr3486; + case 2363: goto tr3486; + case 2570: goto tr3487; + case 2619: goto tr3488; + } + if ( _widec < 65 ) { + if ( 48 <= _widec && _widec <= 57 ) + goto tr3477; + } else if ( _widec > 70 ) { + if ( 97 <= _widec && _widec <= 102 ) + goto tr3477; + } else + goto tr3477; + goto tr2556; +tr3488: + { + s->buffer_length = 0; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1207; +st1207: + if ( ++p == pe ) + goto _test_eof1207; +case 1207: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr3490; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr3489; + goto tr2556; +tr3479: + { + s->buffer_length = 0; + } + goto st1078; +tr3491: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st1078; +tr3473: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st1078; +st1078: + if ( ++p == pe ) + goto _test_eof1078; +case 1078: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr3492; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr3491; + goto tr71; +tr3467: + { + s->buffer_length = 0; + } + goto st1079; +tr3493: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st1079; +tr3461: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st1079; +st1079: + if ( ++p == pe ) + goto _test_eof1079; +case 1079: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr3494; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr3493; + goto tr71; +tr3455: + { + s->buffer_length = 0; + } + goto st1080; +tr3495: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st1080; +tr3449: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st1080; +st1080: + if ( ++p == pe ) + goto _test_eof1080; +case 1080: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr3496; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr3495; + goto tr71; +st1081: + if ( ++p == pe ) + goto _test_eof1081; +case 1081: + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr3497; + goto tr1885; +tr3497: + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st1082; +tr3501: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st1082; +st1082: + if ( ++p == pe ) + goto _test_eof1082; +case 1082: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3498; + case 32: goto tr3498; + case 40: goto tr3499; + case 41: goto tr3500; + case 1034: goto tr3502; + case 1083: goto tr3503; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr3501; + goto tr1885; +tr3505: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st1083; +tr3506: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st1083; +tr3508: + { + s->line_counter++; + } + goto st1083; +tr3513: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st1083; +tr3498: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + goto st1083; +tr3499: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st1083; +tr3500: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st1083; +tr3502: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st1083; +st1083: + if ( ++p == pe ) + goto _test_eof1083; +case 1083: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st1083; + case 32: goto st1083; + case 40: goto tr3505; + case 41: goto tr3506; + case 46: goto tr3507; + case 1034: goto tr3508; + case 1083: goto tr3509; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr3507; + goto tr1862; +tr3507: + { + s->buffer_length = 0; + } + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + goto st1084; +tr3511: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + goto st1084; +st1084: + if ( ++p == pe ) + goto _test_eof1084; +case 1084: + switch( (*p) ) { + case 32: goto tr3510; + case 46: goto tr3511; + case 59: goto tr3510; + } + if ( (*p) < 40 ) { + if ( 9 <= (*p) && (*p) <= 10 ) + goto tr3510; + } else if ( (*p) > 41 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr3511; + } else + goto tr3510; + goto tr1862; +tr3510: + { + s->buffer[s->buffer_length] = 0; + + if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) { + WARN(ZS_BAD_IPV4); + p--; {goto st268;} + } + } + { + memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH); + rdata_tail += ZS_INET4_ADDR_LENGTH; + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1208; +st1208: + if ( ++p == pe ) + goto _test_eof1208; +case 1208: + goto st0; +tr3509: + { + s->buffer_length = 0; + } + goto st1085; +tr3512: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st1085; +tr3503: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st1085; +st1085: + if ( ++p == pe ) + goto _test_eof1085; +case 1085: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr3513; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr3512; + goto tr71; +st1086: + if ( ++p == pe ) + goto _test_eof1086; +case 1086: + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr3514; + goto tr1885; +tr3514: + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st1087; +tr3518: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st1087; +st1087: + if ( ++p == pe ) + goto _test_eof1087; +case 1087: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3515; + case 32: goto tr3515; + case 40: goto tr3516; + case 41: goto tr3517; + case 1034: goto tr3519; + case 1083: goto tr3520; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr3518; + goto tr1885; +tr3522: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st1088; +tr3523: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st1088; +tr3525: + { + s->line_counter++; + } + goto st1088; +tr3539: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st1088; +tr3515: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + goto st1088; +tr3516: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st1088; +tr3517: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st1088; +tr3519: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st1088; +st1088: + if ( ++p == pe ) + goto _test_eof1088; +case 1088: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st1088; + case 32: goto st1088; + case 40: goto tr3522; + case 41: goto tr3523; + case 1034: goto tr3525; + case 1083: goto tr3526; + } + if ( _widec < 65 ) { + if ( 48 <= _widec && _widec <= 57 ) + goto tr3524; + } else if ( _widec > 70 ) { + if ( 97 <= _widec && _widec <= 102 ) + goto tr3524; + } else + goto tr3524; + goto tr2556; +tr3524: + { + s->item_length = 0; + } + { + if (rdata_tail <= rdata_stop) { + *rdata_tail = first_hex_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + goto st1089; +st1089: + if ( ++p == pe ) + goto _test_eof1089; +case 1089: + if ( (*p) < 65 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr3527; + } else if ( (*p) > 70 ) { + if ( 97 <= (*p) && (*p) <= 102 ) + goto tr3527; + } else + goto tr3527; + goto tr2556; +tr3527: + { + *rdata_tail += second_hex_to_num[(uint8_t)(*p)]; + rdata_tail++; + } + goto st1090; +st1090: + if ( ++p == pe ) + goto _test_eof1090; +case 1090: + if ( (*p) < 65 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr3528; + } else if ( (*p) > 70 ) { + if ( 97 <= (*p) && (*p) <= 102 ) + goto tr3528; + } else + goto tr3528; + goto tr2556; +tr3528: + { + if (rdata_tail <= rdata_stop) { + *rdata_tail = first_hex_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + goto st1091; +st1091: + if ( ++p == pe ) + goto _test_eof1091; +case 1091: + if ( (*p) < 65 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr3529; + } else if ( (*p) > 70 ) { + if ( 97 <= (*p) && (*p) <= 102 ) + goto tr3529; + } else + goto tr3529; + goto tr2556; +tr3529: + { + *rdata_tail += second_hex_to_num[(uint8_t)(*p)]; + rdata_tail++; + } + goto st1092; +st1092: + if ( ++p == pe ) + goto _test_eof1092; +case 1092: + if ( (*p) == 58 ) + goto tr3531; + goto tr3530; +tr3531: + { + s->item_length++; + } + goto st1093; +st1093: + if ( ++p == pe ) + goto _test_eof1093; +case 1093: + if ( (*p) < 65 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr3532; + } else if ( (*p) > 70 ) { + if ( 97 <= (*p) && (*p) <= 102 ) + goto tr3532; + } else + goto tr3532; + goto tr2556; +tr3532: + { + if (rdata_tail <= rdata_stop) { + *rdata_tail = first_hex_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + goto st1094; +st1094: + if ( ++p == pe ) + goto _test_eof1094; +case 1094: + if ( (*p) < 65 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr3533; + } else if ( (*p) > 70 ) { + if ( 97 <= (*p) && (*p) <= 102 ) + goto tr3533; + } else + goto tr3533; + goto tr2556; +tr3533: + { + *rdata_tail += second_hex_to_num[(uint8_t)(*p)]; + rdata_tail++; + } + goto st1095; +st1095: + if ( ++p == pe ) + goto _test_eof1095; +case 1095: + if ( (*p) < 65 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr3534; + } else if ( (*p) > 70 ) { + if ( 97 <= (*p) && (*p) <= 102 ) + goto tr3534; + } else + goto tr3534; + goto tr2556; +tr3534: + { + if (rdata_tail <= rdata_stop) { + *rdata_tail = first_hex_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + goto st1096; +st1096: + if ( ++p == pe ) + goto _test_eof1096; +case 1096: + if ( (*p) < 65 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr3535; + } else if ( (*p) > 70 ) { + if ( 97 <= (*p) && (*p) <= 102 ) + goto tr3535; + } else + goto tr3535; + goto tr2556; +tr3535: + { + *rdata_tail += second_hex_to_num[(uint8_t)(*p)]; + rdata_tail++; + } + goto st1097; +st1097: + if ( ++p == pe ) + goto _test_eof1097; +case 1097: + switch( (*p) ) { + case 32: goto tr3537; + case 58: goto tr3531; + case 59: goto tr3537; + } + if ( (*p) > 10 ) { + if ( 40 <= (*p) && (*p) <= 41 ) + goto tr3537; + } else if ( (*p) >= 9 ) + goto tr3537; + goto tr3536; +tr3537: + { + s->item_length++; + } + { + if (s->item_length != 4) { + WARN(ZS_BAD_L64_LENGTH); + p--; {goto st268;} + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1209; +st1209: + if ( ++p == pe ) + goto _test_eof1209; +case 1209: + goto st0; +tr3526: + { + s->buffer_length = 0; + } + goto st1098; +tr3538: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st1098; +tr3520: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st1098; +st1098: + if ( ++p == pe ) + goto _test_eof1098; +case 1098: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr3539; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr3538; + goto tr71; +st1099: + if ( ++p == pe ) + goto _test_eof1099; +case 1099: + if ( (*p) < 65 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr3540; + } else if ( (*p) > 70 ) { + if ( 97 <= (*p) && (*p) <= 102 ) + goto tr3540; + } else + goto tr3540; + goto tr2556; +tr3540: + { + s->item_length = 0; + } + { + if (rdata_tail <= rdata_stop) { + *rdata_tail = first_hex_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + goto st1100; +st1100: + if ( ++p == pe ) + goto _test_eof1100; +case 1100: + if ( (*p) < 65 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr3541; + } else if ( (*p) > 70 ) { + if ( 97 <= (*p) && (*p) <= 102 ) + goto tr3541; + } else + goto tr3541; + goto tr2556; +tr3541: + { + *rdata_tail += second_hex_to_num[(uint8_t)(*p)]; + rdata_tail++; + } + goto st1101; +st1101: + if ( ++p == pe ) + goto _test_eof1101; +case 1101: + if ( (*p) == 45 ) + goto tr3543; + goto tr3542; +tr3543: + { + s->item_length++; + } + goto st1102; +st1102: + if ( ++p == pe ) + goto _test_eof1102; +case 1102: + if ( (*p) < 65 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr3544; + } else if ( (*p) > 70 ) { + if ( 97 <= (*p) && (*p) <= 102 ) + goto tr3544; + } else + goto tr3544; + goto tr2556; +tr3544: + { + if (rdata_tail <= rdata_stop) { + *rdata_tail = first_hex_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + goto st1103; +st1103: + if ( ++p == pe ) + goto _test_eof1103; +case 1103: + if ( (*p) < 65 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr3545; + } else if ( (*p) > 70 ) { + if ( 97 <= (*p) && (*p) <= 102 ) + goto tr3545; + } else + goto tr3545; + goto tr2556; +tr3545: + { + *rdata_tail += second_hex_to_num[(uint8_t)(*p)]; + rdata_tail++; + } + goto st1104; +st1104: + if ( ++p == pe ) + goto _test_eof1104; +case 1104: + switch( (*p) ) { + case 32: goto tr3546; + case 45: goto tr3543; + case 59: goto tr3546; + } + if ( (*p) > 10 ) { + if ( 40 <= (*p) && (*p) <= 41 ) + goto tr3546; + } else if ( (*p) >= 9 ) + goto tr3546; + goto tr3542; +tr3546: + { + s->item_length++; + } + { + if (s->item_length != 6) { + WARN(ZS_BAD_EUI_LENGTH); + p--; {goto st268;} + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1210; +st1210: + if ( ++p == pe ) + goto _test_eof1210; +case 1210: + goto st0; +st1105: + if ( ++p == pe ) + goto _test_eof1105; +case 1105: + if ( (*p) < 65 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr3547; + } else if ( (*p) > 70 ) { + if ( 97 <= (*p) && (*p) <= 102 ) + goto tr3547; + } else + goto tr3547; + goto tr2556; +tr3547: + { + s->item_length = 0; + } + { + if (rdata_tail <= rdata_stop) { + *rdata_tail = first_hex_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + goto st1106; +st1106: + if ( ++p == pe ) + goto _test_eof1106; +case 1106: + if ( (*p) < 65 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr3548; + } else if ( (*p) > 70 ) { + if ( 97 <= (*p) && (*p) <= 102 ) + goto tr3548; + } else + goto tr3548; + goto tr2556; +tr3548: + { + *rdata_tail += second_hex_to_num[(uint8_t)(*p)]; + rdata_tail++; + } + goto st1107; +st1107: + if ( ++p == pe ) + goto _test_eof1107; +case 1107: + if ( (*p) == 45 ) + goto tr3549; + goto tr3542; +tr3549: + { + s->item_length++; + } + goto st1108; +st1108: + if ( ++p == pe ) + goto _test_eof1108; +case 1108: + if ( (*p) < 65 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr3550; + } else if ( (*p) > 70 ) { + if ( 97 <= (*p) && (*p) <= 102 ) + goto tr3550; + } else + goto tr3550; + goto tr2556; +tr3550: + { + if (rdata_tail <= rdata_stop) { + *rdata_tail = first_hex_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + goto st1109; +st1109: + if ( ++p == pe ) + goto _test_eof1109; +case 1109: + if ( (*p) < 65 ) { + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr3551; + } else if ( (*p) > 70 ) { + if ( 97 <= (*p) && (*p) <= 102 ) + goto tr3551; + } else + goto tr3551; + goto tr2556; +tr3551: + { + *rdata_tail += second_hex_to_num[(uint8_t)(*p)]; + rdata_tail++; + } + goto st1110; +st1110: + if ( ++p == pe ) + goto _test_eof1110; +case 1110: + switch( (*p) ) { + case 32: goto tr3552; + case 45: goto tr3549; + case 59: goto tr3552; + } + if ( (*p) > 10 ) { + if ( 40 <= (*p) && (*p) <= 41 ) + goto tr3552; + } else if ( (*p) >= 9 ) + goto tr3552; + goto tr3542; +tr3552: + { + s->item_length++; + } + { + if (s->item_length != 8) { + WARN(ZS_BAD_EUI_LENGTH); + p--; {goto st268;} + } + } + { + p--; {cs = stack[--top];goto _again;} + } + goto st1211; +st1211: + if ( ++p == pe ) + goto _test_eof1211; +case 1211: + goto st0; +st1111: + if ( ++p == pe ) + goto _test_eof1111; +case 1111: + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr3553; + goto tr1885; +tr3553: + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st1112; +tr3557: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st1112; +st1112: + if ( ++p == pe ) + goto _test_eof1112; +case 1112: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3554; + case 32: goto tr3554; + case 40: goto tr3555; + case 41: goto tr3556; + case 1034: goto tr3558; + case 1083: goto tr3559; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr3557; + goto tr1885; +tr3561: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st1113; +tr3562: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st1113; +tr3564: + { + s->line_counter++; + } + goto st1113; +tr3582: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st1113; +tr3554: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + goto st1113; +tr3555: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st1113; +tr3556: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st1113; +tr3558: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st1113; +st1113: + if ( ++p == pe ) + goto _test_eof1113; +case 1113: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st1113; + case 32: goto st1113; + case 40: goto tr3561; + case 41: goto tr3562; + case 1034: goto tr3564; + case 1083: goto tr3565; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr3563; + goto tr1885; +tr3563: + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st1114; +tr3569: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st1114; +st1114: + if ( ++p == pe ) + goto _test_eof1114; +case 1114: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3566; + case 32: goto tr3566; + case 40: goto tr3567; + case 41: goto tr3568; + case 1034: goto tr3570; + case 1083: goto tr3571; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr3569; + goto tr1885; +tr3574: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st1115; +tr3575: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st1115; +tr3576: + { + s->line_counter++; + } + goto st1115; +tr3580: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st1115; +tr3566: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + goto st1115; +tr3567: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st1115; +tr3568: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st1115; +tr3570: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st1115; +st1115: + if ( ++p == pe ) + goto _test_eof1115; +case 1115: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st1115; + case 32: goto st1115; + case 40: goto tr3574; + case 41: goto tr3575; + case 1034: goto tr3576; + case 1083: goto tr3577; + } + if ( _widec < 11 ) { + if ( _widec <= 8 ) + goto tr3572; + } else if ( _widec > 58 ) { + if ( 60 <= _widec ) + goto tr3572; + } else + goto tr3572; + goto tr71; +tr3572: + { p--; {stack[top++] = 1116;goto st279;} } + goto st1116; +st1116: + if ( ++p == pe ) + goto _test_eof1116; +case 1116: + switch( (*p) ) { + case 32: goto tr3578; + case 59: goto tr3578; + } + if ( (*p) > 10 ) { + if ( 40 <= (*p) && (*p) <= 41 ) + goto tr3578; + } else if ( (*p) >= 9 ) + goto tr3578; + goto tr71; +tr3578: + { + p--; {cs = stack[--top];goto _again;} + } + goto st1212; +st1212: + if ( ++p == pe ) + goto _test_eof1212; +case 1212: + goto st0; +tr3577: + { + s->buffer_length = 0; + } + goto st1117; +tr3579: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st1117; +tr3571: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st1117; +st1117: + if ( ++p == pe ) + goto _test_eof1117; +case 1117: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr3580; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr3579; + goto tr71; +tr3565: + { + s->buffer_length = 0; + } + goto st1118; +tr3581: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st1118; +tr3559: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st1118; +st1118: + if ( ++p == pe ) + goto _test_eof1118; +case 1118: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr3582; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr3581; + goto tr71; +st1119: + if ( ++p == pe ) + goto _test_eof1119; +case 1119: + if ( 48 <= (*p) && (*p) <= 57 ) + goto tr3583; + goto tr1885; +tr3583: + { + s->number64 = 0; + } + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st1120; +tr3587: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {goto st268;} + } + } + goto st1120; +st1120: + if ( ++p == pe ) + goto _test_eof1120; +case 1120: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3584; + case 32: goto tr3584; + case 40: goto tr3585; + case 41: goto tr3586; + case 1034: goto tr3588; + case 1083: goto tr3589; + } + if ( 48 <= _widec && _widec <= 57 ) + goto tr3587; + goto tr1885; +tr3592: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st1121; +tr3593: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st1121; +tr3594: + { + s->line_counter++; + } + goto st1121; +tr3611: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st1121; +tr3584: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + goto st1121; +tr3585: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st1121; +tr3586: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st1121; +tr3588: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st1121; +st1121: + if ( ++p == pe ) + goto _test_eof1121; +case 1121: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st1121; + case 32: goto st1121; + case 40: goto tr3592; + case 41: goto tr3593; + case 1034: goto tr3594; + case 1083: goto tr3595; + } + if ( _widec < 11 ) { + if ( _widec <= 8 ) + goto tr3590; + } else if ( _widec > 58 ) { + if ( 60 <= _widec ) + goto tr3590; + } else + goto tr3590; + goto tr71; +tr3590: + { + if (rdata_tail <= rdata_stop) { + s->item_length_location = rdata_tail++; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {goto st268;} + } + } + { p--; {stack[top++] = 1122;goto st279;} } + goto st1122; +st1122: + if ( ++p == pe ) + goto _test_eof1122; +case 1122: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto tr3596; + case 32: goto tr3596; + case 40: goto tr3597; + case 41: goto tr3598; + case 1034: goto tr3599; + case 1083: goto tr3600; + } + goto tr71; +tr3603: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st1123; +tr3604: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st1123; +tr3605: + { + s->line_counter++; + } + goto st1123; +tr3609: + { + s->buffer[s->buffer_length++] = 0; + } + { + s->line_counter++; + } + goto st1123; +tr3596: + { + s->item_length = rdata_tail - s->item_length_location - 1; + + if (s->item_length <= MAX_ITEM_LENGTH) { + *(s->item_length_location) = (uint8_t)(s->item_length); + } else { + WARN(ZS_ITEM_OVERFLOW); + p--; {goto st268;} + } + } + goto st1123; +tr3597: + { + s->item_length = rdata_tail - s->item_length_location - 1; + + if (s->item_length <= MAX_ITEM_LENGTH) { + *(s->item_length_location) = (uint8_t)(s->item_length); + } else { + WARN(ZS_ITEM_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = true; + } + goto st1123; +tr3598: + { + s->item_length = rdata_tail - s->item_length_location - 1; + + if (s->item_length <= MAX_ITEM_LENGTH) { + *(s->item_length_location) = (uint8_t)(s->item_length); + } else { + WARN(ZS_ITEM_OVERFLOW); + p--; {goto st268;} + } + } + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {goto st268;} + } + s->multiline = false; + } + goto st1123; +tr3599: + { + s->item_length = rdata_tail - s->item_length_location - 1; + + if (s->item_length <= MAX_ITEM_LENGTH) { + *(s->item_length_location) = (uint8_t)(s->item_length); + } else { + WARN(ZS_ITEM_OVERFLOW); + p--; {goto st268;} + } + } + { + s->line_counter++; + } + goto st1123; +st1123: + if ( ++p == pe ) + goto _test_eof1123; +case 1123: + _widec = (*p); + if ( (*p) > 10 ) { + if ( 59 <= (*p) && (*p) <= 59 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) >= 10 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + switch( _widec ) { + case 9: goto st1123; + case 32: goto st1123; + case 40: goto tr3603; + case 41: goto tr3604; + case 1034: goto tr3605; + case 1083: goto tr3606; + } + if ( _widec < 11 ) { + if ( _widec <= 8 ) + goto tr3601; + } else if ( _widec > 58 ) { + if ( 60 <= _widec ) + goto tr3601; + } else + goto tr3601; + goto tr71; +tr3601: + { p--; {stack[top++] = 1124;goto st279;} } + goto st1124; +st1124: + if ( ++p == pe ) + goto _test_eof1124; +case 1124: + switch( (*p) ) { + case 32: goto tr3607; + case 59: goto tr3607; + } + if ( (*p) > 10 ) { + if ( 40 <= (*p) && (*p) <= 41 ) + goto tr3607; + } else if ( (*p) >= 9 ) + goto tr3607; + goto tr71; +tr3607: + { + p--; {cs = stack[--top];goto _again;} + } + goto st1213; +st1213: + if ( ++p == pe ) + goto _test_eof1213; +case 1213: + goto st0; +tr3606: + { + s->buffer_length = 0; + } + goto st1125; +tr3608: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st1125; +tr3600: + { + s->item_length = rdata_tail - s->item_length_location - 1; + + if (s->item_length <= MAX_ITEM_LENGTH) { + *(s->item_length_location) = (uint8_t)(s->item_length); + } else { + WARN(ZS_ITEM_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st1125; +st1125: + if ( ++p == pe ) + goto _test_eof1125; +case 1125: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr3609; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr3608; + goto tr71; +tr3595: + { + s->buffer_length = 0; + } + goto st1126; +tr3610: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + goto st1126; +tr3589: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {goto st268;} + } + } + { + s->buffer_length = 0; + } + goto st1126; +st1126: + if ( ++p == pe ) + goto _test_eof1126; +case 1126: + _widec = (*p); + if ( (*p) < 10 ) { + if ( (*p) <= 9 ) { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else if ( (*p) > 10 ) { + if ( 11 <= (*p) ) + { _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + } else { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + } + if ( _widec == 1034 ) + goto tr3611; + if ( 896 <= _widec && _widec <= 1151 ) + goto tr3610; + goto tr71; + } + _test_eof1127: cs = 1127; goto _test_eof; + _test_eof1: cs = 1; goto _test_eof; + _test_eof2: cs = 2; goto _test_eof; + _test_eof3: cs = 3; goto _test_eof; + _test_eof4: cs = 4; goto _test_eof; + _test_eof5: cs = 5; goto _test_eof; + _test_eof6: cs = 6; goto _test_eof; + _test_eof7: cs = 7; goto _test_eof; + _test_eof8: cs = 8; goto _test_eof; + _test_eof9: cs = 9; goto _test_eof; + _test_eof10: cs = 10; goto _test_eof; + _test_eof11: cs = 11; goto _test_eof; + _test_eof12: cs = 12; goto _test_eof; + _test_eof13: cs = 13; goto _test_eof; + _test_eof1128: cs = 1128; goto _test_eof; + _test_eof14: cs = 14; goto _test_eof; + _test_eof15: cs = 15; goto _test_eof; + _test_eof16: cs = 16; goto _test_eof; + _test_eof17: cs = 17; goto _test_eof; + _test_eof18: cs = 18; goto _test_eof; + _test_eof19: cs = 19; goto _test_eof; + _test_eof20: cs = 20; goto _test_eof; + _test_eof21: cs = 21; goto _test_eof; + _test_eof22: cs = 22; goto _test_eof; + _test_eof23: cs = 23; goto _test_eof; + _test_eof24: cs = 24; goto _test_eof; + _test_eof25: cs = 25; goto _test_eof; + _test_eof26: cs = 26; goto _test_eof; + _test_eof27: cs = 27; goto _test_eof; + _test_eof28: cs = 28; goto _test_eof; + _test_eof29: cs = 29; goto _test_eof; + _test_eof30: cs = 30; goto _test_eof; + _test_eof31: cs = 31; goto _test_eof; + _test_eof32: cs = 32; goto _test_eof; + _test_eof33: cs = 33; goto _test_eof; + _test_eof34: cs = 34; goto _test_eof; + _test_eof35: cs = 35; goto _test_eof; + _test_eof36: cs = 36; goto _test_eof; + _test_eof37: cs = 37; goto _test_eof; + _test_eof38: cs = 38; goto _test_eof; + _test_eof39: cs = 39; goto _test_eof; + _test_eof40: cs = 40; goto _test_eof; + _test_eof41: cs = 41; goto _test_eof; + _test_eof42: cs = 42; goto _test_eof; + _test_eof43: cs = 43; goto _test_eof; + _test_eof44: cs = 44; goto _test_eof; + _test_eof45: cs = 45; goto _test_eof; + _test_eof46: cs = 46; goto _test_eof; + _test_eof47: cs = 47; goto _test_eof; + _test_eof48: cs = 48; goto _test_eof; + _test_eof49: cs = 49; goto _test_eof; + _test_eof50: cs = 50; goto _test_eof; + _test_eof51: cs = 51; goto _test_eof; + _test_eof52: cs = 52; goto _test_eof; + _test_eof53: cs = 53; goto _test_eof; + _test_eof54: cs = 54; goto _test_eof; + _test_eof55: cs = 55; goto _test_eof; + _test_eof56: cs = 56; goto _test_eof; + _test_eof57: cs = 57; goto _test_eof; + _test_eof58: cs = 58; goto _test_eof; + _test_eof59: cs = 59; goto _test_eof; + _test_eof60: cs = 60; goto _test_eof; + _test_eof61: cs = 61; goto _test_eof; + _test_eof62: cs = 62; goto _test_eof; + _test_eof63: cs = 63; goto _test_eof; + _test_eof64: cs = 64; goto _test_eof; + _test_eof65: cs = 65; goto _test_eof; + _test_eof66: cs = 66; goto _test_eof; + _test_eof67: cs = 67; goto _test_eof; + _test_eof68: cs = 68; goto _test_eof; + _test_eof69: cs = 69; goto _test_eof; + _test_eof70: cs = 70; goto _test_eof; + _test_eof71: cs = 71; goto _test_eof; + _test_eof72: cs = 72; goto _test_eof; + _test_eof73: cs = 73; goto _test_eof; + _test_eof74: cs = 74; goto _test_eof; + _test_eof75: cs = 75; goto _test_eof; + _test_eof76: cs = 76; goto _test_eof; + _test_eof77: cs = 77; goto _test_eof; + _test_eof78: cs = 78; goto _test_eof; + _test_eof79: cs = 79; goto _test_eof; + _test_eof80: cs = 80; goto _test_eof; + _test_eof81: cs = 81; goto _test_eof; + _test_eof82: cs = 82; goto _test_eof; + _test_eof83: cs = 83; goto _test_eof; + _test_eof84: cs = 84; goto _test_eof; + _test_eof85: cs = 85; goto _test_eof; + _test_eof86: cs = 86; goto _test_eof; + _test_eof87: cs = 87; goto _test_eof; + _test_eof88: cs = 88; goto _test_eof; + _test_eof89: cs = 89; goto _test_eof; + _test_eof90: cs = 90; goto _test_eof; + _test_eof91: cs = 91; goto _test_eof; + _test_eof92: cs = 92; goto _test_eof; + _test_eof93: cs = 93; goto _test_eof; + _test_eof94: cs = 94; goto _test_eof; + _test_eof95: cs = 95; goto _test_eof; + _test_eof96: cs = 96; goto _test_eof; + _test_eof97: cs = 97; goto _test_eof; + _test_eof98: cs = 98; goto _test_eof; + _test_eof99: cs = 99; goto _test_eof; + _test_eof100: cs = 100; goto _test_eof; + _test_eof101: cs = 101; goto _test_eof; + _test_eof102: cs = 102; goto _test_eof; + _test_eof103: cs = 103; goto _test_eof; + _test_eof104: cs = 104; goto _test_eof; + _test_eof105: cs = 105; goto _test_eof; + _test_eof106: cs = 106; goto _test_eof; + _test_eof107: cs = 107; goto _test_eof; + _test_eof108: cs = 108; goto _test_eof; + _test_eof109: cs = 109; goto _test_eof; + _test_eof110: cs = 110; goto _test_eof; + _test_eof111: cs = 111; goto _test_eof; + _test_eof112: cs = 112; goto _test_eof; + _test_eof113: cs = 113; goto _test_eof; + _test_eof114: cs = 114; goto _test_eof; + _test_eof115: cs = 115; goto _test_eof; + _test_eof116: cs = 116; goto _test_eof; + _test_eof117: cs = 117; goto _test_eof; + _test_eof118: cs = 118; goto _test_eof; + _test_eof119: cs = 119; goto _test_eof; + _test_eof120: cs = 120; goto _test_eof; + _test_eof121: cs = 121; goto _test_eof; + _test_eof122: cs = 122; goto _test_eof; + _test_eof123: cs = 123; goto _test_eof; + _test_eof124: cs = 124; goto _test_eof; + _test_eof125: cs = 125; goto _test_eof; + _test_eof126: cs = 126; goto _test_eof; + _test_eof127: cs = 127; goto _test_eof; + _test_eof128: cs = 128; goto _test_eof; + _test_eof129: cs = 129; goto _test_eof; + _test_eof130: cs = 130; goto _test_eof; + _test_eof131: cs = 131; goto _test_eof; + _test_eof132: cs = 132; goto _test_eof; + _test_eof133: cs = 133; goto _test_eof; + _test_eof134: cs = 134; goto _test_eof; + _test_eof135: cs = 135; goto _test_eof; + _test_eof136: cs = 136; goto _test_eof; + _test_eof137: cs = 137; goto _test_eof; + _test_eof138: cs = 138; goto _test_eof; + _test_eof139: cs = 139; goto _test_eof; + _test_eof140: cs = 140; goto _test_eof; + _test_eof141: cs = 141; goto _test_eof; + _test_eof1129: cs = 1129; goto _test_eof; + _test_eof142: cs = 142; goto _test_eof; + _test_eof143: cs = 143; goto _test_eof; + _test_eof144: cs = 144; goto _test_eof; + _test_eof145: cs = 145; goto _test_eof; + _test_eof146: cs = 146; goto _test_eof; + _test_eof147: cs = 147; goto _test_eof; + _test_eof148: cs = 148; goto _test_eof; + _test_eof149: cs = 149; goto _test_eof; + _test_eof150: cs = 150; goto _test_eof; + _test_eof151: cs = 151; goto _test_eof; + _test_eof1130: cs = 1130; goto _test_eof; + _test_eof152: cs = 152; goto _test_eof; + _test_eof153: cs = 153; goto _test_eof; + _test_eof154: cs = 154; goto _test_eof; + _test_eof155: cs = 155; goto _test_eof; + _test_eof156: cs = 156; goto _test_eof; + _test_eof157: cs = 157; goto _test_eof; + _test_eof158: cs = 158; goto _test_eof; + _test_eof159: cs = 159; goto _test_eof; + _test_eof1131: cs = 1131; goto _test_eof; + _test_eof160: cs = 160; goto _test_eof; + _test_eof161: cs = 161; goto _test_eof; + _test_eof162: cs = 162; goto _test_eof; + _test_eof1132: cs = 1132; goto _test_eof; + _test_eof163: cs = 163; goto _test_eof; + _test_eof164: cs = 164; goto _test_eof; + _test_eof165: cs = 165; goto _test_eof; + _test_eof166: cs = 166; goto _test_eof; + _test_eof167: cs = 167; goto _test_eof; + _test_eof168: cs = 168; goto _test_eof; + _test_eof169: cs = 169; goto _test_eof; + _test_eof170: cs = 170; goto _test_eof; + _test_eof171: cs = 171; goto _test_eof; + _test_eof172: cs = 172; goto _test_eof; + _test_eof173: cs = 173; goto _test_eof; + _test_eof1133: cs = 1133; goto _test_eof; + _test_eof174: cs = 174; goto _test_eof; + _test_eof175: cs = 175; goto _test_eof; + _test_eof176: cs = 176; goto _test_eof; + _test_eof177: cs = 177; goto _test_eof; + _test_eof1134: cs = 1134; goto _test_eof; + _test_eof178: cs = 178; goto _test_eof; + _test_eof179: cs = 179; goto _test_eof; + _test_eof180: cs = 180; goto _test_eof; + _test_eof181: cs = 181; goto _test_eof; + _test_eof182: cs = 182; goto _test_eof; + _test_eof183: cs = 183; goto _test_eof; + _test_eof184: cs = 184; goto _test_eof; + _test_eof185: cs = 185; goto _test_eof; + _test_eof186: cs = 186; goto _test_eof; + _test_eof187: cs = 187; goto _test_eof; + _test_eof188: cs = 188; goto _test_eof; + _test_eof189: cs = 189; goto _test_eof; + _test_eof190: cs = 190; goto _test_eof; + _test_eof191: cs = 191; goto _test_eof; + _test_eof192: cs = 192; goto _test_eof; + _test_eof193: cs = 193; goto _test_eof; + _test_eof1135: cs = 1135; goto _test_eof; + _test_eof194: cs = 194; goto _test_eof; + _test_eof195: cs = 195; goto _test_eof; + _test_eof196: cs = 196; goto _test_eof; + _test_eof197: cs = 197; goto _test_eof; + _test_eof198: cs = 198; goto _test_eof; + _test_eof199: cs = 199; goto _test_eof; + _test_eof200: cs = 200; goto _test_eof; + _test_eof201: cs = 201; goto _test_eof; + _test_eof202: cs = 202; goto _test_eof; + _test_eof203: cs = 203; goto _test_eof; + _test_eof204: cs = 204; goto _test_eof; + _test_eof205: cs = 205; goto _test_eof; + _test_eof206: cs = 206; goto _test_eof; + _test_eof207: cs = 207; goto _test_eof; + _test_eof208: cs = 208; goto _test_eof; + _test_eof209: cs = 209; goto _test_eof; + _test_eof1136: cs = 1136; goto _test_eof; + _test_eof210: cs = 210; goto _test_eof; + _test_eof211: cs = 211; goto _test_eof; + _test_eof212: cs = 212; goto _test_eof; + _test_eof213: cs = 213; goto _test_eof; + _test_eof214: cs = 214; goto _test_eof; + _test_eof215: cs = 215; goto _test_eof; + _test_eof216: cs = 216; goto _test_eof; + _test_eof217: cs = 217; goto _test_eof; + _test_eof218: cs = 218; goto _test_eof; + _test_eof219: cs = 219; goto _test_eof; + _test_eof220: cs = 220; goto _test_eof; + _test_eof221: cs = 221; goto _test_eof; + _test_eof222: cs = 222; goto _test_eof; + _test_eof223: cs = 223; goto _test_eof; + _test_eof224: cs = 224; goto _test_eof; + _test_eof225: cs = 225; goto _test_eof; + _test_eof226: cs = 226; goto _test_eof; + _test_eof227: cs = 227; goto _test_eof; + _test_eof228: cs = 228; goto _test_eof; + _test_eof229: cs = 229; goto _test_eof; + _test_eof230: cs = 230; goto _test_eof; + _test_eof231: cs = 231; goto _test_eof; + _test_eof232: cs = 232; goto _test_eof; + _test_eof233: cs = 233; goto _test_eof; + _test_eof234: cs = 234; goto _test_eof; + _test_eof235: cs = 235; goto _test_eof; + _test_eof236: cs = 236; goto _test_eof; + _test_eof237: cs = 237; goto _test_eof; + _test_eof238: cs = 238; goto _test_eof; + _test_eof239: cs = 239; goto _test_eof; + _test_eof240: cs = 240; goto _test_eof; + _test_eof241: cs = 241; goto _test_eof; + _test_eof242: cs = 242; goto _test_eof; + _test_eof243: cs = 243; goto _test_eof; + _test_eof244: cs = 244; goto _test_eof; + _test_eof245: cs = 245; goto _test_eof; + _test_eof246: cs = 246; goto _test_eof; + _test_eof247: cs = 247; goto _test_eof; + _test_eof248: cs = 248; goto _test_eof; + _test_eof249: cs = 249; goto _test_eof; + _test_eof250: cs = 250; goto _test_eof; + _test_eof251: cs = 251; goto _test_eof; + _test_eof252: cs = 252; goto _test_eof; + _test_eof253: cs = 253; goto _test_eof; + _test_eof254: cs = 254; goto _test_eof; + _test_eof255: cs = 255; goto _test_eof; + _test_eof256: cs = 256; goto _test_eof; + _test_eof257: cs = 257; goto _test_eof; + _test_eof258: cs = 258; goto _test_eof; + _test_eof259: cs = 259; goto _test_eof; + _test_eof260: cs = 260; goto _test_eof; + _test_eof261: cs = 261; goto _test_eof; + _test_eof262: cs = 262; goto _test_eof; + _test_eof263: cs = 263; goto _test_eof; + _test_eof264: cs = 264; goto _test_eof; + _test_eof265: cs = 265; goto _test_eof; + _test_eof266: cs = 266; goto _test_eof; + _test_eof267: cs = 267; goto _test_eof; + _test_eof268: cs = 268; goto _test_eof; + _test_eof269: cs = 269; goto _test_eof; + _test_eof1137: cs = 1137; goto _test_eof; + _test_eof270: cs = 270; goto _test_eof; + _test_eof271: cs = 271; goto _test_eof; + _test_eof1138: cs = 1138; goto _test_eof; + _test_eof272: cs = 272; goto _test_eof; + _test_eof273: cs = 273; goto _test_eof; + _test_eof274: cs = 274; goto _test_eof; + _test_eof275: cs = 275; goto _test_eof; + _test_eof276: cs = 276; goto _test_eof; + _test_eof277: cs = 277; goto _test_eof; + _test_eof278: cs = 278; goto _test_eof; + _test_eof279: cs = 279; goto _test_eof; + _test_eof280: cs = 280; goto _test_eof; + _test_eof1139: cs = 1139; goto _test_eof; + _test_eof1140: cs = 1140; goto _test_eof; + _test_eof281: cs = 281; goto _test_eof; + _test_eof282: cs = 282; goto _test_eof; + _test_eof283: cs = 283; goto _test_eof; + _test_eof284: cs = 284; goto _test_eof; + _test_eof285: cs = 285; goto _test_eof; + _test_eof286: cs = 286; goto _test_eof; + _test_eof287: cs = 287; goto _test_eof; + _test_eof288: cs = 288; goto _test_eof; + _test_eof289: cs = 289; goto _test_eof; + _test_eof290: cs = 290; goto _test_eof; + _test_eof291: cs = 291; goto _test_eof; + _test_eof292: cs = 292; goto _test_eof; + _test_eof293: cs = 293; goto _test_eof; + _test_eof294: cs = 294; goto _test_eof; + _test_eof1141: cs = 1141; goto _test_eof; + _test_eof295: cs = 295; goto _test_eof; + _test_eof296: cs = 296; goto _test_eof; + _test_eof297: cs = 297; goto _test_eof; + _test_eof298: cs = 298; goto _test_eof; + _test_eof299: cs = 299; goto _test_eof; + _test_eof300: cs = 300; goto _test_eof; + _test_eof301: cs = 301; goto _test_eof; + _test_eof302: cs = 302; goto _test_eof; + _test_eof303: cs = 303; goto _test_eof; + _test_eof304: cs = 304; goto _test_eof; + _test_eof1142: cs = 1142; goto _test_eof; + _test_eof305: cs = 305; goto _test_eof; + _test_eof306: cs = 306; goto _test_eof; + _test_eof307: cs = 307; goto _test_eof; + _test_eof308: cs = 308; goto _test_eof; + _test_eof309: cs = 309; goto _test_eof; + _test_eof310: cs = 310; goto _test_eof; + _test_eof311: cs = 311; goto _test_eof; + _test_eof312: cs = 312; goto _test_eof; + _test_eof313: cs = 313; goto _test_eof; + _test_eof314: cs = 314; goto _test_eof; + _test_eof315: cs = 315; goto _test_eof; + _test_eof316: cs = 316; goto _test_eof; + _test_eof317: cs = 317; goto _test_eof; + _test_eof318: cs = 318; goto _test_eof; + _test_eof1143: cs = 1143; goto _test_eof; + _test_eof319: cs = 319; goto _test_eof; + _test_eof320: cs = 320; goto _test_eof; + _test_eof321: cs = 321; goto _test_eof; + _test_eof322: cs = 322; goto _test_eof; + _test_eof323: cs = 323; goto _test_eof; + _test_eof324: cs = 324; goto _test_eof; + _test_eof325: cs = 325; goto _test_eof; + _test_eof1144: cs = 1144; goto _test_eof; + _test_eof326: cs = 326; goto _test_eof; + _test_eof327: cs = 327; goto _test_eof; + _test_eof328: cs = 328; goto _test_eof; + _test_eof329: cs = 329; goto _test_eof; + _test_eof330: cs = 330; goto _test_eof; + _test_eof331: cs = 331; goto _test_eof; + _test_eof332: cs = 332; goto _test_eof; + _test_eof333: cs = 333; goto _test_eof; + _test_eof334: cs = 334; goto _test_eof; + _test_eof1145: cs = 1145; goto _test_eof; + _test_eof1146: cs = 1146; goto _test_eof; + _test_eof1147: cs = 1147; goto _test_eof; + _test_eof335: cs = 335; goto _test_eof; + _test_eof336: cs = 336; goto _test_eof; + _test_eof337: cs = 337; goto _test_eof; + _test_eof338: cs = 338; goto _test_eof; + _test_eof339: cs = 339; goto _test_eof; + _test_eof340: cs = 340; goto _test_eof; + _test_eof341: cs = 341; goto _test_eof; + _test_eof342: cs = 342; goto _test_eof; + _test_eof1148: cs = 1148; goto _test_eof; + _test_eof1149: cs = 1149; goto _test_eof; + _test_eof343: cs = 343; goto _test_eof; + _test_eof344: cs = 344; goto _test_eof; + _test_eof345: cs = 345; goto _test_eof; + _test_eof1150: cs = 1150; goto _test_eof; + _test_eof346: cs = 346; goto _test_eof; + _test_eof347: cs = 347; goto _test_eof; + _test_eof348: cs = 348; goto _test_eof; + _test_eof349: cs = 349; goto _test_eof; + _test_eof350: cs = 350; goto _test_eof; + _test_eof351: cs = 351; goto _test_eof; + _test_eof352: cs = 352; goto _test_eof; + _test_eof353: cs = 353; goto _test_eof; + _test_eof354: cs = 354; goto _test_eof; + _test_eof355: cs = 355; goto _test_eof; + _test_eof356: cs = 356; goto _test_eof; + _test_eof357: cs = 357; goto _test_eof; + _test_eof358: cs = 358; goto _test_eof; + _test_eof359: cs = 359; goto _test_eof; + _test_eof360: cs = 360; goto _test_eof; + _test_eof361: cs = 361; goto _test_eof; + _test_eof362: cs = 362; goto _test_eof; + _test_eof363: cs = 363; goto _test_eof; + _test_eof364: cs = 364; goto _test_eof; + _test_eof365: cs = 365; goto _test_eof; + _test_eof366: cs = 366; goto _test_eof; + _test_eof367: cs = 367; goto _test_eof; + _test_eof368: cs = 368; goto _test_eof; + _test_eof369: cs = 369; goto _test_eof; + _test_eof370: cs = 370; goto _test_eof; + _test_eof371: cs = 371; goto _test_eof; + _test_eof372: cs = 372; goto _test_eof; + _test_eof373: cs = 373; goto _test_eof; + _test_eof374: cs = 374; goto _test_eof; + _test_eof375: cs = 375; goto _test_eof; + _test_eof376: cs = 376; goto _test_eof; + _test_eof377: cs = 377; goto _test_eof; + _test_eof378: cs = 378; goto _test_eof; + _test_eof379: cs = 379; goto _test_eof; + _test_eof380: cs = 380; goto _test_eof; + _test_eof381: cs = 381; goto _test_eof; + _test_eof382: cs = 382; goto _test_eof; + _test_eof383: cs = 383; goto _test_eof; + _test_eof384: cs = 384; goto _test_eof; + _test_eof385: cs = 385; goto _test_eof; + _test_eof386: cs = 386; goto _test_eof; + _test_eof387: cs = 387; goto _test_eof; + _test_eof388: cs = 388; goto _test_eof; + _test_eof389: cs = 389; goto _test_eof; + _test_eof390: cs = 390; goto _test_eof; + _test_eof391: cs = 391; goto _test_eof; + _test_eof392: cs = 392; goto _test_eof; + _test_eof393: cs = 393; goto _test_eof; + _test_eof394: cs = 394; goto _test_eof; + _test_eof395: cs = 395; goto _test_eof; + _test_eof396: cs = 396; goto _test_eof; + _test_eof397: cs = 397; goto _test_eof; + _test_eof398: cs = 398; goto _test_eof; + _test_eof399: cs = 399; goto _test_eof; + _test_eof400: cs = 400; goto _test_eof; + _test_eof401: cs = 401; goto _test_eof; + _test_eof402: cs = 402; goto _test_eof; + _test_eof403: cs = 403; goto _test_eof; + _test_eof404: cs = 404; goto _test_eof; + _test_eof405: cs = 405; goto _test_eof; + _test_eof406: cs = 406; goto _test_eof; + _test_eof407: cs = 407; goto _test_eof; + _test_eof408: cs = 408; goto _test_eof; + _test_eof409: cs = 409; goto _test_eof; + _test_eof410: cs = 410; goto _test_eof; + _test_eof411: cs = 411; goto _test_eof; + _test_eof412: cs = 412; goto _test_eof; + _test_eof413: cs = 413; goto _test_eof; + _test_eof414: cs = 414; goto _test_eof; + _test_eof415: cs = 415; goto _test_eof; + _test_eof416: cs = 416; goto _test_eof; + _test_eof417: cs = 417; goto _test_eof; + _test_eof418: cs = 418; goto _test_eof; + _test_eof419: cs = 419; goto _test_eof; + _test_eof420: cs = 420; goto _test_eof; + _test_eof421: cs = 421; goto _test_eof; + _test_eof422: cs = 422; goto _test_eof; + _test_eof423: cs = 423; goto _test_eof; + _test_eof424: cs = 424; goto _test_eof; + _test_eof425: cs = 425; goto _test_eof; + _test_eof426: cs = 426; goto _test_eof; + _test_eof427: cs = 427; goto _test_eof; + _test_eof428: cs = 428; goto _test_eof; + _test_eof429: cs = 429; goto _test_eof; + _test_eof430: cs = 430; goto _test_eof; + _test_eof431: cs = 431; goto _test_eof; + _test_eof432: cs = 432; goto _test_eof; + _test_eof433: cs = 433; goto _test_eof; + _test_eof434: cs = 434; goto _test_eof; + _test_eof435: cs = 435; goto _test_eof; + _test_eof436: cs = 436; goto _test_eof; + _test_eof437: cs = 437; goto _test_eof; + _test_eof438: cs = 438; goto _test_eof; + _test_eof439: cs = 439; goto _test_eof; + _test_eof440: cs = 440; goto _test_eof; + _test_eof441: cs = 441; goto _test_eof; + _test_eof442: cs = 442; goto _test_eof; + _test_eof443: cs = 443; goto _test_eof; + _test_eof444: cs = 444; goto _test_eof; + _test_eof445: cs = 445; goto _test_eof; + _test_eof446: cs = 446; goto _test_eof; + _test_eof447: cs = 447; goto _test_eof; + _test_eof448: cs = 448; goto _test_eof; + _test_eof449: cs = 449; goto _test_eof; + _test_eof450: cs = 450; goto _test_eof; + _test_eof451: cs = 451; goto _test_eof; + _test_eof452: cs = 452; goto _test_eof; + _test_eof453: cs = 453; goto _test_eof; + _test_eof454: cs = 454; goto _test_eof; + _test_eof455: cs = 455; goto _test_eof; + _test_eof456: cs = 456; goto _test_eof; + _test_eof457: cs = 457; goto _test_eof; + _test_eof458: cs = 458; goto _test_eof; + _test_eof459: cs = 459; goto _test_eof; + _test_eof460: cs = 460; goto _test_eof; + _test_eof461: cs = 461; goto _test_eof; + _test_eof462: cs = 462; goto _test_eof; + _test_eof463: cs = 463; goto _test_eof; + _test_eof464: cs = 464; goto _test_eof; + _test_eof465: cs = 465; goto _test_eof; + _test_eof466: cs = 466; goto _test_eof; + _test_eof467: cs = 467; goto _test_eof; + _test_eof468: cs = 468; goto _test_eof; + _test_eof469: cs = 469; goto _test_eof; + _test_eof470: cs = 470; goto _test_eof; + _test_eof471: cs = 471; goto _test_eof; + _test_eof472: cs = 472; goto _test_eof; + _test_eof473: cs = 473; goto _test_eof; + _test_eof474: cs = 474; goto _test_eof; + _test_eof1151: cs = 1151; goto _test_eof; + _test_eof1152: cs = 1152; goto _test_eof; + _test_eof1153: cs = 1153; goto _test_eof; + _test_eof475: cs = 475; goto _test_eof; + _test_eof476: cs = 476; goto _test_eof; + _test_eof477: cs = 477; goto _test_eof; + _test_eof478: cs = 478; goto _test_eof; + _test_eof479: cs = 479; goto _test_eof; + _test_eof1154: cs = 1154; goto _test_eof; + _test_eof480: cs = 480; goto _test_eof; + _test_eof481: cs = 481; goto _test_eof; + _test_eof482: cs = 482; goto _test_eof; + _test_eof483: cs = 483; goto _test_eof; + _test_eof1155: cs = 1155; goto _test_eof; + _test_eof1156: cs = 1156; goto _test_eof; + _test_eof1157: cs = 1157; goto _test_eof; + _test_eof484: cs = 484; goto _test_eof; + _test_eof485: cs = 485; goto _test_eof; + _test_eof1158: cs = 1158; goto _test_eof; + _test_eof486: cs = 486; goto _test_eof; + _test_eof487: cs = 487; goto _test_eof; + _test_eof488: cs = 488; goto _test_eof; + _test_eof1159: cs = 1159; goto _test_eof; + _test_eof489: cs = 489; goto _test_eof; + _test_eof490: cs = 490; goto _test_eof; + _test_eof491: cs = 491; goto _test_eof; + _test_eof492: cs = 492; goto _test_eof; + _test_eof493: cs = 493; goto _test_eof; + _test_eof494: cs = 494; goto _test_eof; + _test_eof495: cs = 495; goto _test_eof; + _test_eof496: cs = 496; goto _test_eof; + _test_eof497: cs = 497; goto _test_eof; + _test_eof498: cs = 498; goto _test_eof; + _test_eof499: cs = 499; goto _test_eof; + _test_eof500: cs = 500; goto _test_eof; + _test_eof501: cs = 501; goto _test_eof; + _test_eof502: cs = 502; goto _test_eof; + _test_eof503: cs = 503; goto _test_eof; + _test_eof504: cs = 504; goto _test_eof; + _test_eof505: cs = 505; goto _test_eof; + _test_eof506: cs = 506; goto _test_eof; + _test_eof507: cs = 507; goto _test_eof; + _test_eof508: cs = 508; goto _test_eof; + _test_eof509: cs = 509; goto _test_eof; + _test_eof510: cs = 510; goto _test_eof; + _test_eof511: cs = 511; goto _test_eof; + _test_eof512: cs = 512; goto _test_eof; + _test_eof513: cs = 513; goto _test_eof; + _test_eof514: cs = 514; goto _test_eof; + _test_eof515: cs = 515; goto _test_eof; + _test_eof516: cs = 516; goto _test_eof; + _test_eof517: cs = 517; goto _test_eof; + _test_eof518: cs = 518; goto _test_eof; + _test_eof519: cs = 519; goto _test_eof; + _test_eof520: cs = 520; goto _test_eof; + _test_eof521: cs = 521; goto _test_eof; + _test_eof522: cs = 522; goto _test_eof; + _test_eof523: cs = 523; goto _test_eof; + _test_eof524: cs = 524; goto _test_eof; + _test_eof525: cs = 525; goto _test_eof; + _test_eof526: cs = 526; goto _test_eof; + _test_eof527: cs = 527; goto _test_eof; + _test_eof528: cs = 528; goto _test_eof; + _test_eof529: cs = 529; goto _test_eof; + _test_eof530: cs = 530; goto _test_eof; + _test_eof531: cs = 531; goto _test_eof; + _test_eof532: cs = 532; goto _test_eof; + _test_eof533: cs = 533; goto _test_eof; + _test_eof534: cs = 534; goto _test_eof; + _test_eof535: cs = 535; goto _test_eof; + _test_eof536: cs = 536; goto _test_eof; + _test_eof537: cs = 537; goto _test_eof; + _test_eof538: cs = 538; goto _test_eof; + _test_eof539: cs = 539; goto _test_eof; + _test_eof540: cs = 540; goto _test_eof; + _test_eof541: cs = 541; goto _test_eof; + _test_eof542: cs = 542; goto _test_eof; + _test_eof543: cs = 543; goto _test_eof; + _test_eof544: cs = 544; goto _test_eof; + _test_eof545: cs = 545; goto _test_eof; + _test_eof546: cs = 546; goto _test_eof; + _test_eof547: cs = 547; goto _test_eof; + _test_eof548: cs = 548; goto _test_eof; + _test_eof549: cs = 549; goto _test_eof; + _test_eof550: cs = 550; goto _test_eof; + _test_eof551: cs = 551; goto _test_eof; + _test_eof552: cs = 552; goto _test_eof; + _test_eof553: cs = 553; goto _test_eof; + _test_eof554: cs = 554; goto _test_eof; + _test_eof555: cs = 555; goto _test_eof; + _test_eof556: cs = 556; goto _test_eof; + _test_eof557: cs = 557; goto _test_eof; + _test_eof558: cs = 558; goto _test_eof; + _test_eof559: cs = 559; goto _test_eof; + _test_eof560: cs = 560; goto _test_eof; + _test_eof561: cs = 561; goto _test_eof; + _test_eof562: cs = 562; goto _test_eof; + _test_eof563: cs = 563; goto _test_eof; + _test_eof564: cs = 564; goto _test_eof; + _test_eof565: cs = 565; goto _test_eof; + _test_eof566: cs = 566; goto _test_eof; + _test_eof567: cs = 567; goto _test_eof; + _test_eof568: cs = 568; goto _test_eof; + _test_eof569: cs = 569; goto _test_eof; + _test_eof570: cs = 570; goto _test_eof; + _test_eof571: cs = 571; goto _test_eof; + _test_eof572: cs = 572; goto _test_eof; + _test_eof573: cs = 573; goto _test_eof; + _test_eof574: cs = 574; goto _test_eof; + _test_eof575: cs = 575; goto _test_eof; + _test_eof576: cs = 576; goto _test_eof; + _test_eof577: cs = 577; goto _test_eof; + _test_eof578: cs = 578; goto _test_eof; + _test_eof579: cs = 579; goto _test_eof; + _test_eof580: cs = 580; goto _test_eof; + _test_eof581: cs = 581; goto _test_eof; + _test_eof582: cs = 582; goto _test_eof; + _test_eof583: cs = 583; goto _test_eof; + _test_eof584: cs = 584; goto _test_eof; + _test_eof585: cs = 585; goto _test_eof; + _test_eof586: cs = 586; goto _test_eof; + _test_eof587: cs = 587; goto _test_eof; + _test_eof588: cs = 588; goto _test_eof; + _test_eof589: cs = 589; goto _test_eof; + _test_eof590: cs = 590; goto _test_eof; + _test_eof591: cs = 591; goto _test_eof; + _test_eof592: cs = 592; goto _test_eof; + _test_eof1160: cs = 1160; goto _test_eof; + _test_eof593: cs = 593; goto _test_eof; + _test_eof594: cs = 594; goto _test_eof; + _test_eof595: cs = 595; goto _test_eof; + _test_eof596: cs = 596; goto _test_eof; + _test_eof597: cs = 597; goto _test_eof; + _test_eof598: cs = 598; goto _test_eof; + _test_eof599: cs = 599; goto _test_eof; + _test_eof600: cs = 600; goto _test_eof; + _test_eof601: cs = 601; goto _test_eof; + _test_eof602: cs = 602; goto _test_eof; + _test_eof603: cs = 603; goto _test_eof; + _test_eof604: cs = 604; goto _test_eof; + _test_eof605: cs = 605; goto _test_eof; + _test_eof606: cs = 606; goto _test_eof; + _test_eof607: cs = 607; goto _test_eof; + _test_eof608: cs = 608; goto _test_eof; + _test_eof609: cs = 609; goto _test_eof; + _test_eof610: cs = 610; goto _test_eof; + _test_eof611: cs = 611; goto _test_eof; + _test_eof612: cs = 612; goto _test_eof; + _test_eof613: cs = 613; goto _test_eof; + _test_eof614: cs = 614; goto _test_eof; + _test_eof615: cs = 615; goto _test_eof; + _test_eof616: cs = 616; goto _test_eof; + _test_eof617: cs = 617; goto _test_eof; + _test_eof618: cs = 618; goto _test_eof; + _test_eof619: cs = 619; goto _test_eof; + _test_eof620: cs = 620; goto _test_eof; + _test_eof621: cs = 621; goto _test_eof; + _test_eof622: cs = 622; goto _test_eof; + _test_eof623: cs = 623; goto _test_eof; + _test_eof624: cs = 624; goto _test_eof; + _test_eof625: cs = 625; goto _test_eof; + _test_eof626: cs = 626; goto _test_eof; + _test_eof627: cs = 627; goto _test_eof; + _test_eof628: cs = 628; goto _test_eof; + _test_eof629: cs = 629; goto _test_eof; + _test_eof630: cs = 630; goto _test_eof; + _test_eof631: cs = 631; goto _test_eof; + _test_eof632: cs = 632; goto _test_eof; + _test_eof633: cs = 633; goto _test_eof; + _test_eof1161: cs = 1161; goto _test_eof; + _test_eof634: cs = 634; goto _test_eof; + _test_eof635: cs = 635; goto _test_eof; + _test_eof1162: cs = 1162; goto _test_eof; + _test_eof636: cs = 636; goto _test_eof; + _test_eof637: cs = 637; goto _test_eof; + _test_eof638: cs = 638; goto _test_eof; + _test_eof639: cs = 639; goto _test_eof; + _test_eof640: cs = 640; goto _test_eof; + _test_eof641: cs = 641; goto _test_eof; + _test_eof642: cs = 642; goto _test_eof; + _test_eof643: cs = 643; goto _test_eof; + _test_eof644: cs = 644; goto _test_eof; + _test_eof645: cs = 645; goto _test_eof; + _test_eof646: cs = 646; goto _test_eof; + _test_eof647: cs = 647; goto _test_eof; + _test_eof648: cs = 648; goto _test_eof; + _test_eof649: cs = 649; goto _test_eof; + _test_eof1163: cs = 1163; goto _test_eof; + _test_eof650: cs = 650; goto _test_eof; + _test_eof651: cs = 651; goto _test_eof; + _test_eof652: cs = 652; goto _test_eof; + _test_eof653: cs = 653; goto _test_eof; + _test_eof654: cs = 654; goto _test_eof; + _test_eof655: cs = 655; goto _test_eof; + _test_eof656: cs = 656; goto _test_eof; + _test_eof657: cs = 657; goto _test_eof; + _test_eof658: cs = 658; goto _test_eof; + _test_eof659: cs = 659; goto _test_eof; + _test_eof660: cs = 660; goto _test_eof; + _test_eof661: cs = 661; goto _test_eof; + _test_eof662: cs = 662; goto _test_eof; + _test_eof663: cs = 663; goto _test_eof; + _test_eof664: cs = 664; goto _test_eof; + _test_eof665: cs = 665; goto _test_eof; + _test_eof666: cs = 666; goto _test_eof; + _test_eof667: cs = 667; goto _test_eof; + _test_eof668: cs = 668; goto _test_eof; + _test_eof669: cs = 669; goto _test_eof; + _test_eof670: cs = 670; goto _test_eof; + _test_eof671: cs = 671; goto _test_eof; + _test_eof1164: cs = 1164; goto _test_eof; + _test_eof672: cs = 672; goto _test_eof; + _test_eof673: cs = 673; goto _test_eof; + _test_eof674: cs = 674; goto _test_eof; + _test_eof675: cs = 675; goto _test_eof; + _test_eof676: cs = 676; goto _test_eof; + _test_eof1165: cs = 1165; goto _test_eof; + _test_eof677: cs = 677; goto _test_eof; + _test_eof678: cs = 678; goto _test_eof; + _test_eof679: cs = 679; goto _test_eof; + _test_eof680: cs = 680; goto _test_eof; + _test_eof681: cs = 681; goto _test_eof; + _test_eof1166: cs = 1166; goto _test_eof; + _test_eof682: cs = 682; goto _test_eof; + _test_eof683: cs = 683; goto _test_eof; + _test_eof684: cs = 684; goto _test_eof; + _test_eof685: cs = 685; goto _test_eof; + _test_eof686: cs = 686; goto _test_eof; + _test_eof1167: cs = 1167; goto _test_eof; + _test_eof1168: cs = 1168; goto _test_eof; + _test_eof1169: cs = 1169; goto _test_eof; + _test_eof687: cs = 687; goto _test_eof; + _test_eof688: cs = 688; goto _test_eof; + _test_eof1170: cs = 1170; goto _test_eof; + _test_eof689: cs = 689; goto _test_eof; + _test_eof690: cs = 690; goto _test_eof; + _test_eof691: cs = 691; goto _test_eof; + _test_eof692: cs = 692; goto _test_eof; + _test_eof693: cs = 693; goto _test_eof; + _test_eof694: cs = 694; goto _test_eof; + _test_eof695: cs = 695; goto _test_eof; + _test_eof696: cs = 696; goto _test_eof; + _test_eof697: cs = 697; goto _test_eof; + _test_eof698: cs = 698; goto _test_eof; + _test_eof699: cs = 699; goto _test_eof; + _test_eof700: cs = 700; goto _test_eof; + _test_eof701: cs = 701; goto _test_eof; + _test_eof702: cs = 702; goto _test_eof; + _test_eof703: cs = 703; goto _test_eof; + _test_eof704: cs = 704; goto _test_eof; + _test_eof705: cs = 705; goto _test_eof; + _test_eof706: cs = 706; goto _test_eof; + _test_eof707: cs = 707; goto _test_eof; + _test_eof708: cs = 708; goto _test_eof; + _test_eof709: cs = 709; goto _test_eof; + _test_eof710: cs = 710; goto _test_eof; + _test_eof711: cs = 711; goto _test_eof; + _test_eof712: cs = 712; goto _test_eof; + _test_eof713: cs = 713; goto _test_eof; + _test_eof714: cs = 714; goto _test_eof; + _test_eof715: cs = 715; goto _test_eof; + _test_eof1171: cs = 1171; goto _test_eof; + _test_eof1172: cs = 1172; goto _test_eof; + _test_eof1173: cs = 1173; goto _test_eof; + _test_eof716: cs = 716; goto _test_eof; + _test_eof717: cs = 717; goto _test_eof; + _test_eof718: cs = 718; goto _test_eof; + _test_eof1174: cs = 1174; goto _test_eof; + _test_eof1175: cs = 1175; goto _test_eof; + _test_eof719: cs = 719; goto _test_eof; + _test_eof720: cs = 720; goto _test_eof; + _test_eof721: cs = 721; goto _test_eof; + _test_eof722: cs = 722; goto _test_eof; + _test_eof1176: cs = 1176; goto _test_eof; + _test_eof1177: cs = 1177; goto _test_eof; + _test_eof723: cs = 723; goto _test_eof; + _test_eof724: cs = 724; goto _test_eof; + _test_eof725: cs = 725; goto _test_eof; + _test_eof726: cs = 726; goto _test_eof; + _test_eof1178: cs = 1178; goto _test_eof; + _test_eof1179: cs = 1179; goto _test_eof; + _test_eof727: cs = 727; goto _test_eof; + _test_eof728: cs = 728; goto _test_eof; + _test_eof729: cs = 729; goto _test_eof; + _test_eof730: cs = 730; goto _test_eof; + _test_eof731: cs = 731; goto _test_eof; + _test_eof732: cs = 732; goto _test_eof; + _test_eof733: cs = 733; goto _test_eof; + _test_eof734: cs = 734; goto _test_eof; + _test_eof735: cs = 735; goto _test_eof; + _test_eof736: cs = 736; goto _test_eof; + _test_eof737: cs = 737; goto _test_eof; + _test_eof738: cs = 738; goto _test_eof; + _test_eof739: cs = 739; goto _test_eof; + _test_eof740: cs = 740; goto _test_eof; + _test_eof741: cs = 741; goto _test_eof; + _test_eof742: cs = 742; goto _test_eof; + _test_eof743: cs = 743; goto _test_eof; + _test_eof744: cs = 744; goto _test_eof; + _test_eof745: cs = 745; goto _test_eof; + _test_eof746: cs = 746; goto _test_eof; + _test_eof747: cs = 747; goto _test_eof; + _test_eof748: cs = 748; goto _test_eof; + _test_eof749: cs = 749; goto _test_eof; + _test_eof750: cs = 750; goto _test_eof; + _test_eof751: cs = 751; goto _test_eof; + _test_eof1180: cs = 1180; goto _test_eof; + _test_eof752: cs = 752; goto _test_eof; + _test_eof753: cs = 753; goto _test_eof; + _test_eof754: cs = 754; goto _test_eof; + _test_eof755: cs = 755; goto _test_eof; + _test_eof756: cs = 756; goto _test_eof; + _test_eof757: cs = 757; goto _test_eof; + _test_eof758: cs = 758; goto _test_eof; + _test_eof759: cs = 759; goto _test_eof; + _test_eof760: cs = 760; goto _test_eof; + _test_eof761: cs = 761; goto _test_eof; + _test_eof762: cs = 762; goto _test_eof; + _test_eof763: cs = 763; goto _test_eof; + _test_eof764: cs = 764; goto _test_eof; + _test_eof765: cs = 765; goto _test_eof; + _test_eof766: cs = 766; goto _test_eof; + _test_eof1181: cs = 1181; goto _test_eof; + _test_eof767: cs = 767; goto _test_eof; + _test_eof768: cs = 768; goto _test_eof; + _test_eof769: cs = 769; goto _test_eof; + _test_eof770: cs = 770; goto _test_eof; + _test_eof771: cs = 771; goto _test_eof; + _test_eof772: cs = 772; goto _test_eof; + _test_eof773: cs = 773; goto _test_eof; + _test_eof774: cs = 774; goto _test_eof; + _test_eof775: cs = 775; goto _test_eof; + _test_eof776: cs = 776; goto _test_eof; + _test_eof777: cs = 777; goto _test_eof; + _test_eof778: cs = 778; goto _test_eof; + _test_eof779: cs = 779; goto _test_eof; + _test_eof1182: cs = 1182; goto _test_eof; + _test_eof780: cs = 780; goto _test_eof; + _test_eof781: cs = 781; goto _test_eof; + _test_eof782: cs = 782; goto _test_eof; + _test_eof783: cs = 783; goto _test_eof; + _test_eof784: cs = 784; goto _test_eof; + _test_eof785: cs = 785; goto _test_eof; + _test_eof786: cs = 786; goto _test_eof; + _test_eof787: cs = 787; goto _test_eof; + _test_eof788: cs = 788; goto _test_eof; + _test_eof789: cs = 789; goto _test_eof; + _test_eof790: cs = 790; goto _test_eof; + _test_eof1183: cs = 1183; goto _test_eof; + _test_eof1184: cs = 1184; goto _test_eof; + _test_eof791: cs = 791; goto _test_eof; + _test_eof792: cs = 792; goto _test_eof; + _test_eof793: cs = 793; goto _test_eof; + _test_eof1185: cs = 1185; goto _test_eof; + _test_eof794: cs = 794; goto _test_eof; + _test_eof795: cs = 795; goto _test_eof; + _test_eof796: cs = 796; goto _test_eof; + _test_eof797: cs = 797; goto _test_eof; + _test_eof798: cs = 798; goto _test_eof; + _test_eof799: cs = 799; goto _test_eof; + _test_eof800: cs = 800; goto _test_eof; + _test_eof801: cs = 801; goto _test_eof; + _test_eof802: cs = 802; goto _test_eof; + _test_eof803: cs = 803; goto _test_eof; + _test_eof1186: cs = 1186; goto _test_eof; + _test_eof1187: cs = 1187; goto _test_eof; + _test_eof1188: cs = 1188; goto _test_eof; + _test_eof804: cs = 804; goto _test_eof; + _test_eof805: cs = 805; goto _test_eof; + _test_eof806: cs = 806; goto _test_eof; + _test_eof807: cs = 807; goto _test_eof; + _test_eof808: cs = 808; goto _test_eof; + _test_eof809: cs = 809; goto _test_eof; + _test_eof810: cs = 810; goto _test_eof; + _test_eof811: cs = 811; goto _test_eof; + _test_eof812: cs = 812; goto _test_eof; + _test_eof813: cs = 813; goto _test_eof; + _test_eof814: cs = 814; goto _test_eof; + _test_eof1189: cs = 1189; goto _test_eof; + _test_eof1190: cs = 1190; goto _test_eof; + _test_eof1191: cs = 1191; goto _test_eof; + _test_eof815: cs = 815; goto _test_eof; + _test_eof816: cs = 816; goto _test_eof; + _test_eof817: cs = 817; goto _test_eof; + _test_eof818: cs = 818; goto _test_eof; + _test_eof819: cs = 819; goto _test_eof; + _test_eof820: cs = 820; goto _test_eof; + _test_eof821: cs = 821; goto _test_eof; + _test_eof822: cs = 822; goto _test_eof; + _test_eof823: cs = 823; goto _test_eof; + _test_eof824: cs = 824; goto _test_eof; + _test_eof825: cs = 825; goto _test_eof; + _test_eof826: cs = 826; goto _test_eof; + _test_eof1192: cs = 1192; goto _test_eof; + _test_eof827: cs = 827; goto _test_eof; + _test_eof828: cs = 828; goto _test_eof; + _test_eof829: cs = 829; goto _test_eof; + _test_eof1193: cs = 1193; goto _test_eof; + _test_eof1194: cs = 1194; goto _test_eof; + _test_eof830: cs = 830; goto _test_eof; + _test_eof1195: cs = 1195; goto _test_eof; + _test_eof1196: cs = 1196; goto _test_eof; + _test_eof831: cs = 831; goto _test_eof; + _test_eof1197: cs = 1197; goto _test_eof; + _test_eof1198: cs = 1198; goto _test_eof; + _test_eof832: cs = 832; goto _test_eof; + _test_eof833: cs = 833; goto _test_eof; + _test_eof834: cs = 834; goto _test_eof; + _test_eof835: cs = 835; goto _test_eof; + _test_eof836: cs = 836; goto _test_eof; + _test_eof837: cs = 837; goto _test_eof; + _test_eof838: cs = 838; goto _test_eof; + _test_eof839: cs = 839; goto _test_eof; + _test_eof840: cs = 840; goto _test_eof; + _test_eof841: cs = 841; goto _test_eof; + _test_eof842: cs = 842; goto _test_eof; + _test_eof843: cs = 843; goto _test_eof; + _test_eof844: cs = 844; goto _test_eof; + _test_eof845: cs = 845; goto _test_eof; + _test_eof846: cs = 846; goto _test_eof; + _test_eof847: cs = 847; goto _test_eof; + _test_eof848: cs = 848; goto _test_eof; + _test_eof849: cs = 849; goto _test_eof; + _test_eof850: cs = 850; goto _test_eof; + _test_eof851: cs = 851; goto _test_eof; + _test_eof852: cs = 852; goto _test_eof; + _test_eof853: cs = 853; goto _test_eof; + _test_eof854: cs = 854; goto _test_eof; + _test_eof855: cs = 855; goto _test_eof; + _test_eof856: cs = 856; goto _test_eof; + _test_eof857: cs = 857; goto _test_eof; + _test_eof858: cs = 858; goto _test_eof; + _test_eof859: cs = 859; goto _test_eof; + _test_eof860: cs = 860; goto _test_eof; + _test_eof861: cs = 861; goto _test_eof; + _test_eof862: cs = 862; goto _test_eof; + _test_eof863: cs = 863; goto _test_eof; + _test_eof864: cs = 864; goto _test_eof; + _test_eof865: cs = 865; goto _test_eof; + _test_eof866: cs = 866; goto _test_eof; + _test_eof867: cs = 867; goto _test_eof; + _test_eof868: cs = 868; goto _test_eof; + _test_eof869: cs = 869; goto _test_eof; + _test_eof870: cs = 870; goto _test_eof; + _test_eof871: cs = 871; goto _test_eof; + _test_eof872: cs = 872; goto _test_eof; + _test_eof873: cs = 873; goto _test_eof; + _test_eof1199: cs = 1199; goto _test_eof; + _test_eof874: cs = 874; goto _test_eof; + _test_eof875: cs = 875; goto _test_eof; + _test_eof876: cs = 876; goto _test_eof; + _test_eof877: cs = 877; goto _test_eof; + _test_eof878: cs = 878; goto _test_eof; + _test_eof879: cs = 879; goto _test_eof; + _test_eof880: cs = 880; goto _test_eof; + _test_eof881: cs = 881; goto _test_eof; + _test_eof882: cs = 882; goto _test_eof; + _test_eof883: cs = 883; goto _test_eof; + _test_eof884: cs = 884; goto _test_eof; + _test_eof885: cs = 885; goto _test_eof; + _test_eof886: cs = 886; goto _test_eof; + _test_eof887: cs = 887; goto _test_eof; + _test_eof888: cs = 888; goto _test_eof; + _test_eof889: cs = 889; goto _test_eof; + _test_eof890: cs = 890; goto _test_eof; + _test_eof891: cs = 891; goto _test_eof; + _test_eof892: cs = 892; goto _test_eof; + _test_eof893: cs = 893; goto _test_eof; + _test_eof894: cs = 894; goto _test_eof; + _test_eof895: cs = 895; goto _test_eof; + _test_eof896: cs = 896; goto _test_eof; + _test_eof897: cs = 897; goto _test_eof; + _test_eof898: cs = 898; goto _test_eof; + _test_eof899: cs = 899; goto _test_eof; + _test_eof900: cs = 900; goto _test_eof; + _test_eof901: cs = 901; goto _test_eof; + _test_eof902: cs = 902; goto _test_eof; + _test_eof903: cs = 903; goto _test_eof; + _test_eof904: cs = 904; goto _test_eof; + _test_eof905: cs = 905; goto _test_eof; + _test_eof906: cs = 906; goto _test_eof; + _test_eof907: cs = 907; goto _test_eof; + _test_eof908: cs = 908; goto _test_eof; + _test_eof909: cs = 909; goto _test_eof; + _test_eof910: cs = 910; goto _test_eof; + _test_eof911: cs = 911; goto _test_eof; + _test_eof912: cs = 912; goto _test_eof; + _test_eof913: cs = 913; goto _test_eof; + _test_eof914: cs = 914; goto _test_eof; + _test_eof915: cs = 915; goto _test_eof; + _test_eof916: cs = 916; goto _test_eof; + _test_eof917: cs = 917; goto _test_eof; + _test_eof918: cs = 918; goto _test_eof; + _test_eof919: cs = 919; goto _test_eof; + _test_eof920: cs = 920; goto _test_eof; + _test_eof921: cs = 921; goto _test_eof; + _test_eof922: cs = 922; goto _test_eof; + _test_eof923: cs = 923; goto _test_eof; + _test_eof924: cs = 924; goto _test_eof; + _test_eof925: cs = 925; goto _test_eof; + _test_eof926: cs = 926; goto _test_eof; + _test_eof927: cs = 927; goto _test_eof; + _test_eof928: cs = 928; goto _test_eof; + _test_eof929: cs = 929; goto _test_eof; + _test_eof930: cs = 930; goto _test_eof; + _test_eof931: cs = 931; goto _test_eof; + _test_eof932: cs = 932; goto _test_eof; + _test_eof933: cs = 933; goto _test_eof; + _test_eof934: cs = 934; goto _test_eof; + _test_eof935: cs = 935; goto _test_eof; + _test_eof936: cs = 936; goto _test_eof; + _test_eof937: cs = 937; goto _test_eof; + _test_eof938: cs = 938; goto _test_eof; + _test_eof939: cs = 939; goto _test_eof; + _test_eof940: cs = 940; goto _test_eof; + _test_eof941: cs = 941; goto _test_eof; + _test_eof942: cs = 942; goto _test_eof; + _test_eof943: cs = 943; goto _test_eof; + _test_eof944: cs = 944; goto _test_eof; + _test_eof945: cs = 945; goto _test_eof; + _test_eof946: cs = 946; goto _test_eof; + _test_eof947: cs = 947; goto _test_eof; + _test_eof948: cs = 948; goto _test_eof; + _test_eof949: cs = 949; goto _test_eof; + _test_eof950: cs = 950; goto _test_eof; + _test_eof951: cs = 951; goto _test_eof; + _test_eof952: cs = 952; goto _test_eof; + _test_eof953: cs = 953; goto _test_eof; + _test_eof954: cs = 954; goto _test_eof; + _test_eof955: cs = 955; goto _test_eof; + _test_eof956: cs = 956; goto _test_eof; + _test_eof957: cs = 957; goto _test_eof; + _test_eof958: cs = 958; goto _test_eof; + _test_eof959: cs = 959; goto _test_eof; + _test_eof960: cs = 960; goto _test_eof; + _test_eof961: cs = 961; goto _test_eof; + _test_eof962: cs = 962; goto _test_eof; + _test_eof963: cs = 963; goto _test_eof; + _test_eof964: cs = 964; goto _test_eof; + _test_eof965: cs = 965; goto _test_eof; + _test_eof966: cs = 966; goto _test_eof; + _test_eof967: cs = 967; goto _test_eof; + _test_eof968: cs = 968; goto _test_eof; + _test_eof969: cs = 969; goto _test_eof; + _test_eof970: cs = 970; goto _test_eof; + _test_eof971: cs = 971; goto _test_eof; + _test_eof972: cs = 972; goto _test_eof; + _test_eof973: cs = 973; goto _test_eof; + _test_eof974: cs = 974; goto _test_eof; + _test_eof975: cs = 975; goto _test_eof; + _test_eof976: cs = 976; goto _test_eof; + _test_eof977: cs = 977; goto _test_eof; + _test_eof978: cs = 978; goto _test_eof; + _test_eof979: cs = 979; goto _test_eof; + _test_eof980: cs = 980; goto _test_eof; + _test_eof981: cs = 981; goto _test_eof; + _test_eof982: cs = 982; goto _test_eof; + _test_eof983: cs = 983; goto _test_eof; + _test_eof984: cs = 984; goto _test_eof; + _test_eof985: cs = 985; goto _test_eof; + _test_eof986: cs = 986; goto _test_eof; + _test_eof987: cs = 987; goto _test_eof; + _test_eof988: cs = 988; goto _test_eof; + _test_eof989: cs = 989; goto _test_eof; + _test_eof990: cs = 990; goto _test_eof; + _test_eof991: cs = 991; goto _test_eof; + _test_eof992: cs = 992; goto _test_eof; + _test_eof993: cs = 993; goto _test_eof; + _test_eof994: cs = 994; goto _test_eof; + _test_eof995: cs = 995; goto _test_eof; + _test_eof996: cs = 996; goto _test_eof; + _test_eof997: cs = 997; goto _test_eof; + _test_eof998: cs = 998; goto _test_eof; + _test_eof999: cs = 999; goto _test_eof; + _test_eof1000: cs = 1000; goto _test_eof; + _test_eof1001: cs = 1001; goto _test_eof; + _test_eof1002: cs = 1002; goto _test_eof; + _test_eof1003: cs = 1003; goto _test_eof; + _test_eof1004: cs = 1004; goto _test_eof; + _test_eof1005: cs = 1005; goto _test_eof; + _test_eof1006: cs = 1006; goto _test_eof; + _test_eof1007: cs = 1007; goto _test_eof; + _test_eof1008: cs = 1008; goto _test_eof; + _test_eof1009: cs = 1009; goto _test_eof; + _test_eof1010: cs = 1010; goto _test_eof; + _test_eof1011: cs = 1011; goto _test_eof; + _test_eof1012: cs = 1012; goto _test_eof; + _test_eof1200: cs = 1200; goto _test_eof; + _test_eof1013: cs = 1013; goto _test_eof; + _test_eof1014: cs = 1014; goto _test_eof; + _test_eof1015: cs = 1015; goto _test_eof; + _test_eof1016: cs = 1016; goto _test_eof; + _test_eof1017: cs = 1017; goto _test_eof; + _test_eof1018: cs = 1018; goto _test_eof; + _test_eof1019: cs = 1019; goto _test_eof; + _test_eof1020: cs = 1020; goto _test_eof; + _test_eof1201: cs = 1201; goto _test_eof; + _test_eof1021: cs = 1021; goto _test_eof; + _test_eof1022: cs = 1022; goto _test_eof; + _test_eof1023: cs = 1023; goto _test_eof; + _test_eof1024: cs = 1024; goto _test_eof; + _test_eof1025: cs = 1025; goto _test_eof; + _test_eof1202: cs = 1202; goto _test_eof; + _test_eof1026: cs = 1026; goto _test_eof; + _test_eof1027: cs = 1027; goto _test_eof; + _test_eof1028: cs = 1028; goto _test_eof; + _test_eof1029: cs = 1029; goto _test_eof; + _test_eof1030: cs = 1030; goto _test_eof; + _test_eof1031: cs = 1031; goto _test_eof; + _test_eof1032: cs = 1032; goto _test_eof; + _test_eof1033: cs = 1033; goto _test_eof; + _test_eof1034: cs = 1034; goto _test_eof; + _test_eof1035: cs = 1035; goto _test_eof; + _test_eof1036: cs = 1036; goto _test_eof; + _test_eof1037: cs = 1037; goto _test_eof; + _test_eof1038: cs = 1038; goto _test_eof; + _test_eof1039: cs = 1039; goto _test_eof; + _test_eof1040: cs = 1040; goto _test_eof; + _test_eof1041: cs = 1041; goto _test_eof; + _test_eof1042: cs = 1042; goto _test_eof; + _test_eof1043: cs = 1043; goto _test_eof; + _test_eof1203: cs = 1203; goto _test_eof; + _test_eof1044: cs = 1044; goto _test_eof; + _test_eof1045: cs = 1045; goto _test_eof; + _test_eof1046: cs = 1046; goto _test_eof; + _test_eof1047: cs = 1047; goto _test_eof; + _test_eof1048: cs = 1048; goto _test_eof; + _test_eof1049: cs = 1049; goto _test_eof; + _test_eof1050: cs = 1050; goto _test_eof; + _test_eof1051: cs = 1051; goto _test_eof; + _test_eof1052: cs = 1052; goto _test_eof; + _test_eof1053: cs = 1053; goto _test_eof; + _test_eof1054: cs = 1054; goto _test_eof; + _test_eof1055: cs = 1055; goto _test_eof; + _test_eof1056: cs = 1056; goto _test_eof; + _test_eof1057: cs = 1057; goto _test_eof; + _test_eof1058: cs = 1058; goto _test_eof; + _test_eof1059: cs = 1059; goto _test_eof; + _test_eof1060: cs = 1060; goto _test_eof; + _test_eof1061: cs = 1061; goto _test_eof; + _test_eof1062: cs = 1062; goto _test_eof; + _test_eof1204: cs = 1204; goto _test_eof; + _test_eof1063: cs = 1063; goto _test_eof; + _test_eof1064: cs = 1064; goto _test_eof; + _test_eof1065: cs = 1065; goto _test_eof; + _test_eof1066: cs = 1066; goto _test_eof; + _test_eof1067: cs = 1067; goto _test_eof; + _test_eof1068: cs = 1068; goto _test_eof; + _test_eof1069: cs = 1069; goto _test_eof; + _test_eof1070: cs = 1070; goto _test_eof; + _test_eof1071: cs = 1071; goto _test_eof; + _test_eof1072: cs = 1072; goto _test_eof; + _test_eof1073: cs = 1073; goto _test_eof; + _test_eof1074: cs = 1074; goto _test_eof; + _test_eof1075: cs = 1075; goto _test_eof; + _test_eof1076: cs = 1076; goto _test_eof; + _test_eof1077: cs = 1077; goto _test_eof; + _test_eof1205: cs = 1205; goto _test_eof; + _test_eof1206: cs = 1206; goto _test_eof; + _test_eof1207: cs = 1207; goto _test_eof; + _test_eof1078: cs = 1078; goto _test_eof; + _test_eof1079: cs = 1079; goto _test_eof; + _test_eof1080: cs = 1080; goto _test_eof; + _test_eof1081: cs = 1081; goto _test_eof; + _test_eof1082: cs = 1082; goto _test_eof; + _test_eof1083: cs = 1083; goto _test_eof; + _test_eof1084: cs = 1084; goto _test_eof; + _test_eof1208: cs = 1208; goto _test_eof; + _test_eof1085: cs = 1085; goto _test_eof; + _test_eof1086: cs = 1086; goto _test_eof; + _test_eof1087: cs = 1087; goto _test_eof; + _test_eof1088: cs = 1088; goto _test_eof; + _test_eof1089: cs = 1089; goto _test_eof; + _test_eof1090: cs = 1090; goto _test_eof; + _test_eof1091: cs = 1091; goto _test_eof; + _test_eof1092: cs = 1092; goto _test_eof; + _test_eof1093: cs = 1093; goto _test_eof; + _test_eof1094: cs = 1094; goto _test_eof; + _test_eof1095: cs = 1095; goto _test_eof; + _test_eof1096: cs = 1096; goto _test_eof; + _test_eof1097: cs = 1097; goto _test_eof; + _test_eof1209: cs = 1209; goto _test_eof; + _test_eof1098: cs = 1098; goto _test_eof; + _test_eof1099: cs = 1099; goto _test_eof; + _test_eof1100: cs = 1100; goto _test_eof; + _test_eof1101: cs = 1101; goto _test_eof; + _test_eof1102: cs = 1102; goto _test_eof; + _test_eof1103: cs = 1103; goto _test_eof; + _test_eof1104: cs = 1104; goto _test_eof; + _test_eof1210: cs = 1210; goto _test_eof; + _test_eof1105: cs = 1105; goto _test_eof; + _test_eof1106: cs = 1106; goto _test_eof; + _test_eof1107: cs = 1107; goto _test_eof; + _test_eof1108: cs = 1108; goto _test_eof; + _test_eof1109: cs = 1109; goto _test_eof; + _test_eof1110: cs = 1110; goto _test_eof; + _test_eof1211: cs = 1211; goto _test_eof; + _test_eof1111: cs = 1111; goto _test_eof; + _test_eof1112: cs = 1112; goto _test_eof; + _test_eof1113: cs = 1113; goto _test_eof; + _test_eof1114: cs = 1114; goto _test_eof; + _test_eof1115: cs = 1115; goto _test_eof; + _test_eof1116: cs = 1116; goto _test_eof; + _test_eof1212: cs = 1212; goto _test_eof; + _test_eof1117: cs = 1117; goto _test_eof; + _test_eof1118: cs = 1118; goto _test_eof; + _test_eof1119: cs = 1119; goto _test_eof; + _test_eof1120: cs = 1120; goto _test_eof; + _test_eof1121: cs = 1121; goto _test_eof; + _test_eof1122: cs = 1122; goto _test_eof; + _test_eof1123: cs = 1123; goto _test_eof; + _test_eof1124: cs = 1124; goto _test_eof; + _test_eof1213: cs = 1213; goto _test_eof; + _test_eof1125: cs = 1125; goto _test_eof; + _test_eof1126: cs = 1126; goto _test_eof; + + _test_eof: {} + if ( p == eof ) + { + switch ( cs ) { + case 7: + case 8: + case 151: + case 175: + case 177: + case 227: + case 241: + case 264: + case 318: + case 319: + case 325: + { + WARN(ZS_BAD_REST); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 270: + case 271: + case 272: + case 277: + case 278: + { + WARN(ZS_BAD_DNAME_CHAR); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 160: + { + s->r_owner_length = 0; + WARN(ZS_BAD_OWNER); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 286: + { + WARN(ZS_BAD_TEXT); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 291: + case 299: + { + ERR(ZS_BAD_TTL); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 300: + case 301: + case 302: + case 311: + { + ERR(ZS_BAD_ORIGIN); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 313: + { + ERR(ZS_BAD_INCLUDE_FILENAME); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 316: + case 326: + { + ERR(ZS_BAD_INCLUDE_ORIGIN); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 1131: + { + NOERR; + } + break; + case 152: + case 153: + case 154: + case 155: + case 156: + case 157: + case 158: + case 159: + case 195: + case 196: + case 197: + case 198: + case 199: + case 200: + case 201: + case 202: + case 203: + { + ERR(ZS_BAD_DIRECTIVE); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 329: + case 330: + case 331: + case 332: + case 333: + case 334: + case 335: + { + WARN(ZS_BAD_BASE64_CHAR); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 336: + case 337: + case 338: + case 339: + case 340: + case 341: + case 342: + case 343: + case 344: + case 345: + case 346: + case 347: + case 348: + case 349: + case 350: + case 351: + case 352: + case 353: + case 354: + case 355: + case 356: + case 357: + case 358: + case 359: + case 360: + case 361: + case 362: + case 363: + case 364: + case 365: + case 366: + case 367: + case 368: + case 369: + case 370: + case 371: + case 372: + case 373: + case 374: + case 375: + case 376: + case 377: + case 378: + case 379: + case 380: + case 381: + case 382: + case 383: + case 384: + case 385: + case 386: + case 387: + case 388: + case 389: + case 390: + case 391: + case 392: + case 393: + case 394: + case 395: + case 396: + case 397: + case 398: + case 399: + case 400: + case 401: + case 402: + case 403: + case 404: + case 405: + case 406: + case 407: + case 408: + case 409: + case 410: + case 411: + case 412: + case 413: + case 414: + case 415: + case 416: + case 417: + case 418: + case 419: + case 420: + case 421: + case 422: + case 423: + case 424: + case 425: + case 426: + case 427: + case 428: + case 429: + case 430: + case 431: + case 432: + case 433: + case 434: + case 435: + case 436: + case 437: + case 438: + case 439: + case 440: + case 441: + case 442: + case 443: + case 444: + case 445: + case 446: + case 447: + case 448: + case 449: + case 450: + case 451: + case 452: + case 453: + case 454: + case 455: + case 456: + case 457: + case 458: + case 459: + case 460: + case 461: + case 462: + case 463: + case 464: + case 465: + case 466: + case 467: + { + WARN(ZS_BAD_BITMAP); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 468: + case 475: + case 476: + case 477: + case 484: + case 486: + { + WARN(ZS_BAD_HEX_RDATA); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 487: + case 488: + case 489: + case 490: + case 491: + case 492: + case 493: + case 494: + case 495: + case 496: + case 497: + case 498: + case 499: + case 500: + case 501: + case 502: + case 503: + case 504: + case 505: + case 506: + case 507: + case 508: + case 509: + case 510: + case 511: + case 512: + case 513: + case 514: + case 515: + case 516: + case 517: + case 518: + case 519: + case 520: + case 521: + case 522: + case 523: + case 524: + case 525: + case 526: + case 527: + case 528: + case 529: + case 530: + case 531: + case 532: + case 533: + case 534: + case 535: + case 536: + case 537: + case 538: + case 539: + case 540: + case 541: + case 542: + case 543: + case 544: + case 545: + case 546: + case 547: + case 548: + case 549: + case 550: + case 551: + case 552: + case 553: + case 554: + case 555: + case 556: + case 557: + case 558: + case 559: + case 560: + case 561: + case 562: + case 563: + case 564: + case 565: + case 566: + case 567: + case 568: + case 569: + case 570: + case 571: + case 572: + case 573: + case 574: + case 575: + case 576: + case 577: + case 578: + case 579: + case 580: + case 581: + case 582: + case 583: + case 584: + case 585: + case 586: + case 587: + case 588: + case 589: + case 590: + { + WARN(ZS_BAD_ALGORITHM); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 591: + case 592: + case 593: + case 594: + case 595: + case 596: + case 597: + case 598: + case 599: + case 600: + case 601: + case 602: + case 603: + case 604: + case 605: + case 606: + case 607: + case 608: + case 609: + case 610: + case 611: + case 612: + case 613: + case 614: + case 615: + case 616: + case 617: + case 618: + case 619: + case 620: + case 621: + case 622: + case 623: + case 624: + case 625: + case 626: + case 627: + case 628: + case 629: + case 630: + case 631: + { + WARN(ZS_BAD_CERT_TYPE); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 5: + case 9: + case 10: + case 246: + case 634: + case 635: + case 636: + case 637: + case 638: + case 639: + case 653: + case 657: + case 661: + case 665: + case 666: + case 667: + case 668: + case 669: + case 670: + case 671: + case 672: + case 673: + case 674: + case 675: + case 676: + case 677: + case 680: + case 681: + case 682: + case 750: + case 751: + case 752: + case 753: + case 754: + case 759: + case 760: + case 761: + case 762: + case 763: + case 764: + case 765: + case 766: + case 767: + case 768: + case 769: + case 770: + case 771: + case 772: + case 773: + case 776: + case 777: + case 778: + case 779: + case 780: + case 781: + case 782: + case 790: + case 796: + case 797: + case 804: + case 805: + case 806: + case 815: + case 816: + case 855: + case 858: + case 859: + case 870: + case 871: + case 872: + case 873: + case 874: + case 875: + case 876: + case 877: + case 878: + case 879: + case 880: + case 881: + case 1010: + case 1011: + case 1012: + case 1017: + case 1018: + case 1019: + case 1020: + case 1021: + case 1022: + case 1023: + case 1024: + case 1025: + case 1043: + case 1049: + case 1052: + case 1053: + case 1054: + case 1065: + case 1066: + case 1067: + case 1078: + case 1079: + case 1080: + case 1085: + case 1098: + case 1115: + case 1116: + case 1117: + case 1118: + case 1121: + case 1122: + case 1123: + case 1124: + case 1125: + case 1126: + { + WARN(ZS_BAD_RDATA); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 3: + case 17: + case 18: + case 22: + case 23: + case 24: + case 25: + case 26: + case 29: + case 30: + case 32: + case 33: + case 34: + case 36: + case 37: + case 38: + case 39: + case 41: + case 42: + case 43: + case 45: + case 46: + case 47: + case 50: + case 51: + case 52: + case 53: + case 55: + case 57: + case 58: + case 59: + case 60: + case 62: + case 63: + case 64: + case 65: + case 66: + case 67: + case 68: + case 69: + case 70: + case 71: + case 73: + case 74: + case 77: + case 78: + case 80: + case 82: + case 85: + case 86: + case 87: + case 88: + case 91: + case 92: + case 93: + case 94: + case 96: + case 99: + case 102: + case 103: + case 104: + case 105: + case 107: + case 108: + case 110: + case 112: + case 113: + case 114: + case 117: + case 118: + case 120: + case 122: + case 124: + case 125: + case 126: + case 128: + case 129: + case 130: + case 132: + case 134: + case 135: + case 138: + case 139: + case 141: + case 143: + case 144: + case 150: + case 165: + case 169: + case 230: + case 231: + case 233: + case 234: + case 235: + case 237: + { + WARN(ZS_UNSUPPORTED_TYPE); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 294: + case 295: + { + WARN(ZS_BAD_REST); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + ERR(ZS_BAD_TTL); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 303: + case 304: + case 305: + case 310: + { + WARN(ZS_BAD_REST); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + ERR(ZS_BAD_ORIGIN); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 273: + case 274: + case 275: + case 276: + { + WARN(ZS_BAD_NUMBER); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_DNAME_CHAR); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 306: + case 307: + case 308: + case 309: + { + WARN(ZS_BAD_NUMBER); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + ERR(ZS_BAD_ORIGIN); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 320: + case 321: + case 322: + case 323: + { + WARN(ZS_BAD_NUMBER); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + ERR(ZS_BAD_INCLUDE_ORIGIN); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 224: + { + s->r_owner_length = 0; + WARN(ZS_BAD_OWNER); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 250: + case 251: + case 252: + case 253: + case 254: + case 255: + case 256: + case 257: + case 258: + case 259: + case 260: + case 261: + case 262: + case 263: + case 266: + { + s->r_owner_length = 0; + WARN(ZS_BAD_OWNER); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_UNSUPPORTED_TYPE); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 292: + { + WARN(ZS_BAD_NUMBER); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + ERR(ZS_BAD_TTL); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 469: + case 470: + case 478: + case 479: + case 485: + { + WARN(ZS_BAD_NUMBER); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_HEX_RDATA); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 640: + case 641: + case 642: + case 644: + case 646: + case 648: + case 678: + case 679: + case 744: + case 745: + case 746: + case 747: + case 748: + case 749: + case 755: + case 756: + case 757: + case 758: + case 774: + case 775: + case 794: + case 795: + case 798: + case 799: + case 807: + case 808: + case 809: + case 810: + case 817: + case 818: + case 860: + case 861: + case 862: + case 863: + case 868: + case 869: + case 1013: + case 1014: + case 1015: + case 1016: + case 1026: + case 1027: + case 1028: + case 1029: + case 1030: + case 1031: + case 1055: + case 1056: + case 1057: + case 1058: + case 1059: + case 1060: + case 1068: + case 1069: + case 1070: + case 1071: + case 1072: + case 1073: + case 1081: + case 1082: + case 1086: + case 1087: + case 1111: + case 1112: + case 1113: + case 1114: + case 1119: + case 1120: + { + WARN(ZS_BAD_NUMBER); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 136: + case 145: + case 161: + case 164: + { + WARN(ZS_BAD_NUMBER); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_UNSUPPORTED_TYPE); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 864: + case 865: + case 866: + case 867: + { + WARN(ZS_BAD_TIMESTAMP_CHAR); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 279: + case 280: + case 285: + { + WARN(ZS_BAD_TEXT_CHAR); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_TEXT); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 683: + case 684: + case 685: + case 686: + { + s->long_string = false; + } + { + WARN(ZS_BAD_RDATA); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 314: + { + ERR(ZS_BAD_INCLUDE_FILENAME); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_REST); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 315: + case 317: + case 324: + { + ERR(ZS_BAD_INCLUDE_ORIGIN); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_REST); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 632: + case 633: + case 687: + case 688: + case 1083: + case 1084: + { + WARN(ZS_BAD_ADDRESS_CHAR); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 783: + case 784: + case 785: + case 788: + case 789: + case 791: + { + WARN(ZS_BAD_APL); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 800: + case 801: + case 802: + case 803: + case 811: + case 812: + case 813: + case 814: + case 1032: + case 1033: + case 1050: + case 1051: + case 1061: + case 1062: + case 1063: + case 1064: + case 1074: + case 1075: + case 1076: + case 1077: + case 1088: + case 1089: + case 1090: + case 1091: + case 1093: + case 1094: + case 1095: + case 1096: + case 1099: + case 1100: + case 1102: + case 1103: + case 1105: + case 1106: + case 1108: + case 1109: + { + WARN(ZS_BAD_HEX_CHAR); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 1034: + case 1035: + case 1036: + case 1037: + case 1038: + case 1039: + case 1040: + case 1041: + case 1042: + case 1044: + case 1045: + case 1046: + case 1047: + case 1048: + { + WARN(ZS_BAD_BASE32HEX_CHAR); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 819: + case 820: + case 823: + case 832: + case 833: + case 834: + case 839: + case 840: + case 841: + case 846: + case 847: + case 848: + case 851: + case 853: + case 854: + { + WARN(ZS_BAD_GATEWAY); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 825: + case 826: + case 827: + case 828: + case 829: + case 830: + case 831: + { + WARN(ZS_BAD_GATEWAY_KEY); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 856: + case 857: + case 882: + case 883: + case 884: + case 885: + case 886: + case 887: + case 888: + case 889: + case 890: + case 891: + case 892: + case 893: + case 894: + case 895: + case 896: + case 897: + case 898: + case 899: + case 900: + case 901: + case 902: + case 903: + case 904: + case 905: + case 906: + case 907: + case 908: + case 909: + case 910: + case 911: + case 912: + case 913: + case 914: + case 915: + case 916: + case 917: + case 918: + case 919: + case 920: + case 921: + case 922: + case 923: + case 924: + case 925: + case 926: + case 927: + case 928: + case 929: + case 930: + case 931: + case 932: + case 933: + case 934: + case 935: + case 936: + case 937: + case 938: + case 939: + case 940: + case 941: + case 942: + case 943: + case 944: + case 945: + case 946: + case 947: + case 948: + case 949: + case 950: + case 951: + case 952: + case 953: + case 954: + case 955: + case 956: + case 957: + case 958: + case 959: + case 960: + case 961: + case 962: + case 963: + case 964: + case 965: + case 966: + case 967: + case 968: + case 969: + case 970: + case 971: + case 972: + case 973: + case 974: + case 975: + case 976: + case 977: + case 978: + case 979: + case 980: + case 981: + case 982: + case 983: + case 984: + case 985: + case 986: + case 987: + case 988: + case 989: + case 990: + case 991: + case 992: + case 993: + case 994: + case 995: + case 996: + case 997: + case 998: + case 999: + case 1000: + case 1001: + case 1002: + case 1003: + case 1004: + case 1007: + case 1008: + case 1009: + { + WARN(ZS_UNSUPPORTED_TYPE); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 689: + case 690: + case 691: + case 692: + case 693: + case 694: + case 695: + case 696: + case 697: + case 698: + case 699: + case 700: + case 701: + case 702: + case 703: + case 704: + case 705: + case 706: + case 707: + case 708: + case 709: + case 710: + case 711: + case 712: + case 713: + case 714: + case 715: + case 716: + case 717: + case 718: + case 719: + case 720: + case 721: + case 722: + case 723: + case 724: + case 725: + case 726: + case 727: + case 728: + case 729: + case 730: + case 731: + case 732: + case 733: + case 734: + case 735: + case 736: + case 737: + case 738: + case 739: + case 740: + case 741: + case 742: + case 743: + { + WARN(ZS_BAD_LOC_DATA); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 6: + case 11: + case 12: + case 13: + case 20: + case 21: + case 173: + case 176: + case 193: + case 226: + case 228: + case 229: + case 239: + case 240: + case 242: + case 243: + case 244: + case 245: + case 248: + case 249: + { + WARN(ZS_BAD_RDATA); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_REST); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 16: + case 207: + case 265: + case 267: + { + WARN(ZS_UNSUPPORTED_TYPE); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_REST); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 4: + case 19: + case 27: + case 28: + case 31: + case 35: + case 40: + case 44: + case 48: + case 49: + case 54: + case 56: + case 61: + case 72: + case 75: + case 76: + case 79: + case 81: + case 83: + case 84: + case 89: + case 90: + case 95: + case 97: + case 98: + case 100: + case 101: + case 106: + case 109: + case 111: + case 115: + case 116: + case 119: + case 121: + case 123: + case 127: + case 131: + case 133: + case 140: + case 232: + case 236: + case 238: + { + WARN(ZS_UNSUPPORTED_TYPE); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 204: + { + s->r_owner_length = 0; + WARN(ZS_BAD_OWNER); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_REST); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 170: + { + s->r_owner_length = 0; + WARN(ZS_BAD_OWNER); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_UNSUPPORTED_TYPE); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 821: + case 822: + case 835: + case 836: + case 842: + case 843: + case 849: + case 850: + { + WARN(ZS_BAD_NUMBER); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_GATEWAY); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 1005: + case 1006: + { + WARN(ZS_BAD_NUMBER); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_UNSUPPORTED_TYPE); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 1: + case 142: + case 174: + case 206: + { + WARN(ZS_BAD_NUMBER); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_UNSUPPORTED_TYPE); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_REST); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 137: + case 171: + { + WARN(ZS_BAD_NUMBER); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_UNSUPPORTED_TYPE); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 297: + { + WARN(ZS_BAD_TIME_UNIT); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_NUMBER); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + ERR(ZS_BAD_TTL); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 643: + case 645: + case 647: + case 649: + case 650: + case 651: + case 652: + case 654: + case 655: + case 656: + case 658: + case 659: + case 660: + case 662: + case 663: + case 664: + { + WARN(ZS_BAD_TIME_UNIT); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_NUMBER); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 2: + case 146: + case 147: + case 148: + case 149: + case 166: + case 167: + case 168: + { + WARN(ZS_BAD_TIME_UNIT); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_NUMBER); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_UNSUPPORTED_TYPE); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 281: + case 282: + case 283: + case 284: + case 287: + case 288: + case 289: + case 290: + { + WARN(ZS_BAD_NUMBER); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_TEXT_CHAR); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_TEXT); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 786: + case 787: + case 792: + case 793: + { + WARN(ZS_BAD_ADDRESS_CHAR); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_APL); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 837: + case 844: + { + WARN(ZS_BAD_ADDRESS_CHAR); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_GATEWAY); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 471: + case 472: + case 473: + case 474: + case 480: + case 481: + case 482: + case 483: + { + WARN(ZS_BAD_HEX_CHAR); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_HEX_RDATA); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_HEX_RDATA); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 1092: + { + WARN(ZS_BAD_HEX_CHAR); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_CHAR_COLON); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 824: + case 852: + { + WARN(ZS_BAD_GATEWAY); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_GATEWAY_KEY); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 1101: + case 1104: + case 1107: + case 1110: + { + WARN(ZS_BAD_CHAR_DASH); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_HEX_CHAR); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 1097: + { + WARN(ZS_BAD_CHAR_COLON); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_HEX_CHAR); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 194: + { + WARN(ZS_BAD_RDATA); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_REST); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + ERR(ZS_BAD_DIRECTIVE); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 172: + case 179: + case 180: + case 181: + case 182: + case 183: + case 184: + case 185: + case 186: + case 187: + case 188: + case 189: + case 190: + case 191: + case 192: + { + WARN(ZS_UNSUPPORTED_TYPE); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_REST); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 163: + { + s->r_owner_length = 0; + WARN(ZS_BAD_OWNER); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_TIME_UNIT); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_NUMBER); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_UNSUPPORTED_TYPE); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 208: + case 210: + case 211: + case 212: + case 213: + case 214: + case 215: + case 216: + case 217: + case 218: + case 219: + case 220: + case 221: + case 222: + case 223: + { + s->r_owner_length = 0; + WARN(ZS_BAD_OWNER); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_UNSUPPORTED_TYPE); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_REST); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 14: + case 178: + case 209: + case 225: + case 247: + { + WARN(ZS_BAD_NUMBER); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_UNSUPPORTED_TYPE); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_REST); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 293: + case 296: + case 298: + { + WARN(ZS_BAD_TIME_UNIT); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_NUMBER); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_REST); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + ERR(ZS_BAD_TTL); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 838: + case 845: + { + WARN(ZS_BAD_ADDRESS_CHAR); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_GATEWAY); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_GATEWAY_KEY); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 15: + { + WARN(ZS_BAD_TIME_UNIT); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_NUMBER); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_UNSUPPORTED_TYPE); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_REST); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + case 205: + { + s->r_owner_length = 0; + WARN(ZS_BAD_OWNER); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_TIME_UNIT); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_NUMBER); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_UNSUPPORTED_TYPE); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_RDATA); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + { + WARN(ZS_BAD_REST); + p--; { if ( p == pe ) + goto _test_eof268; +goto st268;} + } + break; + } + } + + _out: {} + } + + + // Check if the scanner state machine is in an uncovered state. + bool extra_error = false; + if (cs == 0) { + ERR(ZS_UNCOVERED_STATE); + extra_error = true; + // Check for an unclosed multiline record. + } else if (s->input.eof && s->multiline) { + ERR(ZS_UNCLOSED_MULTILINE); + extra_error = true; + } + + // Treat the extra error. + if (extra_error) { + s->error.counter++; + s->state = ZS_STATE_ERROR; + + // Copy the error context just for the part of the current line. + s->buffer_length = 0; + while (p < pe && *p != '\n' && s->buffer_length < 50) { + s->buffer[s->buffer_length++] = *p++; + } + s->buffer[s->buffer_length++] = 0; + + // Execute the error callback. + if (s->process.automatic && s->process.error != NULL) { + s->process.error(s); + } + + return; + } + + // Storing scanner states. + s->cs = cs; + s->top = top; + memcpy(s->stack, stack, sizeof(stack)); + + // Store the current parser position. + s->input.current = p; + + // Storing r_data pointer. + s->r_data_tail = rdata_tail - s->r_data; + + if (*wrap == WRAP_DETECTED) { + if (set_input_string(s, "\\", 1, true) != 0) { + return; + } + + *wrap = WRAP_PROCESS; + parse(s, wrap); + } else { + *wrap = WRAP_NONE; + } +} + +__attribute__((visibility("default"))) +int zs_parse_record( + zs_scanner_t *s) +{ + if (s == NULL) { + return -1; + } + + // Check if parsing is possible. + switch (s->state) { + case ZS_STATE_NONE: + case ZS_STATE_DATA: + case ZS_STATE_INCLUDE: + break; + case ZS_STATE_ERROR: + if (s->error.fatal) { + return -1; + } + break; + default: + // Return if stop or end of file. + return 0; + } + + // Check for the end of the input. + if (s->input.current != s->input.end) { + // Try to parse another item. + s->state = ZS_STATE_NONE; + wrap_t wrap = WRAP_NONE; + parse(s, &wrap); + + // Finish if nothing was parsed. + if (s->state == ZS_STATE_NONE) { + // Parse the final block. + if (set_input_string(s, "\n", 1, true) != 0) { + return -1; + } + parse(s, &wrap); + if (s->state == ZS_STATE_NONE) { + s->state = ZS_STATE_EOF; + } + } + } else { + s->state = ZS_STATE_EOF; + } + + return 0; +} + +__attribute__((visibility("default"))) +int zs_parse_all( + zs_scanner_t *s) +{ + if (s == NULL) { + return -1; + } + + s->process.automatic = true; + + // Parse input block. + wrap_t wrap = WRAP_NONE; + parse(s, &wrap); + + // Parse trailing newline-char block if it makes sense. + if (s->state != ZS_STATE_STOP && !s->error.fatal) { + if (set_input_string(s, "\n", 1, true) != 0) { + return -1; + } + parse(s, &wrap); + } + + // Check if any errors have occurred. + if (s->error.counter > 0) { + return -1; + } + + return 0; +} diff --git a/src/libzscanner/scanner.c.t0 b/src/libzscanner/scanner.c.t0 new file mode 100644 index 0000000..29205f6 --- /dev/null +++ b/src/libzscanner/scanner.c.t0 @@ -0,0 +1,8509 @@ + +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <arpa/inet.h> +#include <fcntl.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <libgen.h> +#include <math.h> +#include <netinet/in.h> +#include <sys/socket.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include "libzscanner/scanner.h" +#include "libzscanner/functions.h" +#include "libknot/descriptor.h" + +/*! \brief Maximal length of rdata item. */ +#define MAX_ITEM_LENGTH 255 + +/*! \brief Latitude value for equator (2^31). */ +#define LOC_LAT_ZERO (uint32_t)2147483648 +/*! \brief Longitude value for meridian (2^31). */ +#define LOC_LONG_ZERO (uint32_t)2147483648 +/*! \brief Zero level altitude value. */ +#define LOC_ALT_ZERO (uint32_t)10000000 + +/*! \brief Shorthand for setting warning data. */ +#define WARN(err_code) { s->error.code = err_code; } +/*! \brief Shorthand for setting error data. */ +#define ERR(err_code) { WARN(err_code); s->error.fatal = true; } +/*! \brief Shorthand for error reset. */ +#define NOERR { WARN(ZS_OK); s->error.fatal = false; } + +/*! + * \brief Writes record type number to r_data. + * + * \param type Type number. + * \param rdata_tail Position where to write type number to. + */ +static inline void type_num(const uint16_t type, uint8_t **rdata_tail) +{ + *((uint16_t *)*rdata_tail) = htons(type); + *rdata_tail += 2; +} + +/*! + * \brief Sets bit to bitmap window. + * + * \param type Type number. + * \param s Scanner context. + */ +static inline void window_add_bit(const uint16_t type, zs_scanner_t *s) { + uint8_t win = type / 256; + uint8_t bit_pos = type % 256; + uint8_t byte_pos = bit_pos / 8; + + ((s->windows[win]).bitmap)[byte_pos] |= 128 >> (bit_pos % 8); + + if ((s->windows[win]).length < byte_pos + 1) { + (s->windows[win]).length = byte_pos + 1; + } + + if (s->last_window < win) { + s->last_window = win; + } +} + +// Include scanner file (in Ragel). + +static const short _zone_scanner_actions[] = { + 0, 1, 0, 1, 1, 1, 2, 1, + 3, 1, 4, 1, 5, 1, 7, 1, + 8, 1, 10, 1, 12, 1, 13, 1, + 14, 1, 16, 1, 17, 1, 22, 1, + 23, 1, 25, 1, 26, 1, 28, 1, + 30, 1, 32, 1, 33, 1, 42, 1, + 43, 1, 44, 1, 46, 1, 48, 1, + 49, 1, 50, 1, 51, 1, 55, 1, + 56, 1, 58, 1, 60, 1, 62, 1, + 63, 1, 65, 1, 69, 1, 70, 1, + 73, 1, 74, 1, 77, 1, 80, 1, + 82, 1, 83, 1, 84, 1, 85, 1, + 86, 1, 87, 1, 88, 1, 89, 1, + 91, 1, 93, 1, 95, 1, 99, 1, + 100, 1, 104, 1, 105, 1, 109, 1, + 110, 1, 111, 1, 112, 1, 113, 1, + 114, 1, 115, 1, 116, 1, 117, 1, + 118, 1, 119, 1, 120, 1, 121, 1, + 122, 1, 124, 1, 125, 1, 126, 1, + 127, 1, 154, 1, 155, 1, 156, 1, + 157, 1, 158, 1, 159, 1, 160, 1, + 161, 1, 162, 1, 163, 1, 164, 1, + 165, 1, 166, 1, 167, 1, 168, 1, + 169, 1, 170, 1, 171, 1, 172, 1, + 173, 1, 174, 1, 175, 1, 176, 1, + 177, 1, 178, 1, 179, 1, 180, 1, + 181, 1, 182, 1, 183, 1, 184, 1, + 185, 1, 186, 1, 187, 1, 188, 1, + 189, 1, 190, 1, 191, 1, 192, 1, + 193, 1, 194, 1, 195, 1, 196, 1, + 197, 1, 198, 1, 199, 1, 200, 1, + 201, 1, 202, 1, 203, 1, 204, 1, + 205, 1, 206, 1, 207, 1, 208, 1, + 209, 1, 210, 1, 211, 1, 212, 1, + 213, 1, 214, 1, 215, 1, 216, 1, + 217, 1, 218, 1, 219, 1, 220, 1, + 221, 1, 222, 1, 223, 1, 224, 1, + 225, 1, 226, 1, 227, 1, 228, 1, + 229, 1, 230, 1, 231, 1, 232, 1, + 233, 1, 234, 1, 235, 1, 236, 1, + 237, 1, 238, 1, 239, 1, 241, 1, + 243, 1, 244, 1, 245, 1, 246, 1, + 253, 1, 254, 1, 259, 1, 261, 1, + 266, 1, 269, 1, 270, 1, 271, 1, + 272, 1, 274, 1, 275, 1, 276, 1, + 277, 1, 279, 2, 0, 46, 2, 1, + 0, 2, 1, 275, 2, 1, 322, 2, + 2, 5, 2, 2, 7, 2, 3, 5, + 2, 3, 7, 2, 4, 0, 2, 4, + 5, 2, 4, 7, 2, 4, 275, 2, + 5, 2, 2, 5, 3, 2, 5, 4, + 2, 5, 7, 2, 6, 1, 2, 7, + 1, 2, 7, 2, 2, 7, 3, 2, + 7, 4, 2, 7, 5, 2, 7, 29, + 2, 8, 69, 2, 8, 73, 2, 9, + 10, 2, 11, 1, 2, 12, 13, 2, + 15, 16, 2, 17, 13, 2, 17, 14, + 2, 18, 23, 2, 18, 73, 2, 18, + 80, 2, 19, 0, 2, 19, 7, 2, + 21, 0, 2, 22, 12, 2, 25, 65, + 2, 25, 104, 2, 25, 115, 2, 26, + 0, 2, 26, 1, 2, 26, 2, 2, + 26, 3, 2, 26, 4, 2, 26, 242, + 2, 27, 24, 2, 28, 1, 2, 28, + 2, 2, 28, 3, 2, 28, 4, 2, + 28, 7, 2, 28, 89, 2, 29, 7, + 2, 30, 8, 2, 30, 274, 2, 30, + 279, 2, 31, 24, 2, 32, 0, 2, + 32, 1, 2, 32, 2, 2, 32, 3, + 2, 32, 4, 2, 32, 242, 2, 33, + 38, 2, 34, 33, 2, 35, 69, 2, + 35, 259, 2, 35, 274, 2, 35, 279, + 2, 39, 247, 2, 39, 248, 2, 39, + 249, 2, 39, 250, 2, 39, 251, 2, + 39, 252, 2, 42, 0, 2, 42, 1, + 2, 42, 2, 2, 42, 3, 2, 42, + 4, 2, 43, 0, 2, 43, 1, 2, + 43, 2, 2, 43, 3, 2, 43, 4, + 2, 44, 0, 2, 44, 1, 2, 44, + 2, 2, 44, 3, 2, 44, 4, 2, + 45, 273, 2, 46, 1, 2, 46, 2, + 2, 46, 3, 2, 46, 4, 2, 53, + 7, 2, 53, 44, 2, 53, 89, 2, + 54, 55, 2, 56, 1, 2, 56, 2, + 2, 56, 3, 2, 56, 4, 2, 57, + 274, 2, 58, 0, 2, 59, 60, 2, + 61, 62, 2, 63, 0, 2, 63, 58, + 2, 67, 0, 2, 67, 274, 2, 71, + 22, 2, 75, 65, 2, 76, 7, 2, + 77, 8, 2, 78, 22, 2, 80, 8, + 2, 86, 87, 2, 88, 1, 2, 88, + 2, 2, 88, 3, 2, 88, 4, 2, + 89, 1, 2, 89, 2, 2, 89, 3, + 2, 89, 4, 2, 89, 7, 2, 90, + 91, 2, 92, 274, 2, 93, 94, 2, + 95, 96, 2, 97, 98, 2, 97, 99, + 2, 97, 100, 2, 101, 102, 2, 103, + 274, 2, 106, 274, 2, 107, 0, 2, + 123, 274, 2, 125, 0, 2, 126, 0, + 2, 127, 0, 2, 128, 0, 2, 129, + 0, 2, 130, 0, 2, 131, 0, 2, + 132, 0, 2, 133, 0, 2, 134, 0, + 2, 135, 0, 2, 136, 0, 2, 137, + 0, 2, 138, 0, 2, 139, 0, 2, + 140, 0, 2, 141, 0, 2, 142, 0, + 2, 143, 0, 2, 144, 0, 2, 145, + 0, 2, 146, 0, 2, 147, 0, 2, + 148, 0, 2, 149, 0, 2, 150, 0, + 2, 151, 274, 2, 152, 274, 2, 153, + 274, 2, 154, 1, 2, 154, 2, 2, + 154, 3, 2, 154, 4, 2, 155, 1, + 2, 155, 2, 2, 155, 3, 2, 155, + 4, 2, 156, 1, 2, 156, 2, 2, + 156, 3, 2, 156, 4, 2, 157, 1, + 2, 157, 2, 2, 157, 3, 2, 157, + 4, 2, 158, 1, 2, 158, 2, 2, + 158, 3, 2, 158, 4, 2, 159, 1, + 2, 159, 2, 2, 159, 3, 2, 159, + 4, 2, 160, 1, 2, 160, 2, 2, + 160, 3, 2, 160, 4, 2, 161, 1, + 2, 161, 2, 2, 161, 3, 2, 161, + 4, 2, 162, 1, 2, 162, 2, 2, + 162, 3, 2, 162, 4, 2, 163, 1, + 2, 163, 2, 2, 163, 3, 2, 163, + 4, 2, 164, 1, 2, 164, 2, 2, + 164, 3, 2, 164, 4, 2, 165, 1, + 2, 165, 2, 2, 165, 3, 2, 165, + 4, 2, 166, 1, 2, 166, 2, 2, + 166, 3, 2, 166, 4, 2, 167, 1, + 2, 167, 2, 2, 167, 3, 2, 167, + 4, 2, 168, 1, 2, 168, 2, 2, + 168, 3, 2, 168, 4, 2, 169, 1, + 2, 169, 2, 2, 169, 3, 2, 169, + 4, 2, 170, 1, 2, 170, 2, 2, + 170, 3, 2, 170, 4, 2, 171, 1, + 2, 171, 2, 2, 171, 3, 2, 171, + 4, 2, 172, 1, 2, 172, 2, 2, + 172, 3, 2, 172, 4, 2, 173, 1, + 2, 173, 2, 2, 173, 3, 2, 173, + 4, 2, 174, 1, 2, 174, 2, 2, + 174, 3, 2, 174, 4, 2, 175, 1, + 2, 175, 2, 2, 175, 3, 2, 175, + 4, 2, 176, 1, 2, 176, 2, 2, + 176, 3, 2, 176, 4, 2, 177, 1, + 2, 177, 2, 2, 177, 3, 2, 177, + 4, 2, 178, 1, 2, 178, 2, 2, + 178, 3, 2, 178, 4, 2, 179, 1, + 2, 179, 2, 2, 179, 3, 2, 179, + 4, 2, 180, 1, 2, 180, 2, 2, + 180, 3, 2, 180, 4, 2, 181, 1, + 2, 181, 2, 2, 181, 3, 2, 181, + 4, 2, 182, 1, 2, 182, 2, 2, + 182, 3, 2, 182, 4, 2, 183, 1, + 2, 183, 2, 2, 183, 3, 2, 183, + 4, 2, 184, 1, 2, 184, 2, 2, + 184, 3, 2, 184, 4, 2, 185, 1, + 2, 185, 2, 2, 185, 3, 2, 185, + 4, 2, 186, 1, 2, 186, 2, 2, + 186, 3, 2, 186, 4, 2, 187, 1, + 2, 187, 2, 2, 187, 3, 2, 187, + 4, 2, 188, 1, 2, 188, 2, 2, + 188, 3, 2, 188, 4, 2, 189, 1, + 2, 189, 2, 2, 189, 3, 2, 189, + 4, 2, 190, 1, 2, 190, 2, 2, + 190, 3, 2, 190, 4, 2, 191, 1, + 2, 191, 2, 2, 191, 3, 2, 191, + 4, 2, 192, 1, 2, 192, 2, 2, + 192, 3, 2, 192, 4, 2, 193, 1, + 2, 193, 2, 2, 193, 3, 2, 193, + 4, 2, 194, 1, 2, 194, 2, 2, + 194, 3, 2, 194, 4, 2, 195, 1, + 2, 195, 2, 2, 195, 3, 2, 195, + 4, 2, 196, 1, 2, 196, 2, 2, + 196, 3, 2, 196, 4, 2, 197, 1, + 2, 197, 2, 2, 197, 3, 2, 197, + 4, 2, 198, 1, 2, 198, 2, 2, + 198, 3, 2, 198, 4, 2, 199, 1, + 2, 199, 2, 2, 199, 3, 2, 199, + 4, 2, 200, 1, 2, 200, 2, 2, + 200, 3, 2, 200, 4, 2, 201, 1, + 2, 201, 2, 2, 201, 3, 2, 201, + 4, 2, 202, 1, 2, 202, 2, 2, + 202, 3, 2, 202, 4, 2, 203, 1, + 2, 203, 2, 2, 203, 3, 2, 203, + 4, 2, 204, 1, 2, 204, 2, 2, + 204, 3, 2, 204, 4, 2, 205, 1, + 2, 205, 2, 2, 205, 3, 2, 205, + 4, 2, 206, 1, 2, 206, 2, 2, + 206, 3, 2, 206, 4, 2, 207, 1, + 2, 207, 2, 2, 207, 3, 2, 207, + 4, 2, 208, 1, 2, 208, 2, 2, + 208, 3, 2, 208, 4, 2, 209, 1, + 2, 209, 2, 2, 209, 3, 2, 209, + 4, 2, 210, 1, 2, 210, 2, 2, + 210, 3, 2, 210, 4, 2, 211, 1, + 2, 211, 2, 2, 211, 3, 2, 211, + 4, 2, 212, 1, 2, 212, 2, 2, + 212, 3, 2, 212, 4, 2, 213, 1, + 2, 213, 2, 2, 213, 3, 2, 213, + 4, 2, 214, 1, 2, 214, 2, 2, + 214, 3, 2, 214, 4, 2, 215, 1, + 2, 215, 2, 2, 215, 3, 2, 215, + 4, 2, 216, 1, 2, 216, 2, 2, + 216, 3, 2, 216, 4, 2, 217, 1, + 2, 217, 2, 2, 217, 3, 2, 217, + 4, 2, 218, 1, 2, 218, 2, 2, + 218, 3, 2, 218, 4, 2, 219, 1, + 2, 219, 2, 2, 219, 3, 2, 219, + 4, 2, 220, 1, 2, 220, 2, 2, + 220, 3, 2, 220, 4, 2, 221, 1, + 2, 221, 2, 2, 221, 3, 2, 221, + 4, 2, 222, 1, 2, 222, 2, 2, + 222, 3, 2, 222, 4, 2, 223, 1, + 2, 223, 2, 2, 223, 3, 2, 223, + 4, 2, 224, 1, 2, 224, 2, 2, + 224, 3, 2, 224, 4, 2, 225, 1, + 2, 225, 2, 2, 225, 3, 2, 225, + 4, 2, 226, 1, 2, 226, 2, 2, + 226, 3, 2, 226, 4, 2, 227, 1, + 2, 227, 2, 2, 227, 3, 2, 227, + 4, 2, 228, 1, 2, 228, 2, 2, + 228, 3, 2, 228, 4, 2, 229, 1, + 2, 229, 2, 2, 229, 3, 2, 229, + 4, 2, 230, 1, 2, 230, 2, 2, + 230, 3, 2, 230, 4, 2, 231, 1, + 2, 231, 2, 2, 231, 3, 2, 231, + 4, 2, 232, 1, 2, 232, 2, 2, + 232, 3, 2, 232, 4, 2, 233, 1, + 2, 233, 2, 2, 233, 3, 2, 233, + 4, 2, 234, 1, 2, 234, 2, 2, + 234, 3, 2, 234, 4, 2, 235, 1, + 2, 235, 2, 2, 235, 3, 2, 235, + 4, 2, 236, 1, 2, 236, 2, 2, + 236, 3, 2, 236, 4, 2, 237, 1, + 2, 237, 2, 2, 237, 3, 2, 237, + 4, 2, 238, 1, 2, 238, 2, 2, + 238, 3, 2, 238, 4, 2, 239, 1, + 2, 239, 2, 2, 239, 3, 2, 239, + 4, 2, 240, 0, 2, 243, 1, 2, + 243, 2, 2, 243, 3, 2, 243, 4, + 2, 244, 1, 2, 244, 2, 2, 244, + 3, 2, 244, 4, 2, 245, 1, 2, + 245, 2, 2, 245, 3, 2, 245, 4, + 2, 246, 1, 2, 246, 2, 2, 246, + 3, 2, 246, 4, 2, 253, 1, 2, + 253, 2, 2, 253, 3, 2, 253, 4, + 2, 254, 1, 2, 254, 2, 2, 254, + 3, 2, 254, 4, 2, 257, 0, 2, + 258, 274, 2, 260, 104, 2, 265, 104, + 2, 274, 8, 2, 275, 1, 2, 275, + 4, 2, 278, 275, 2, 279, 8, 2, + 279, 274, 2, 280, 273, 2, 281, 273, + 2, 282, 273, 2, 283, 273, 2, 284, + 273, 2, 285, 273, 2, 286, 273, 2, + 287, 273, 2, 288, 273, 2, 289, 273, + 2, 290, 273, 2, 291, 273, 2, 292, + 273, 2, 293, 273, 2, 294, 273, 2, + 295, 273, 2, 296, 273, 2, 297, 273, + 2, 298, 273, 2, 299, 273, 2, 300, + 273, 2, 301, 273, 2, 302, 273, 2, + 303, 273, 2, 304, 273, 2, 305, 273, + 2, 306, 273, 2, 307, 273, 2, 308, + 273, 2, 309, 273, 2, 310, 273, 2, + 311, 273, 2, 312, 273, 2, 313, 273, + 2, 314, 273, 2, 315, 273, 2, 316, + 273, 2, 317, 273, 2, 318, 273, 2, + 319, 273, 2, 320, 273, 2, 321, 273, + 2, 322, 1, 3, 0, 46, 1, 3, + 0, 46, 2, 3, 0, 46, 3, 3, + 0, 46, 4, 3, 1, 7, 322, 3, + 1, 29, 7, 3, 1, 67, 0, 3, + 1, 107, 0, 3, 1, 240, 0, 3, + 1, 257, 0, 3, 1, 275, 322, 3, + 2, 7, 29, 3, 2, 29, 7, 3, + 3, 7, 29, 3, 3, 29, 7, 3, + 4, 7, 29, 3, 4, 29, 7, 3, + 4, 67, 0, 3, 4, 107, 0, 3, + 4, 240, 0, 3, 4, 257, 0, 3, + 4, 275, 7, 3, 5, 7, 2, 3, + 5, 7, 3, 3, 5, 7, 4, 3, + 6, 1, 322, 3, 6, 322, 1, 3, + 7, 2, 5, 3, 7, 2, 29, 3, + 7, 3, 5, 3, 7, 3, 29, 3, + 7, 4, 5, 3, 7, 4, 29, 3, + 7, 322, 1, 3, 9, 11, 1, 3, + 14, 20, 0, 3, 19, 7, 2, 3, + 19, 7, 3, 3, 19, 7, 4, 3, + 19, 79, 7, 3, 22, 12, 13, 3, + 26, 67, 0, 3, 28, 1, 89, 3, + 28, 2, 7, 3, 28, 2, 89, 3, + 28, 3, 7, 3, 28, 3, 89, 3, + 28, 4, 7, 3, 28, 4, 89, 3, + 28, 89, 7, 3, 28, 278, 275, 3, + 28, 280, 273, 3, 29, 1, 7, 3, + 29, 2, 7, 3, 29, 3, 7, 3, + 29, 4, 7, 3, 30, 85, 8, 3, + 30, 274, 8, 3, 30, 279, 8, 3, + 30, 279, 274, 3, 32, 1, 0, 3, + 32, 4, 0, 3, 34, 33, 275, 3, + 35, 151, 274, 3, 35, 153, 274, 3, + 35, 279, 8, 3, 35, 279, 274, 3, + 37, 39, 247, 3, 37, 39, 248, 3, + 37, 39, 249, 3, 37, 39, 250, 3, + 37, 39, 251, 3, 37, 39, 252, 3, + 39, 247, 1, 3, 39, 247, 2, 3, + 39, 247, 3, 3, 39, 247, 4, 3, + 39, 248, 1, 3, 39, 248, 2, 3, + 39, 248, 3, 3, 39, 248, 4, 3, + 39, 249, 1, 3, 39, 249, 2, 3, + 39, 249, 3, 3, 39, 249, 4, 3, + 39, 250, 1, 3, 39, 250, 2, 3, + 39, 250, 3, 3, 39, 250, 4, 3, + 39, 251, 1, 3, 39, 251, 2, 3, + 39, 251, 3, 3, 39, 251, 4, 3, + 39, 252, 1, 3, 39, 252, 2, 3, + 39, 252, 3, 3, 39, 252, 4, 3, + 45, 273, 1, 3, 45, 273, 2, 3, + 45, 273, 3, 3, 45, 273, 4, 3, + 45, 273, 275, 3, 47, 35, 69, 3, + 47, 35, 274, 3, 47, 35, 279, 3, + 52, 34, 33, 3, 53, 7, 2, 3, + 53, 7, 3, 3, 53, 7, 4, 3, + 53, 44, 0, 3, 53, 44, 1, 3, + 53, 44, 2, 3, 53, 44, 3, 3, + 53, 44, 4, 3, 53, 89, 1, 3, + 53, 89, 2, 3, 53, 89, 3, 3, + 53, 89, 4, 3, 63, 58, 0, 3, + 64, 59, 60, 3, 66, 25, 65, 3, + 68, 0, 1, 3, 71, 22, 12, 3, + 72, 0, 1, 3, 76, 2, 7, 3, + 76, 3, 7, 3, 76, 4, 7, 3, + 76, 7, 4, 3, 78, 22, 12, 3, + 81, 0, 1, 3, 84, 7, 1, 3, + 84, 7, 4, 3, 84, 27, 24, 3, + 84, 29, 7, 3, 86, 27, 24, 3, + 86, 87, 275, 3, 89, 2, 7, 3, + 89, 3, 7, 3, 89, 4, 7, 3, + 92, 103, 274, 3, 92, 151, 274, 3, + 93, 94, 0, 3, 93, 94, 1, 3, + 93, 94, 2, 3, 93, 94, 3, 3, + 93, 94, 4, 3, 95, 96, 0, 3, + 95, 96, 1, 3, 95, 96, 2, 3, + 95, 96, 3, 3, 95, 96, 4, 3, + 101, 102, 0, 3, 101, 102, 1, 3, + 101, 102, 2, 3, 101, 102, 3, 3, + 101, 102, 4, 3, 106, 108, 259, 3, + 106, 268, 274, 3, 151, 152, 274, 3, + 196, 240, 0, 3, 197, 240, 0, 3, + 198, 240, 0, 3, 199, 240, 0, 3, + 200, 240, 0, 3, 201, 240, 0, 3, + 202, 240, 0, 3, 203, 240, 0, 3, + 204, 240, 0, 3, 205, 240, 0, 3, + 206, 240, 0, 3, 207, 240, 0, 3, + 208, 240, 0, 3, 209, 240, 0, 3, + 210, 240, 0, 3, 211, 240, 0, 3, + 212, 240, 0, 3, 213, 240, 0, 3, + 214, 240, 0, 3, 215, 240, 0, 3, + 216, 240, 0, 3, 217, 240, 0, 3, + 218, 240, 0, 3, 219, 240, 0, 3, + 220, 240, 0, 3, 221, 240, 0, 3, + 222, 240, 0, 3, 223, 240, 0, 3, + 224, 240, 0, 3, 225, 240, 0, 3, + 226, 240, 0, 3, 227, 240, 0, 3, + 228, 240, 0, 3, 229, 240, 0, 3, + 230, 240, 0, 3, 231, 240, 0, 3, + 232, 240, 0, 3, 233, 240, 0, 3, + 234, 240, 0, 3, 235, 240, 0, 3, + 236, 240, 0, 3, 237, 240, 0, 3, + 238, 240, 0, 3, 239, 240, 0, 3, + 256, 34, 33, 3, 261, 262, 0, 3, + 261, 263, 0, 3, 264, 106, 274, 3, + 266, 267, 0, 3, 268, 106, 274, 3, + 274, 8, 85, 3, 275, 7, 1, 3, + 275, 7, 4, 3, 275, 27, 24, 3, + 275, 322, 1, 3, 277, 27, 24, 3, + 279, 274, 8, 3, 280, 273, 1, 3, + 280, 273, 2, 3, 280, 273, 3, 3, + 280, 273, 4, 3, 280, 273, 7, 3, + 280, 273, 275, 3, 281, 273, 1, 3, + 281, 273, 2, 3, 281, 273, 3, 3, + 281, 273, 4, 3, 281, 273, 275, 3, + 282, 273, 1, 3, 282, 273, 2, 3, + 282, 273, 3, 3, 282, 273, 4, 3, + 282, 273, 275, 3, 283, 273, 1, 3, + 283, 273, 2, 3, 283, 273, 3, 3, + 283, 273, 4, 3, 283, 273, 275, 3, + 284, 273, 1, 3, 284, 273, 2, 3, + 284, 273, 3, 3, 284, 273, 4, 3, + 284, 273, 275, 3, 285, 273, 1, 3, + 285, 273, 2, 3, 285, 273, 3, 3, + 285, 273, 4, 3, 285, 273, 275, 3, + 286, 273, 1, 3, 286, 273, 2, 3, + 286, 273, 3, 3, 286, 273, 4, 3, + 286, 273, 275, 3, 287, 273, 1, 3, + 287, 273, 2, 3, 287, 273, 3, 3, + 287, 273, 4, 3, 287, 273, 275, 3, + 288, 273, 1, 3, 288, 273, 2, 3, + 288, 273, 3, 3, 288, 273, 4, 3, + 288, 273, 275, 3, 289, 273, 1, 3, + 289, 273, 2, 3, 289, 273, 3, 3, + 289, 273, 4, 3, 289, 273, 275, 3, + 290, 273, 1, 3, 290, 273, 2, 3, + 290, 273, 3, 3, 290, 273, 4, 3, + 290, 273, 275, 3, 291, 273, 1, 3, + 291, 273, 2, 3, 291, 273, 3, 3, + 291, 273, 4, 3, 291, 273, 275, 3, + 292, 273, 1, 3, 292, 273, 2, 3, + 292, 273, 3, 3, 292, 273, 4, 3, + 292, 273, 275, 3, 293, 273, 1, 3, + 293, 273, 2, 3, 293, 273, 3, 3, + 293, 273, 4, 3, 293, 273, 275, 3, + 294, 273, 1, 3, 294, 273, 2, 3, + 294, 273, 3, 3, 294, 273, 4, 3, + 294, 273, 275, 3, 295, 273, 1, 3, + 295, 273, 2, 3, 295, 273, 3, 3, + 295, 273, 4, 3, 295, 273, 275, 3, + 296, 273, 1, 3, 296, 273, 2, 3, + 296, 273, 3, 3, 296, 273, 4, 3, + 296, 273, 275, 3, 297, 273, 1, 3, + 297, 273, 2, 3, 297, 273, 3, 3, + 297, 273, 4, 3, 297, 273, 275, 3, + 298, 273, 1, 3, 298, 273, 2, 3, + 298, 273, 3, 3, 298, 273, 4, 3, + 298, 273, 275, 3, 299, 273, 1, 3, + 299, 273, 2, 3, 299, 273, 3, 3, + 299, 273, 4, 3, 299, 273, 275, 3, + 300, 273, 1, 3, 300, 273, 2, 3, + 300, 273, 3, 3, 300, 273, 4, 3, + 300, 273, 275, 3, 301, 273, 1, 3, + 301, 273, 2, 3, 301, 273, 3, 3, + 301, 273, 4, 3, 301, 273, 275, 3, + 302, 273, 1, 3, 302, 273, 2, 3, + 302, 273, 3, 3, 302, 273, 4, 3, + 302, 273, 275, 3, 303, 273, 1, 3, + 303, 273, 2, 3, 303, 273, 3, 3, + 303, 273, 4, 3, 303, 273, 275, 3, + 304, 273, 1, 3, 304, 273, 2, 3, + 304, 273, 3, 3, 304, 273, 4, 3, + 304, 273, 275, 3, 305, 273, 1, 3, + 305, 273, 2, 3, 305, 273, 3, 3, + 305, 273, 4, 3, 305, 273, 275, 3, + 306, 273, 1, 3, 306, 273, 2, 3, + 306, 273, 3, 3, 306, 273, 4, 3, + 306, 273, 275, 3, 307, 273, 1, 3, + 307, 273, 2, 3, 307, 273, 3, 3, + 307, 273, 4, 3, 307, 273, 275, 3, + 308, 273, 1, 3, 308, 273, 2, 3, + 308, 273, 3, 3, 308, 273, 4, 3, + 308, 273, 275, 3, 309, 273, 1, 3, + 309, 273, 2, 3, 309, 273, 3, 3, + 309, 273, 4, 3, 309, 273, 275, 3, + 310, 273, 1, 3, 310, 273, 2, 3, + 310, 273, 3, 3, 310, 273, 4, 3, + 310, 273, 275, 3, 311, 273, 1, 3, + 311, 273, 2, 3, 311, 273, 3, 3, + 311, 273, 4, 3, 311, 273, 275, 3, + 312, 273, 1, 3, 312, 273, 2, 3, + 312, 273, 3, 3, 312, 273, 4, 3, + 312, 273, 275, 3, 313, 273, 1, 3, + 313, 273, 2, 3, 313, 273, 3, 3, + 313, 273, 4, 3, 313, 273, 275, 3, + 314, 273, 1, 3, 314, 273, 2, 3, + 314, 273, 3, 3, 314, 273, 4, 3, + 314, 273, 275, 3, 315, 273, 1, 3, + 315, 273, 2, 3, 315, 273, 3, 3, + 315, 273, 4, 3, 315, 273, 275, 3, + 316, 273, 1, 3, 316, 273, 2, 3, + 316, 273, 3, 3, 316, 273, 4, 3, + 316, 273, 275, 3, 317, 273, 1, 3, + 317, 273, 2, 3, 317, 273, 3, 3, + 317, 273, 4, 3, 317, 273, 275, 3, + 318, 273, 1, 3, 318, 273, 2, 3, + 318, 273, 3, 3, 318, 273, 4, 3, + 318, 273, 275, 3, 319, 273, 1, 3, + 319, 273, 2, 3, 319, 273, 3, 3, + 319, 273, 4, 3, 319, 273, 275, 3, + 320, 273, 1, 3, 320, 273, 2, 3, + 320, 273, 3, 3, 320, 273, 4, 3, + 320, 273, 275, 3, 321, 273, 1, 3, + 321, 273, 2, 3, 321, 273, 3, 3, + 321, 273, 4, 3, 321, 273, 275, 3, + 322, 1, 6, 4, 1, 7, 322, 29, + 4, 1, 275, 29, 7, 4, 4, 275, + 7, 29, 4, 4, 275, 29, 7, 4, + 6, 1, 7, 322, 4, 6, 1, 81, + 0, 4, 6, 68, 0, 1, 4, 6, + 72, 0, 1, 4, 6, 81, 0, 1, + 4, 7, 6, 322, 1, 4, 7, 68, + 0, 1, 4, 7, 322, 1, 6, 4, + 7, 322, 1, 29, 4, 17, 14, 20, + 0, 4, 19, 79, 7, 2, 4, 19, + 79, 7, 3, 4, 19, 79, 7, 4, + 4, 26, 1, 67, 0, 4, 26, 4, + 67, 0, 4, 28, 1, 7, 322, 4, + 28, 1, 278, 275, 4, 28, 1, 280, + 273, 4, 28, 2, 89, 7, 4, 28, + 2, 278, 275, 4, 28, 2, 280, 273, + 4, 28, 3, 89, 7, 4, 28, 3, + 278, 275, 4, 28, 3, 280, 273, 4, + 28, 4, 89, 7, 4, 28, 4, 278, + 275, 4, 28, 4, 280, 273, 4, 28, + 280, 273, 7, 4, 30, 35, 279, 8, + 4, 30, 47, 35, 279, 4, 30, 279, + 274, 8, 4, 34, 33, 27, 24, 4, + 35, 279, 274, 8, 4, 37, 34, 33, + 38, 4, 37, 39, 247, 1, 4, 37, + 39, 247, 2, 4, 37, 39, 247, 3, + 4, 37, 39, 247, 4, 4, 37, 39, + 248, 1, 4, 37, 39, 248, 2, 4, + 37, 39, 248, 3, 4, 37, 39, 248, + 4, 4, 37, 39, 249, 1, 4, 37, + 39, 249, 2, 4, 37, 39, 249, 3, + 4, 37, 39, 249, 4, 4, 37, 39, + 250, 1, 4, 37, 39, 250, 2, 4, + 37, 39, 250, 3, 4, 37, 39, 250, + 4, 4, 37, 39, 251, 1, 4, 37, + 39, 251, 2, 4, 37, 39, 251, 3, + 4, 37, 39, 251, 4, 4, 37, 39, + 252, 1, 4, 37, 39, 252, 2, 4, + 37, 39, 252, 3, 4, 37, 39, 252, + 4, 4, 39, 249, 257, 0, 4, 39, + 250, 257, 0, 4, 39, 251, 257, 0, + 4, 39, 252, 257, 0, 4, 40, 36, + 34, 33, 4, 41, 36, 34, 33, 4, + 45, 273, 1, 275, 4, 45, 273, 4, + 275, 4, 47, 35, 8, 69, 4, 53, + 52, 34, 33, 4, 71, 22, 12, 13, + 4, 78, 22, 12, 13, 4, 84, 29, + 1, 7, 4, 84, 29, 2, 7, 4, + 84, 29, 3, 7, 4, 84, 29, 4, + 7, 4, 86, 87, 27, 24, 4, 89, + 1, 7, 322, 4, 92, 151, 152, 274, + 4, 93, 94, 1, 0, 4, 93, 94, + 4, 0, 4, 95, 96, 1, 0, 4, + 95, 96, 4, 0, 4, 101, 102, 1, + 0, 4, 101, 102, 4, 0, 4, 196, + 1, 240, 0, 4, 196, 4, 240, 0, + 4, 197, 1, 240, 0, 4, 197, 4, + 240, 0, 4, 198, 1, 240, 0, 4, + 198, 4, 240, 0, 4, 199, 1, 240, + 0, 4, 199, 4, 240, 0, 4, 200, + 1, 240, 0, 4, 200, 4, 240, 0, + 4, 201, 1, 240, 0, 4, 201, 4, + 240, 0, 4, 202, 1, 240, 0, 4, + 202, 4, 240, 0, 4, 203, 1, 240, + 0, 4, 203, 4, 240, 0, 4, 204, + 1, 240, 0, 4, 204, 4, 240, 0, + 4, 205, 1, 240, 0, 4, 205, 4, + 240, 0, 4, 206, 1, 240, 0, 4, + 206, 4, 240, 0, 4, 207, 1, 240, + 0, 4, 207, 4, 240, 0, 4, 208, + 1, 240, 0, 4, 208, 4, 240, 0, + 4, 209, 1, 240, 0, 4, 209, 4, + 240, 0, 4, 210, 1, 240, 0, 4, + 210, 4, 240, 0, 4, 211, 1, 240, + 0, 4, 211, 4, 240, 0, 4, 212, + 1, 240, 0, 4, 212, 4, 240, 0, + 4, 213, 1, 240, 0, 4, 213, 4, + 240, 0, 4, 214, 1, 240, 0, 4, + 214, 4, 240, 0, 4, 215, 1, 240, + 0, 4, 215, 4, 240, 0, 4, 216, + 1, 240, 0, 4, 216, 4, 240, 0, + 4, 217, 1, 240, 0, 4, 217, 4, + 240, 0, 4, 218, 1, 240, 0, 4, + 218, 4, 240, 0, 4, 219, 1, 240, + 0, 4, 219, 4, 240, 0, 4, 220, + 1, 240, 0, 4, 220, 4, 240, 0, + 4, 221, 1, 240, 0, 4, 221, 4, + 240, 0, 4, 222, 1, 240, 0, 4, + 222, 4, 240, 0, 4, 223, 1, 240, + 0, 4, 223, 4, 240, 0, 4, 224, + 1, 240, 0, 4, 224, 4, 240, 0, + 4, 225, 1, 240, 0, 4, 225, 4, + 240, 0, 4, 226, 1, 240, 0, 4, + 226, 4, 240, 0, 4, 227, 1, 240, + 0, 4, 227, 4, 240, 0, 4, 228, + 1, 240, 0, 4, 228, 4, 240, 0, + 4, 229, 1, 240, 0, 4, 229, 4, + 240, 0, 4, 230, 1, 240, 0, 4, + 230, 4, 240, 0, 4, 231, 1, 240, + 0, 4, 231, 4, 240, 0, 4, 232, + 1, 240, 0, 4, 232, 4, 240, 0, + 4, 233, 1, 240, 0, 4, 233, 4, + 240, 0, 4, 234, 1, 240, 0, 4, + 234, 4, 240, 0, 4, 235, 1, 240, + 0, 4, 235, 4, 240, 0, 4, 236, + 1, 240, 0, 4, 236, 4, 240, 0, + 4, 237, 1, 240, 0, 4, 237, 4, + 240, 0, 4, 238, 1, 240, 0, 4, + 238, 4, 240, 0, 4, 239, 1, 240, + 0, 4, 239, 4, 240, 0, 4, 275, + 7, 322, 1, 4, 280, 273, 1, 275, + 4, 280, 273, 2, 7, 4, 280, 273, + 3, 7, 4, 280, 273, 4, 7, 4, + 280, 273, 4, 275, 4, 281, 273, 1, + 275, 4, 281, 273, 4, 275, 4, 282, + 273, 1, 275, 4, 282, 273, 4, 275, + 4, 283, 273, 1, 275, 4, 283, 273, + 4, 275, 4, 284, 273, 1, 275, 4, + 284, 273, 4, 275, 4, 285, 273, 1, + 275, 4, 285, 273, 4, 275, 4, 286, + 273, 1, 275, 4, 286, 273, 4, 275, + 4, 287, 273, 1, 275, 4, 287, 273, + 4, 275, 4, 288, 273, 1, 275, 4, + 288, 273, 4, 275, 4, 289, 273, 1, + 275, 4, 289, 273, 4, 275, 4, 290, + 273, 1, 275, 4, 290, 273, 4, 275, + 4, 291, 273, 1, 275, 4, 291, 273, + 4, 275, 4, 292, 273, 1, 275, 4, + 292, 273, 4, 275, 4, 293, 273, 1, + 275, 4, 293, 273, 4, 275, 4, 294, + 273, 1, 275, 4, 294, 273, 4, 275, + 4, 295, 273, 1, 275, 4, 295, 273, + 4, 275, 4, 296, 273, 1, 275, 4, + 296, 273, 4, 275, 4, 297, 273, 1, + 275, 4, 297, 273, 4, 275, 4, 298, + 273, 1, 275, 4, 298, 273, 4, 275, + 4, 299, 273, 1, 275, 4, 299, 273, + 4, 275, 4, 300, 273, 1, 275, 4, + 300, 273, 4, 275, 4, 301, 273, 1, + 275, 4, 301, 273, 4, 275, 4, 302, + 273, 1, 275, 4, 302, 273, 4, 275, + 4, 303, 273, 1, 275, 4, 303, 273, + 4, 275, 4, 304, 273, 1, 275, 4, + 304, 273, 4, 275, 4, 305, 273, 1, + 275, 4, 305, 273, 4, 275, 4, 306, + 273, 1, 275, 4, 306, 273, 4, 275, + 4, 307, 273, 1, 275, 4, 307, 273, + 4, 275, 4, 308, 273, 1, 275, 4, + 308, 273, 4, 275, 4, 309, 273, 1, + 275, 4, 309, 273, 4, 275, 4, 310, + 273, 1, 275, 4, 310, 273, 4, 275, + 4, 311, 273, 1, 275, 4, 311, 273, + 4, 275, 4, 312, 273, 1, 275, 4, + 312, 273, 4, 275, 4, 313, 273, 1, + 275, 4, 313, 273, 4, 275, 4, 314, + 273, 1, 275, 4, 314, 273, 4, 275, + 4, 315, 273, 1, 275, 4, 315, 273, + 4, 275, 4, 316, 273, 1, 275, 4, + 316, 273, 4, 275, 4, 317, 273, 1, + 275, 4, 317, 273, 4, 275, 4, 318, + 273, 1, 275, 4, 318, 273, 4, 275, + 4, 319, 273, 1, 275, 4, 319, 273, + 4, 275, 4, 320, 273, 1, 275, 4, + 320, 273, 4, 275, 4, 321, 273, 1, + 275, 4, 321, 273, 4, 275, 5, 19, + 7, 72, 0, 1, 5, 28, 1, 89, + 7, 322, 5, 28, 1, 280, 273, 275, + 5, 28, 2, 280, 273, 7, 5, 28, + 3, 280, 273, 7, 5, 28, 4, 280, + 273, 7, 5, 28, 4, 280, 273, 275, + 5, 30, 35, 279, 274, 8, 5, 34, + 33, 275, 27, 24, 5, 37, 39, 249, + 257, 0, 5, 37, 39, 250, 257, 0, + 5, 37, 39, 251, 257, 0, 5, 37, + 39, 252, 257, 0, 5, 39, 249, 1, + 257, 0, 5, 39, 249, 4, 257, 0, + 5, 39, 250, 1, 257, 0, 5, 39, + 250, 4, 257, 0, 5, 39, 251, 1, + 257, 0, 5, 39, 251, 4, 257, 0, + 5, 39, 252, 1, 257, 0, 5, 39, + 252, 4, 257, 0, 5, 47, 35, 279, + 274, 8, 5, 53, 7, 68, 0, 1, + 5, 76, 7, 81, 0, 1, 5, 86, + 87, 275, 27, 24, 5, 255, 40, 36, + 34, 33, 5, 280, 273, 1, 7, 322, + 5, 280, 273, 4, 275, 7, 5, 280, + 273, 275, 7, 4, 6, 19, 79, 7, + 81, 0, 1, 6, 28, 1, 280, 273, + 7, 322, 6, 28, 4, 280, 273, 275, + 7, 6, 30, 47, 35, 279, 274, 8, + 6, 37, 39, 249, 1, 257, 0, 6, + 37, 39, 249, 4, 257, 0, 6, 37, + 39, 250, 1, 257, 0, 6, 37, 39, + 250, 4, 257, 0, 6, 37, 39, 251, + 1, 257, 0, 6, 37, 39, 251, 4, + 257, 0, 6, 37, 39, 252, 1, 257, + 0, 6, 37, 39, 252, 4, 257, 0, + 6, 280, 273, 1, 275, 7, 322, 6, + 280, 273, 275, 7, 322, 1, 7, 28, + 1, 280, 273, 275, 7, 322 +}; + +static const short _zone_scanner_cond_offsets[] = { + 0, 0, 2, 4, 6, 8, 10, 12, + 14, 14, 14, 17, 19, 21, 24, 26, + 28, 30, 30, 30, 32, 37, 42, 42, + 42, 42, 42, 42, 44, 46, 46, 46, + 48, 48, 48, 48, 50, 50, 50, 50, + 50, 52, 52, 52, 52, 54, 54, 54, + 54, 56, 58, 58, 58, 58, 58, 60, + 60, 62, 62, 62, 62, 62, 64, 64, + 66, 68, 68, 68, 68, 68, 68, 68, + 68, 70, 70, 70, 72, 74, 74, 74, + 76, 76, 78, 78, 80, 82, 82, 82, + 82, 82, 84, 86, 86, 86, 86, 86, + 88, 88, 90, 92, 92, 94, 96, 96, + 96, 96, 96, 98, 98, 98, 100, 100, + 102, 102, 102, 102, 104, 106, 106, 106, + 108, 108, 110, 110, 112, 112, 112, 112, + 114, 114, 114, 114, 116, 116, 118, 118, + 118, 118, 120, 120, 120, 122, 125, 127, + 127, 129, 131, 133, 135, 135, 137, 140, + 140, 140, 140, 140, 140, 140, 140, 140, + 140, 142, 144, 147, 149, 151, 154, 156, + 156, 158, 161, 163, 165, 167, 169, 171, + 174, 176, 178, 180, 182, 184, 186, 188, + 190, 192, 194, 196, 198, 200, 202, 204, + 206, 208, 211, 213, 213, 213, 213, 213, + 213, 213, 213, 213, 213, 215, 217, 219, + 222, 224, 226, 228, 230, 232, 234, 236, + 238, 240, 242, 244, 246, 248, 250, 252, + 254, 256, 258, 263, 268, 273, 278, 278, + 278, 280, 280, 280, 280, 282, 282, 284, + 287, 289, 291, 296, 301, 306, 311, 314, + 316, 321, 326, 328, 330, 332, 334, 336, + 338, 340, 342, 344, 346, 348, 350, 352, + 354, 357, 360, 362, 365, 365, 365, 365, + 365, 365, 365, 365, 365, 365, 365, 365, + 365, 365, 365, 365, 365, 365, 366, 366, + 366, 366, 366, 367, 369, 371, 373, 375, + 375, 377, 377, 379, 382, 384, 386, 386, + 388, 390, 390, 390, 390, 390, 390, 392, + 395, 397, 399, 401, 403, 403, 405, 407, + 407, 407, 407, 407, 407, 409, 412, 414, + 417, 420, 420, 420, 420, 420, 422, 425, + 425, 427, 429, 431, 431, 431, 433, 436, + 436, 436, 438, 438, 438, 438, 438, 438, + 440, 442, 442, 442, 444, 444, 444, 444, + 446, 446, 446, 446, 446, 448, 448, 448, + 448, 450, 450, 450, 450, 452, 454, 454, + 454, 454, 454, 456, 456, 458, 458, 458, + 458, 458, 460, 460, 460, 460, 460, 460, + 460, 460, 462, 462, 462, 464, 466, 466, + 466, 468, 468, 470, 470, 472, 474, 474, + 474, 474, 474, 476, 478, 478, 478, 478, + 478, 480, 480, 482, 484, 484, 486, 488, + 488, 488, 488, 488, 490, 490, 490, 492, + 492, 494, 494, 494, 494, 496, 498, 498, + 498, 500, 500, 502, 502, 504, 504, 504, + 504, 506, 506, 506, 506, 508, 508, 510, + 510, 510, 510, 512, 512, 512, 514, 514, + 514, 514, 516, 516, 518, 520, 522, 524, + 526, 526, 528, 531, 534, 537, 539, 541, + 543, 545, 545, 547, 550, 553, 555, 558, + 558, 558, 558, 558, 558, 558, 558, 558, + 558, 558, 558, 558, 558, 558, 558, 558, + 558, 558, 558, 558, 558, 558, 558, 558, + 558, 558, 558, 558, 558, 558, 558, 558, + 558, 558, 558, 558, 558, 558, 558, 558, + 558, 558, 558, 558, 558, 558, 558, 558, + 558, 558, 558, 558, 558, 558, 558, 558, + 558, 558, 558, 558, 558, 558, 558, 558, + 558, 558, 558, 558, 558, 558, 558, 558, + 558, 558, 558, 558, 558, 558, 558, 558, + 558, 558, 558, 558, 558, 558, 558, 558, + 558, 558, 558, 558, 558, 558, 558, 558, + 558, 558, 558, 558, 558, 558, 558, 558, + 558, 558, 558, 558, 558, 558, 558, 558, + 558, 558, 558, 558, 558, 558, 558, 558, + 558, 558, 558, 558, 558, 558, 558, 558, + 558, 558, 558, 558, 558, 558, 558, 558, + 558, 558, 558, 558, 558, 558, 558, 558, + 558, 558, 558, 558, 558, 558, 560, 562, + 564, 566, 568, 570, 572, 574, 576, 578, + 580, 582, 582, 582, 582, 582, 585, 587, + 587, 589, 592, 594, 594, 596, 599, 601, + 601, 603, 606, 609, 612, 612, 614, 616, + 616, 619, 619, 621, 623, 623, 626, 626, + 628, 630, 630, 633, 633, 635, 637, 640, + 640, 640, 640, 642, 644, 646, 648, 650, + 652, 654, 656, 658, 660, 662, 664, 666, + 668, 670, 672, 672, 674, 676, 678, 680, + 682, 684, 686, 688, 691, 693, 695, 698, + 700, 702, 704, 707, 709, 711, 713, 716, + 718, 720, 722, 725, 727, 730, 732, 734, + 737, 740, 743, 745, 748, 750, 752, 755, + 758, 758, 760, 762, 764, 766, 768, 770, + 770, 773, 776, 779, 779, 781, 783, 785, + 787, 789, 791, 793, 795, 797, 799, 799, + 802, 805, 808, 811, 814, 814, 816, 818, + 820, 822, 824, 826, 828, 831, 834, 837, + 839, 839, 839, 839, 839, 839, 841, 844, + 844, 844, 844, 844, 846, 848, 850, 852, + 854, 856, 856, 858, 861, 864, 867, 870, + 870, 872, 874, 876, 878, 878, 880, 883, + 886, 889, 889, 891, 893, 895, 897, 899, + 901, 907, 918, 920, 923, 929, 932, 943, + 946, 949, 952, 954, 956, 958, 960, 966, + 969, 972, 974, 976, 978, 980, 986, 989, + 992, 994, 996, 998, 1000, 1006, 1009, 1012, + 1015, 1015, 1017, 1019, 1021, 1023, 1025, 1027, + 1029, 1031, 1033, 1035, 1037, 1039, 1041, 1043, + 1045, 1047, 1049, 1052, 1055, 1058, 1061, 1064, + 1067, 1070, 1073, 1073, 1073, 1075, 1075, 1075, + 1075, 1077, 1077, 1079, 1079, 1079, 1081, 1081, + 1081, 1081, 1081, 1081, 1083, 1085, 1085, 1085, + 1087, 1087, 1087, 1087, 1089, 1089, 1089, 1089, + 1089, 1091, 1091, 1091, 1091, 1093, 1093, 1093, + 1093, 1095, 1097, 1097, 1097, 1097, 1097, 1099, + 1099, 1101, 1101, 1101, 1101, 1101, 1103, 1103, + 1103, 1103, 1103, 1103, 1103, 1103, 1105, 1105, + 1105, 1107, 1109, 1109, 1109, 1111, 1111, 1113, + 1113, 1115, 1117, 1117, 1117, 1117, 1117, 1119, + 1121, 1121, 1121, 1121, 1121, 1123, 1123, 1125, + 1127, 1127, 1129, 1131, 1131, 1131, 1131, 1131, + 1133, 1133, 1133, 1135, 1135, 1137, 1137, 1137, + 1137, 1139, 1141, 1141, 1141, 1143, 1143, 1145, + 1145, 1147, 1147, 1147, 1147, 1149, 1149, 1149, + 1149, 1151, 1151, 1153, 1153, 1153, 1153, 1155, + 1155, 1155, 1157, 1157, 1157, 1157, 1157, 1159, + 1161, 1163, 1165, 1167, 1169, 1171, 1174, 1177, + 1180, 1180, 1182, 1182, 1184, 1186, 1188, 1190, + 1192, 1194, 1196, 1198, 1198, 1198, 1198, 1198, + 1198, 1198, 1198, 1198, 1198, 1198, 1198, 1198, + 1198, 1198, 1201, 1201, 1203, 1206, 1209, 1212, + 1212, 1214, 1216, 1218, 1220, 1222, 1224, 1224, + 1224, 1224, 1227, 1230, 1233, 1233, 1235, 1237, + 1239, 1241, 1243, 1245, 1245, 1247, 1250, 1253, + 1256, 1259, 1259, 1261, 1263, 1263, 1266, 1266, + 1268, 1270, 1270, 1270, 1270, 1270, 1270, 1270, + 1270, 1270, 1270, 1273, 1273, 1273, 1273, 1273, + 1273, 1273, 1273, 1273, 1273, 1273, 1273, 1273, + 1273, 1275, 1277, 1279, 1281, 1281, 1284, 1287, + 1287, 1289, 1291, 1293, 1295, 1295, 1298, 1301, + 1303, 1305, 1307, 1309, 1311, 1313, 1315, 1317, + 1319, 1321, 1321, 1321, 1321, 1321, 1321, 1321, + 1321, 1323, 1323, 1325, 1328, 1328, 1330, 1333, + 1333, 1335, 1338, 1340, 1340, 1342, 1345, 1348, + 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, + 1348, 1350, 1353, 1353, 1353, 1355, 1358, 1360, + 1363, 1365, 1368, 1370, 1373, 1373, 1373, 1373, + 1373, 1375, 1378, 1378, 1380, 1383, 1383, 1385, + 1388, 1388, 1394, 1397, 1408, 1411, 1422, 1425, + 1425, 1425, 1425, 1425, 1425, 1425, 1425, 1427, + 1430, 1430, 1430, 1430, 1430, 1430 +}; + +static const char _zone_scanner_cond_lengths[] = { + 0, 2, 2, 2, 2, 2, 2, 2, + 0, 0, 3, 2, 2, 3, 2, 2, + 2, 0, 0, 2, 5, 5, 0, 0, + 0, 0, 0, 2, 2, 0, 0, 2, + 0, 0, 0, 2, 0, 0, 0, 0, + 2, 0, 0, 0, 2, 0, 0, 0, + 2, 2, 0, 0, 0, 0, 2, 0, + 2, 0, 0, 0, 0, 2, 0, 2, + 2, 0, 0, 0, 0, 0, 0, 0, + 2, 0, 0, 2, 2, 0, 0, 2, + 0, 2, 0, 2, 2, 0, 0, 0, + 0, 2, 2, 0, 0, 0, 0, 2, + 0, 2, 2, 0, 2, 2, 0, 0, + 0, 0, 2, 0, 0, 2, 0, 2, + 0, 0, 0, 2, 2, 0, 0, 2, + 0, 2, 0, 2, 0, 0, 0, 2, + 0, 0, 0, 2, 0, 2, 0, 0, + 0, 2, 0, 0, 2, 3, 2, 0, + 2, 2, 2, 2, 0, 2, 3, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 2, 2, 3, 2, 2, 3, 2, 0, + 2, 3, 2, 2, 2, 2, 2, 3, + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 3, 2, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 2, 2, 2, 3, + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 5, 5, 5, 5, 0, 0, + 2, 0, 0, 0, 2, 0, 2, 3, + 2, 2, 5, 5, 5, 5, 3, 2, + 5, 5, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 2, 3, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 0, 0, + 0, 0, 1, 2, 2, 2, 2, 0, + 2, 0, 2, 3, 2, 2, 0, 2, + 2, 0, 0, 0, 0, 0, 2, 3, + 2, 2, 2, 2, 0, 2, 2, 0, + 0, 0, 0, 0, 2, 3, 2, 3, + 3, 0, 0, 0, 0, 2, 3, 0, + 2, 2, 2, 0, 0, 2, 3, 0, + 0, 2, 0, 0, 0, 0, 0, 2, + 2, 0, 0, 2, 0, 0, 0, 2, + 0, 0, 0, 0, 2, 0, 0, 0, + 2, 0, 0, 0, 2, 2, 0, 0, + 0, 0, 2, 0, 2, 0, 0, 0, + 0, 2, 0, 0, 0, 0, 0, 0, + 0, 2, 0, 0, 2, 2, 0, 0, + 2, 0, 2, 0, 2, 2, 0, 0, + 0, 0, 2, 2, 0, 0, 0, 0, + 2, 0, 2, 2, 0, 2, 2, 0, + 0, 0, 0, 2, 0, 0, 2, 0, + 2, 0, 0, 0, 2, 2, 0, 0, + 2, 0, 2, 0, 2, 0, 0, 0, + 2, 0, 0, 0, 2, 0, 2, 0, + 0, 0, 2, 0, 0, 2, 0, 0, + 0, 2, 0, 2, 2, 2, 2, 2, + 0, 2, 3, 3, 3, 2, 2, 2, + 2, 0, 2, 3, 3, 2, 3, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 0, 0, 0, 0, 3, 2, 0, + 2, 3, 2, 0, 2, 3, 2, 0, + 2, 3, 3, 3, 0, 2, 2, 0, + 3, 0, 2, 2, 0, 3, 0, 2, + 2, 0, 3, 0, 2, 2, 3, 0, + 0, 0, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 0, 2, 2, 2, 2, 2, + 2, 2, 2, 3, 2, 2, 3, 2, + 2, 2, 3, 2, 2, 2, 3, 2, + 2, 2, 3, 2, 3, 2, 2, 3, + 3, 3, 2, 3, 2, 2, 3, 3, + 0, 2, 2, 2, 2, 2, 2, 0, + 3, 3, 3, 0, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 0, 3, + 3, 3, 3, 3, 0, 2, 2, 2, + 2, 2, 2, 2, 3, 3, 3, 2, + 0, 0, 0, 0, 0, 2, 3, 0, + 0, 0, 0, 2, 2, 2, 2, 2, + 2, 0, 2, 3, 3, 3, 3, 0, + 2, 2, 2, 2, 0, 2, 3, 3, + 3, 0, 2, 2, 2, 2, 2, 2, + 6, 11, 2, 3, 6, 3, 11, 3, + 3, 3, 2, 2, 2, 2, 6, 3, + 3, 2, 2, 2, 2, 6, 3, 3, + 2, 2, 2, 2, 6, 3, 3, 3, + 0, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 3, 3, 3, 3, 3, 3, + 3, 3, 0, 0, 2, 0, 0, 0, + 2, 0, 2, 0, 0, 2, 0, 0, + 0, 0, 0, 2, 2, 0, 0, 2, + 0, 0, 0, 2, 0, 0, 0, 0, + 2, 0, 0, 0, 2, 0, 0, 0, + 2, 2, 0, 0, 0, 0, 2, 0, + 2, 0, 0, 0, 0, 2, 0, 0, + 0, 0, 0, 0, 0, 2, 0, 0, + 2, 2, 0, 0, 2, 0, 2, 0, + 2, 2, 0, 0, 0, 0, 2, 2, + 0, 0, 0, 0, 2, 0, 2, 2, + 0, 2, 2, 0, 0, 0, 0, 2, + 0, 0, 2, 0, 2, 0, 0, 0, + 2, 2, 0, 0, 2, 0, 2, 0, + 2, 0, 0, 0, 2, 0, 0, 0, + 2, 0, 2, 0, 0, 0, 2, 0, + 0, 2, 0, 0, 0, 0, 2, 2, + 2, 2, 2, 2, 2, 3, 3, 3, + 0, 2, 0, 2, 2, 2, 2, 2, + 2, 2, 2, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 3, 0, 2, 3, 3, 3, 0, + 2, 2, 2, 2, 2, 2, 0, 0, + 0, 3, 3, 3, 0, 2, 2, 2, + 2, 2, 2, 0, 2, 3, 3, 3, + 3, 0, 2, 2, 0, 3, 0, 2, + 2, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 3, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 2, 2, 2, 2, 0, 3, 3, 0, + 2, 2, 2, 2, 0, 3, 3, 2, + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 0, 0, 0, 0, 0, 0, 0, + 2, 0, 2, 3, 0, 2, 3, 0, + 2, 3, 2, 0, 2, 3, 3, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 2, 3, 0, 0, 2, 3, 2, 3, + 2, 3, 2, 3, 0, 0, 0, 0, + 2, 3, 0, 2, 3, 0, 2, 3, + 0, 6, 3, 11, 3, 11, 3, 0, + 0, 0, 0, 0, 0, 0, 2, 3, + 0, 0, 0, 0, 0, 0 +}; + +static const short _zone_scanner_cond_keys[] = { + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, -128, 9, 10, 10, + 11, 127, 10, 10, 59, 59, 10, 10, + 59, 59, -128, 9, 10, 10, 11, 127, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + -128, 9, 10, 10, 11, 58, 59, 59, + 60, 127, -128, 9, 10, 10, 11, 58, + 59, 59, 60, 127, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, -128, 9, 10, 10, + 11, 127, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, 10, 10, + 59, 59, -128, 9, 10, 10, 11, 127, + 10, 10, 59, 59, 10, 10, 59, 59, + -128, 9, 10, 10, 11, 127, 10, 10, + 59, 59, 10, 10, 59, 59, -128, 9, + 10, 10, 11, 127, 10, 10, 59, 59, + 10, 10, 59, 59, -128, 9, 10, 10, + 11, 127, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, -128, 9, + 10, 10, 11, 127, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + -128, 9, 10, 10, 11, 127, 10, 10, + 59, 59, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, -128, 9, + 10, 10, 11, 127, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, -128, 9, 10, 10, + 11, 58, 59, 59, 60, 127, -128, 9, + 10, 10, 11, 58, 59, 59, 60, 127, + -128, 9, 10, 10, 11, 58, 59, 59, + 60, 127, -128, 9, 10, 10, 11, 58, + 59, 59, 60, 127, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + -128, 9, 10, 10, 11, 127, 10, 10, + 59, 59, 10, 10, 59, 59, -128, 9, + 10, 10, 11, 58, 59, 59, 60, 127, + -128, 9, 10, 10, 11, 58, 59, 59, + 60, 127, -128, 9, 10, 10, 11, 58, + 59, 59, 60, 127, -128, 9, 10, 10, + 11, 58, 59, 59, 60, 127, -128, 9, + 10, 10, 11, 127, 10, 10, 59, 59, + -128, 9, 10, 10, 11, 58, 59, 59, + 60, 127, -128, 9, 10, 10, 11, 58, + 59, 59, 60, 127, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, -128, 9, 10, 10, + 11, 127, -128, 9, 10, 10, 11, 127, + 10, 10, 59, 59, -128, 9, 10, 10, + 11, 127, 10, 10, 10, 10, 10, 10, + 59, 59, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, -128, 9, + 10, 10, 11, 127, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + -128, 9, 10, 10, 11, 127, 10, 10, + 59, 59, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, 10, 10, + 59, 59, -128, 9, 10, 10, 11, 127, + 10, 10, 59, 59, -128, 9, 10, 10, + 11, 127, -128, 9, 10, 10, 11, 127, + 10, 10, 59, 59, -128, 9, 10, 10, + 11, 127, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, 10, 10, + 59, 59, -128, 9, 10, 10, 11, 127, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + -128, 9, 10, 10, 11, 127, -128, 9, + 10, 10, 11, 127, -128, 9, 10, 10, + 11, 127, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, -128, 9, + 10, 10, 11, 127, -128, 9, 10, 10, + 11, 127, 10, 10, 59, 59, -128, 9, + 10, 10, 11, 127, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, -128, 9, 10, 10, + 11, 127, 10, 10, 59, 59, 10, 10, + 59, 59, -128, 9, 10, 10, 11, 127, + 10, 10, 59, 59, 10, 10, 59, 59, + -128, 9, 10, 10, 11, 127, 10, 10, + 59, 59, 10, 10, 59, 59, -128, 9, + 10, 10, 11, 127, -128, 9, 10, 10, + 11, 127, -128, 9, 10, 10, 11, 127, + 10, 10, 59, 59, 10, 10, 59, 59, + -128, 9, 10, 10, 11, 127, 10, 10, + 59, 59, 10, 10, 59, 59, -128, 9, + 10, 10, 11, 127, 10, 10, 59, 59, + 10, 10, 59, 59, -128, 9, 10, 10, + 11, 127, 10, 10, 59, 59, 10, 10, + 59, 59, -128, 9, 10, 10, 11, 127, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + -128, 9, 10, 10, 11, 127, 10, 10, + 59, 59, 10, 10, 59, 59, -128, 9, + 10, 10, 11, 127, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + -128, 9, 10, 10, 11, 127, 10, 10, + 59, 59, 10, 10, 59, 59, 10, 10, + 59, 59, -128, 9, 10, 10, 11, 127, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, -128, 9, 10, 10, + 11, 127, 10, 10, 59, 59, -128, 9, + 10, 10, 11, 127, 10, 10, 59, 59, + 10, 10, 59, 59, -128, 9, 10, 10, + 11, 127, -128, 9, 10, 10, 11, 127, + -128, 9, 10, 10, 11, 127, 10, 10, + 59, 59, -128, 9, 10, 10, 11, 127, + 10, 10, 59, 59, 10, 10, 59, 59, + -128, 9, 10, 10, 11, 127, -128, 9, + 10, 10, 11, 127, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, -128, 9, 10, 10, + 11, 127, -128, 9, 10, 10, 11, 127, + -128, 9, 10, 10, 11, 127, 10, 10, + 59, 59, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, -128, 9, + 10, 10, 11, 127, -128, 9, 10, 10, + 11, 127, -128, 9, 10, 10, 11, 127, + -128, 9, 10, 10, 11, 127, -128, 9, + 10, 10, 11, 127, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + -128, 9, 10, 10, 11, 127, -128, 9, + 10, 10, 11, 127, -128, 9, 10, 10, + 11, 127, 10, 10, 59, 59, 10, 10, + 59, 59, -128, 9, 10, 10, 11, 127, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, -128, 9, 10, 10, + 11, 127, -128, 9, 10, 10, 11, 127, + -128, 9, 10, 10, 11, 127, -128, 9, + 10, 10, 11, 127, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + -128, 9, 10, 10, 11, 127, -128, 9, + 10, 10, 11, 127, -128, 9, 10, 10, + 11, 127, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, 10, 10, + 59, 59, 9, 9, 10, 10, 32, 32, + 40, 40, 41, 41, 59, 59, 9, 9, + 10, 10, 32, 32, 40, 40, 41, 41, + 43, 43, 47, 47, 48, 57, 59, 59, + 65, 90, 97, 122, 10, 10, 59, 59, + -128, 9, 10, 10, 11, 127, 9, 9, + 10, 10, 32, 32, 40, 40, 41, 41, + 59, 59, -128, 9, 10, 10, 11, 127, + 9, 9, 10, 10, 32, 32, 40, 40, + 41, 41, 43, 43, 47, 47, 48, 57, + 59, 59, 65, 90, 97, 122, -128, 9, + 10, 10, 11, 127, -128, 9, 10, 10, + 11, 127, -128, 9, 10, 10, 11, 127, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 9, 9, 10, 10, 32, 32, 40, 40, + 41, 41, 59, 59, -128, 9, 10, 10, + 11, 127, -128, 9, 10, 10, 11, 127, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 9, 9, 10, 10, 32, 32, 40, 40, + 41, 41, 59, 59, -128, 9, 10, 10, + 11, 127, -128, 9, 10, 10, 11, 127, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 9, 9, 10, 10, 32, 32, 40, 40, + 41, 41, 59, 59, -128, 9, 10, 10, + 11, 127, -128, 9, 10, 10, 11, 127, + -128, 9, 10, 10, 11, 127, 10, 10, + 59, 59, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, 10, 10, + 59, 59, -128, 9, 10, 10, 11, 127, + -128, 9, 10, 10, 11, 127, -128, 9, + 10, 10, 11, 127, -128, 9, 10, 10, + 11, 127, -128, 9, 10, 10, 11, 127, + -128, 9, 10, 10, 11, 127, -128, 9, + 10, 10, 11, 127, -128, 9, 10, 10, + 11, 127, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, -128, 9, + 10, 10, 11, 127, -128, 9, 10, 10, + 11, 127, -128, 9, 10, 10, 11, 127, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, -128, 9, 10, 10, + 11, 127, 10, 10, 59, 59, -128, 9, + 10, 10, 11, 127, -128, 9, 10, 10, + 11, 127, -128, 9, 10, 10, 11, 127, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + 10, 10, 59, 59, 10, 10, 59, 59, + -128, 9, 10, 10, 11, 127, -128, 9, + 10, 10, 11, 127, -128, 9, 10, 10, + 11, 127, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, -128, 9, + 10, 10, 11, 127, -128, 9, 10, 10, + 11, 127, -128, 9, 10, 10, 11, 127, + -128, 9, 10, 10, 11, 127, 10, 10, + 59, 59, 10, 10, 59, 59, -128, 9, + 10, 10, 11, 127, 10, 10, 59, 59, + 10, 10, 59, 59, -128, 9, 10, 10, + 11, 127, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, 10, 10, + 59, 59, -128, 9, 10, 10, 11, 127, + -128, 9, 10, 10, 11, 127, 10, 10, + 59, 59, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, -128, 9, + 10, 10, 11, 127, -128, 9, 10, 10, + 11, 127, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, 10, 10, + 59, 59, 10, 10, 59, 59, 10, 10, + 59, 59, -128, 9, 10, 10, 11, 127, + 10, 10, 59, 59, -128, 9, 10, 10, + 11, 127, 10, 10, 59, 59, -128, 9, + 10, 10, 11, 127, 10, 10, 59, 59, + 10, 10, 59, 59, -128, 9, 10, 10, + 11, 127, -128, 9, 10, 10, 11, 127, + 10, 10, 59, 59, -128, 9, 10, 10, + 11, 127, 10, 10, 59, 59, -128, 9, + 10, 10, 11, 127, 10, 10, 59, 59, + -128, 9, 10, 10, 11, 127, 10, 10, + 59, 59, -128, 9, 10, 10, 11, 127, + 10, 10, 59, 59, -128, 9, 10, 10, + 11, 127, 10, 10, 59, 59, -128, 9, + 10, 10, 11, 127, 10, 10, 59, 59, + -128, 9, 10, 10, 11, 127, 10, 10, + 59, 59, -128, 9, 10, 10, 11, 127, + 9, 9, 10, 10, 32, 32, 40, 40, + 41, 41, 59, 59, -128, 9, 10, 10, + 11, 127, 9, 9, 10, 10, 32, 32, + 40, 40, 41, 41, 43, 43, 47, 47, + 48, 57, 59, 59, 65, 90, 97, 122, + -128, 9, 10, 10, 11, 127, 9, 9, + 10, 10, 32, 32, 40, 40, 41, 41, + 43, 43, 47, 47, 48, 57, 59, 59, + 65, 90, 97, 122, -128, 9, 10, 10, + 11, 127, 10, 10, 59, 59, -128, 9, + 10, 10, 11, 127, 0 +}; + +static const char _zone_scanner_cond_spaces[] = { + 0, 0, 0, 0, 0, 0, 5, 5, + 5, 5, 0, 0, 0, 0, 0, 0, + 0, 5, 5, 5, 5, 0, 0, 0, + 5, 5, 0, 0, 0, 0, 5, 5, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, + 0, 0, 0, 0, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 5, 5, 5, 5, 5, 5, 5, + 5, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5, 5, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 5, 5, + 5, 5, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 5, 5, + 5, 5, 5, 5, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5, 5, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 2, 2, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 5, 5, 0, 0, + 0, 5, 5, 5, 5, 5, 5, 5, + 5, 0, 0, 0, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 0, 0, + 0, 0, 0, 0, 0, 0, 5, 5, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 5, 5, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 5, 5, 5, 5, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, + 0, 0, 0, 5, 5, 5, 5, 0, + 0, 0, 5, 5, 5, 5, 5, 5, + 0, 0, 0, 5, 5, 5, 5, 5, + 5, 0, 0, 0, 5, 5, 5, 5, + 5, 5, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 5, 5, 5, + 5, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 5, 5, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 5, 5, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 8, 11, 8, + 8, 8, 11, 3, 6, 3, 3, 3, + 3, 3, 3, 6, 3, 3, 1, 1, + 6, 6, 6, 4, 9, 4, 4, 4, + 9, 7, 7, 7, 8, 11, 8, 8, + 8, 3, 3, 3, 11, 3, 3, 10, + 10, 10, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 8, 11, 8, 8, 8, 11, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 8, 11, 8, 8, + 8, 11, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 8, 11, 8, 8, 8, 11, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, + 1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 5, 5, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 5, + 5, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 5, 5, 5, 5, 5, + 5, 0, 0, 5, 5, 0, 0, 0, + 5, 5, 0, 0, 0, 5, 5, 0, + 0, 0, 0, 0, 5, 5, 0, 0, + 0, 0, 0, 0, 5, 5, 0, 0, + 0, 5, 5, 0, 0, 0, 5, 5, + 0, 0, 0, 5, 5, 0, 0, 0, + 5, 5, 0, 0, 0, 5, 5, 0, + 0, 0, 5, 5, 0, 0, 0, 5, + 5, 0, 0, 0, 4, 9, 4, 4, + 4, 9, 7, 7, 7, 3, 6, 3, + 3, 3, 3, 3, 3, 6, 3, 3, + 6, 6, 6, 8, 11, 8, 8, 8, + 3, 3, 3, 11, 3, 3, 10, 10, + 10, 5, 5, 0, 0, 0, 0 +}; + +static const short _zone_scanner_key_offsets[] = { + 0, 0, 40, 58, 94, 110, 127, 135, + 143, 144, 145, 148, 167, 186, 192, 244, + 264, 302, 310, 312, 322, 336, 350, 354, + 356, 358, 360, 362, 372, 382, 384, 386, + 396, 398, 400, 402, 412, 418, 420, 422, + 424, 434, 438, 440, 442, 452, 454, 456, + 458, 468, 478, 480, 482, 484, 485, 495, + 496, 506, 508, 510, 512, 514, 524, 528, + 534, 570, 572, 574, 576, 578, 580, 582, + 584, 594, 598, 600, 610, 620, 626, 627, + 637, 638, 648, 650, 660, 670, 674, 676, + 678, 680, 690, 700, 706, 708, 710, 712, + 722, 724, 734, 746, 748, 759, 771, 773, + 775, 777, 779, 789, 791, 793, 803, 809, + 819, 821, 823, 825, 835, 845, 853, 855, + 865, 867, 877, 879, 889, 891, 893, 895, + 905, 911, 913, 915, 925, 927, 937, 939, + 941, 943, 955, 957, 959, 969, 972, 1012, + 1016, 1022, 1060, 1078, 1086, 1098, 1106, 1109, + 1110, 1116, 1118, 1120, 1122, 1124, 1126, 1128, + 1134, 1140, 1178, 1181, 1199, 1237, 1240, 1248, + 1260, 1268, 1271, 1287, 1337, 1355, 1374, 1414, + 1420, 1434, 1448, 1500, 1516, 1530, 1540, 1550, + 1562, 1574, 1588, 1600, 1614, 1624, 1638, 1654, + 1668, 1678, 1684, 1698, 1700, 1702, 1704, 1706, + 1708, 1714, 1716, 1718, 1724, 1732, 1752, 1792, + 1798, 1816, 1868, 1884, 1898, 1908, 1918, 1930, + 1942, 1956, 1968, 1982, 1992, 2006, 2022, 2036, + 2046, 2061, 2101, 2115, 2129, 2145, 2161, 2163, + 2165, 2175, 2177, 2179, 2181, 2191, 2193, 2203, + 2209, 2223, 2237, 2253, 2269, 2285, 2301, 2304, + 2356, 2370, 2384, 2398, 2410, 2418, 2426, 2436, + 2446, 2458, 2468, 2480, 2488, 2500, 2514, 2526, + 2534, 2540, 2546, 2556, 2562, 2563, 2564, 2575, + 2591, 2607, 2609, 2611, 2613, 2629, 2635, 2641, + 2647, 2659, 2665, 2667, 2669, 2681, 2687, 2693, + 2699, 2701, 2703, 2709, 2715, 2723, 2743, 2751, + 2752, 2762, 2774, 2784, 2787, 2793, 2809, 2819, + 2837, 2845, 2846, 2848, 2850, 2852, 2862, 2870, + 2873, 2879, 2891, 2899, 2917, 2927, 2945, 2953, + 2954, 2956, 2958, 2960, 2970, 2978, 2984, 3000, + 3003, 3006, 3013, 3020, 3028, 3036, 3053, 3056, + 3057, 3067, 3107, 3123, 3125, 3127, 3137, 3140, + 3148, 3150, 3160, 3164, 3166, 3168, 3170, 3172, + 3182, 3192, 3194, 3196, 3206, 3208, 3210, 3212, + 3222, 3228, 3230, 3232, 3234, 3244, 3248, 3250, + 3252, 3262, 3264, 3266, 3268, 3278, 3288, 3290, + 3292, 3294, 3295, 3305, 3306, 3316, 3318, 3320, + 3322, 3324, 3334, 3336, 3338, 3340, 3342, 3344, + 3346, 3348, 3358, 3362, 3364, 3374, 3384, 3390, + 3391, 3401, 3402, 3412, 3414, 3424, 3434, 3438, + 3440, 3442, 3444, 3454, 3464, 3470, 3472, 3474, + 3476, 3486, 3488, 3498, 3510, 3512, 3523, 3535, + 3537, 3539, 3541, 3543, 3553, 3555, 3557, 3567, + 3573, 3583, 3585, 3587, 3589, 3599, 3609, 3617, + 3619, 3629, 3631, 3641, 3643, 3653, 3655, 3657, + 3659, 3669, 3675, 3677, 3679, 3689, 3691, 3701, + 3703, 3705, 3707, 3719, 3721, 3723, 3733, 3735, + 3737, 3739, 3749, 3751, 3761, 3767, 3775, 3783, + 3795, 3801, 3817, 3820, 3823, 3826, 3832, 3841, + 3851, 3863, 3869, 3885, 3888, 3891, 3899, 3902, + 3914, 3922, 3926, 3932, 3934, 3941, 3943, 3945, + 3947, 3949, 3950, 3951, 3953, 3955, 3957, 3958, + 3964, 3968, 3972, 3973, 3975, 3977, 3979, 3981, + 3987, 3989, 3991, 3993, 3995, 3996, 3997, 3999, + 4001, 4003, 4004, 4005, 4006, 4012, 4013, 4014, + 4016, 4018, 4020, 4021, 4022, 4023, 4029, 4031, + 4032, 4033, 4034, 4035, 4041, 4042, 4043, 4049, + 4051, 4053, 4055, 4057, 4059, 4061, 4063, 4069, + 4071, 4073, 4075, 4077, 4079, 4081, 4085, 4087, + 4089, 4095, 4097, 4099, 4105, 4107, 4109, 4113, + 4115, 4116, 4122, 4124, 4126, 4129, 4136, 4138, + 4140, 4142, 4144, 4145, 4146, 4148, 4150, 4152, + 4153, 4159, 4160, 4161, 4167, 4168, 4169, 4175, + 4189, 4197, 4199, 4201, 4203, 4205, 4207, 4213, + 4219, 4221, 4223, 4225, 4227, 4229, 4235, 4239, + 4241, 4247, 4249, 4251, 4257, 4259, 4261, 4263, + 4269, 4271, 4273, 4279, 4283, 4285, 4291, 4293, + 4295, 4301, 4303, 4305, 4307, 4313, 4315, 4317, + 4323, 4326, 4335, 4344, 4350, 4359, 4365, 4380, + 4386, 4394, 4402, 4410, 4428, 4436, 4454, 4462, + 4480, 4488, 4506, 4514, 4526, 4534, 4537, 4545, + 4557, 4565, 4568, 4576, 4588, 4596, 4599, 4607, + 4619, 4627, 4630, 4633, 4636, 4642, 4648, 4660, + 4666, 4669, 4678, 4684, 4699, 4705, 4708, 4710, + 4718, 4733, 4739, 4742, 4748, 4758, 4774, 4777, + 4784, 4797, 4799, 4807, 4817, 4825, 4835, 4844, + 4852, 4858, 4866, 4874, 4884, 4892, 4902, 4911, + 4919, 4925, 4934, 4936, 4950, 4962, 4976, 4988, + 5002, 5014, 5028, 5038, 5041, 5054, 5067, 5070, + 5083, 5096, 5106, 5109, 5122, 5135, 5145, 5148, + 5161, 5174, 5184, 5187, 5193, 5196, 5204, 5212, + 5215, 5218, 5221, 5227, 5230, 5238, 5246, 5249, + 5252, 5254, 5262, 5270, 5278, 5286, 5294, 5309, + 5315, 5318, 5321, 5324, 5326, 5334, 5342, 5350, + 5362, 5368, 5380, 5386, 5398, 5404, 5419, 5425, + 5428, 5431, 5434, 5437, 5440, 5446, 5452, 5460, + 5468, 5480, 5486, 5499, 5501, 5504, 5507, 5510, + 5523, 5525, 5526, 5529, 5532, 5534, 5546, 5549, + 5550, 5557, 5564, 5566, 5574, 5586, 5592, 5600, + 5608, 5620, 5626, 5642, 5645, 5648, 5651, 5654, + 5656, 5664, 5672, 5680, 5692, 5698, 5714, 5717, + 5720, 5723, 5725, 5733, 5743, 5749, 5757, 5765, + 5772, 5806, 5819, 5821, 5824, 5838, 5841, 5882, + 5891, 5894, 5897, 5903, 5911, 5919, 5928, 5965, + 5968, 5971, 5977, 5985, 5993, 6006, 6047, 6050, + 6053, 6059, 6067, 6075, 6090, 6124, 6127, 6130, + 6133, 6163, 6175, 6187, 6193, 6201, 6209, 6217, + 6225, 6233, 6241, 6249, 6257, 6265, 6273, 6288, + 6294, 6307, 6309, 6312, 6315, 6318, 6321, 6324, + 6327, 6330, 6333, 6335, 6337, 6343, 6345, 6347, + 6349, 6355, 6357, 6363, 6371, 6373, 6379, 6383, + 6385, 6387, 6389, 6391, 6397, 6403, 6405, 6407, + 6413, 6415, 6417, 6419, 6425, 6431, 6433, 6435, + 6437, 6443, 6447, 6449, 6451, 6457, 6459, 6461, + 6463, 6469, 6475, 6477, 6479, 6481, 6482, 6488, + 6489, 6495, 6497, 6499, 6501, 6503, 6509, 6511, + 6513, 6515, 6517, 6519, 6521, 6523, 6529, 6533, + 6535, 6541, 6547, 6553, 6554, 6560, 6561, 6567, + 6569, 6575, 6581, 6585, 6587, 6589, 6591, 6597, + 6603, 6609, 6611, 6613, 6615, 6621, 6623, 6629, + 6637, 6639, 6646, 6654, 6656, 6658, 6660, 6662, + 6668, 6670, 6672, 6678, 6684, 6690, 6692, 6694, + 6696, 6702, 6708, 6716, 6718, 6724, 6726, 6732, + 6734, 6740, 6742, 6744, 6746, 6752, 6758, 6760, + 6762, 6768, 6770, 6776, 6778, 6780, 6782, 6790, + 6792, 6794, 6800, 6809, 6815, 6821, 6823, 6831, + 6839, 6847, 6859, 6865, 6878, 6880, 6883, 6886, + 6889, 6896, 6898, 6900, 6908, 6916, 6924, 6932, + 6940, 6953, 6959, 6971, 6977, 6984, 6990, 6997, + 7004, 7010, 7017, 7029, 7035, 7036, 7037, 7038, + 7039, 7040, 7043, 7049, 7061, 7064, 7067, 7070, + 7072, 7080, 7088, 7096, 7104, 7112, 7125, 7131, + 7137, 7149, 7152, 7155, 7158, 7160, 7168, 7176, + 7184, 7192, 7200, 7212, 7218, 7234, 7237, 7240, + 7243, 7246, 7248, 7256, 7265, 7274, 7277, 7279, + 7287, 7299, 7305, 7311, 7317, 7318, 7324, 7330, + 7336, 7342, 7349, 7352, 7358, 7364, 7365, 7371, + 7377, 7384, 7390, 7396, 7397, 7403, 7409, 7416, + 7418, 7426, 7434, 7442, 7454, 7460, 7463, 7466, + 7468, 7476, 7488, 7494, 7506, 7512, 7515, 7518, + 7536, 7569, 7617, 7667, 7685, 7735, 7753, 7786, + 7851, 7916, 7916, 7916, 7916, 7928, 7928, 7928, + 7928, 7944, 7944, 7961, 7964, 7964, 8004, 8007, + 8007, 8023, 8026, 8038, 8038, 8054, 8057, 8060, + 8060, 8060, 8060, 8060, 8060, 8060, 8060, 8060, + 8060, 8076, 8079, 8079, 8079, 8089, 8092, 8104, + 8107, 8119, 8122, 8134, 8137, 8137, 8137, 8137, + 8137, 8150, 8153, 8153, 8169, 8172, 8172, 8188, + 8191, 8191, 8205, 8208, 8221, 8224, 8265, 8274, + 8274, 8274, 8274, 8274, 8274, 8274, 8274, 8290, + 8293, 8293, 8293, 8293, 8293, 8293 +}; + +static const short _zone_scanner_trans_keys[] = { + 9, 32, 40, 41, 65, 67, 68, 69, + 72, 73, 75, 76, 77, 78, 80, 82, + 83, 84, 85, 97, 99, 100, 101, 104, + 105, 107, 108, 109, 110, 112, 114, 115, + 116, 117, 778, 827, 1034, 1083, 48, 57, + 9, 32, 40, 41, 68, 72, 77, 83, + 87, 100, 104, 109, 115, 119, 1034, 1083, + 48, 57, 9, 32, 40, 41, 65, 67, + 68, 69, 72, 73, 75, 76, 77, 78, + 80, 82, 83, 84, 85, 97, 99, 100, + 101, 104, 105, 107, 108, 109, 110, 112, + 114, 115, 116, 117, 1034, 1083, 9, 32, + 40, 41, 65, 70, 80, 97, 102, 112, + 2058, 2107, 2314, 2363, 2570, 2619, 9, 32, + 40, 41, 92, 2058, 2107, 2314, 2363, 2570, + 2619, -128, 8, 11, 58, 60, 127, 9, + 32, 40, 41, 778, 827, 1034, 1083, 9, + 32, 40, 41, 778, 827, 1034, 1083, 10, + 35, 1034, 896, 1151, 9, 32, 40, 41, + 92, 1802, 1851, 2058, 2107, 2314, 2363, 2570, + 2619, -128, 8, 11, 58, 60, 127, 9, + 32, 40, 41, 92, 1802, 1851, 2058, 2107, + 2314, 2363, 2570, 2619, -128, 8, 11, 58, + 60, 127, 778, 1034, 640, 895, 896, 1151, + 9, 32, 40, 41, 58, 65, 67, 68, + 69, 72, 73, 75, 76, 77, 78, 80, + 82, 83, 84, 85, 92, 97, 99, 100, + 101, 104, 105, 107, 108, 109, 110, 112, + 114, 115, 116, 117, 1802, 1851, 2058, 2107, + 2314, 2363, 2570, 2619, -128, 8, 11, 47, + 48, 57, 60, 127, 9, 32, 40, 41, + 68, 72, 77, 83, 87, 100, 104, 109, + 115, 119, 778, 827, 1034, 1083, 48, 57, + 9, 32, 40, 41, 65, 67, 68, 69, + 72, 73, 75, 76, 77, 78, 80, 82, + 83, 84, 85, 97, 99, 100, 101, 104, + 105, 107, 108, 109, 110, 112, 114, 115, + 116, 117, 778, 827, 1034, 1083, 65, 68, + 69, 78, 97, 100, 101, 110, 65, 97, + 9, 32, 40, 41, 2058, 2107, 2314, 2363, + 2570, 2619, 777, 778, 800, 808, 809, 827, + 1033, 1034, 1056, 1064, 1065, 1083, 896, 1151, + 777, 778, 800, 808, 809, 827, 1033, 1034, + 1056, 1064, 1065, 1083, 896, 1151, 78, 83, + 110, 115, 83, 115, 75, 107, 69, 101, + 89, 121, 9, 32, 40, 41, 2058, 2107, + 2314, 2363, 2570, 2619, 9, 32, 40, 41, + 2058, 2107, 2314, 2363, 2570, 2619, 82, 114, + 84, 116, 9, 32, 40, 41, 2058, 2107, + 2314, 2363, 2570, 2619, 65, 97, 77, 109, + 69, 101, 9, 32, 40, 41, 2058, 2107, + 2314, 2363, 2570, 2619, 72, 78, 83, 104, + 110, 115, 67, 99, 73, 105, 68, 100, + 9, 32, 40, 41, 2058, 2107, 2314, 2363, + 2570, 2619, 65, 83, 97, 115, 77, 109, + 69, 101, 9, 32, 40, 41, 2058, 2107, + 2314, 2363, 2570, 2619, 75, 107, 69, 101, + 89, 121, 9, 32, 40, 41, 2058, 2107, + 2314, 2363, 2570, 2619, 9, 32, 40, 41, + 2058, 2107, 2314, 2363, 2570, 2619, 85, 117, + 73, 105, 52, 54, 56, 9, 32, 40, + 41, 2058, 2107, 2314, 2363, 2570, 2619, 52, + 9, 32, 40, 41, 2058, 2107, 2314, 2363, + 2570, 2619, 73, 105, 78, 110, 70, 102, + 79, 111, 9, 32, 40, 41, 2058, 2107, + 2314, 2363, 2570, 2619, 78, 80, 110, 112, + 9, 32, 40, 41, 1034, 1083, 9, 32, + 40, 41, 65, 67, 68, 69, 72, 73, + 75, 76, 77, 78, 80, 82, 83, 84, + 85, 97, 99, 100, 101, 104, 105, 107, + 108, 109, 110, 112, 114, 115, 116, 117, + 1034, 1083, 80, 112, 83, 115, 69, 101, + 67, 99, 75, 107, 69, 101, 89, 121, + 9, 32, 40, 41, 2058, 2107, 2314, 2363, + 2570, 2619, 69, 88, 101, 120, 89, 121, + 9, 32, 40, 41, 2058, 2107, 2314, 2363, + 2570, 2619, 9, 32, 40, 41, 2058, 2107, + 2314, 2363, 2570, 2619, 51, 54, 79, 80, + 111, 112, 50, 9, 32, 40, 41, 2058, + 2107, 2314, 2363, 2570, 2619, 52, 9, 32, + 40, 41, 2058, 2107, 2314, 2363, 2570, 2619, + 67, 99, 9, 32, 40, 41, 2058, 2107, + 2314, 2363, 2570, 2619, 9, 32, 40, 41, + 2058, 2107, 2314, 2363, 2570, 2619, 73, 88, + 105, 120, 78, 110, 70, 102, 79, 111, + 9, 32, 40, 41, 2058, 2107, 2314, 2363, + 2570, 2619, 9, 32, 40, 41, 2058, 2107, + 2314, 2363, 2570, 2619, 65, 73, 83, 97, + 105, 115, 80, 112, 84, 116, 82, 114, + 9, 32, 40, 41, 2058, 2107, 2314, 2363, + 2570, 2619, 68, 100, 9, 32, 40, 41, + 2058, 2107, 2314, 2363, 2570, 2619, 9, 32, + 40, 41, 69, 101, 2058, 2107, 2314, 2363, + 2570, 2619, 67, 99, 9, 32, 40, 41, + 51, 2058, 2107, 2314, 2363, 2570, 2619, 9, + 32, 40, 41, 80, 112, 2058, 2107, 2314, + 2363, 2570, 2619, 65, 97, 82, 114, 65, + 97, 77, 109, 9, 32, 40, 41, 2058, + 2107, 2314, 2363, 2570, 2619, 84, 116, 82, + 114, 9, 32, 40, 41, 2058, 2107, 2314, + 2363, 2570, 2619, 80, 82, 84, 112, 114, + 116, 9, 32, 40, 41, 2058, 2107, 2314, + 2363, 2570, 2619, 83, 115, 73, 105, 71, + 103, 9, 32, 40, 41, 2058, 2107, 2314, + 2363, 2570, 2619, 9, 32, 40, 41, 2058, + 2107, 2314, 2363, 2570, 2619, 79, 80, 82, + 83, 111, 112, 114, 115, 65, 97, 9, + 32, 40, 41, 2058, 2107, 2314, 2363, 2570, + 2619, 70, 102, 9, 32, 40, 41, 2058, + 2107, 2314, 2363, 2570, 2619, 86, 118, 9, + 32, 40, 41, 2058, 2107, 2314, 2363, 2570, + 2619, 72, 104, 70, 102, 80, 112, 9, + 32, 40, 41, 2058, 2107, 2314, 2363, 2570, + 2619, 76, 88, 89, 108, 120, 121, 83, + 115, 65, 97, 9, 32, 40, 41, 2058, + 2107, 2314, 2363, 2570, 2619, 84, 116, 9, + 32, 40, 41, 2058, 2107, 2314, 2363, 2570, + 2619, 80, 112, 69, 101, 48, 57, 9, + 32, 40, 41, 2058, 2107, 2314, 2363, 2570, + 2619, 48, 57, 82, 114, 73, 105, 9, + 32, 40, 41, 2058, 2107, 2314, 2363, 2570, + 2619, 1034, 896, 1151, 9, 32, 40, 41, + 65, 67, 68, 69, 72, 73, 75, 76, + 77, 78, 80, 82, 83, 84, 85, 97, + 99, 100, 101, 104, 105, 107, 108, 109, + 110, 112, 114, 115, 116, 117, 778, 827, + 1034, 1083, 48, 57, 78, 80, 110, 112, + 9, 32, 40, 41, 1034, 1083, 9, 32, + 40, 41, 65, 67, 68, 69, 72, 73, + 75, 76, 77, 78, 80, 82, 83, 84, + 85, 97, 99, 100, 101, 104, 105, 107, + 108, 109, 110, 112, 114, 115, 116, 117, + 1034, 1083, 48, 57, 9, 32, 40, 41, + 68, 72, 77, 83, 87, 100, 104, 109, + 115, 119, 1034, 1083, 48, 57, 9, 32, + 40, 41, 1034, 1083, 48, 57, 68, 72, + 77, 83, 87, 100, 104, 109, 115, 119, + 48, 57, 9, 32, 40, 41, 1034, 1083, + 48, 57, 1034, 896, 1151, 10, 73, 79, + 84, 105, 111, 116, 78, 110, 67, 99, + 76, 108, 85, 117, 68, 100, 69, 101, + 32, 59, 9, 10, 40, 41, 9, 32, + 40, 41, 1034, 1083, 9, 32, 40, 41, + 65, 67, 68, 69, 72, 73, 75, 76, + 77, 78, 80, 82, 83, 84, 85, 97, + 99, 100, 101, 104, 105, 107, 108, 109, + 110, 112, 114, 115, 116, 117, 1034, 1083, + 48, 57, 1034, 896, 1151, 9, 32, 40, + 41, 68, 72, 77, 83, 87, 100, 104, + 109, 115, 119, 1034, 1083, 48, 57, 9, + 32, 40, 41, 65, 67, 68, 69, 72, + 73, 75, 76, 77, 78, 80, 82, 83, + 84, 85, 97, 99, 100, 101, 104, 105, + 107, 108, 109, 110, 112, 114, 115, 116, + 117, 1034, 1083, 48, 57, 1034, 896, 1151, + 9, 32, 40, 41, 1034, 1083, 48, 57, + 68, 72, 77, 83, 87, 100, 104, 109, + 115, 119, 48, 57, 9, 32, 40, 41, + 1034, 1083, 48, 57, 1034, 896, 1151, 9, + 32, 40, 41, 65, 70, 80, 97, 102, + 112, 2058, 2107, 2314, 2363, 2570, 2619, 9, + 32, 40, 41, 58, 65, 67, 68, 69, + 72, 73, 75, 76, 77, 78, 80, 82, + 83, 84, 85, 92, 97, 99, 100, 101, + 104, 105, 107, 108, 109, 110, 112, 114, + 115, 116, 117, 2058, 2107, 2314, 2363, 2570, + 2619, -128, 8, 11, 47, 48, 57, 60, + 127, 9, 32, 40, 41, 65, 70, 80, + 97, 102, 112, 1802, 1851, 2058, 2107, 2314, + 2363, 2570, 2619, 9, 32, 40, 41, 92, + 1802, 1851, 2058, 2107, 2314, 2363, 2570, 2619, + -128, 8, 11, 58, 60, 127, 9, 32, + 40, 41, 65, 67, 68, 69, 72, 73, + 75, 76, 77, 78, 80, 82, 83, 84, + 85, 97, 99, 100, 101, 104, 105, 107, + 108, 109, 110, 112, 114, 115, 116, 117, + 778, 827, 1034, 1083, 48, 57, 778, 1034, + 640, 895, 896, 1151, 9, 32, 40, 41, + 778, 827, 1034, 1083, -128, 8, 11, 58, + 60, 127, 9, 32, 40, 41, 778, 827, + 1034, 1083, -128, 8, 11, 58, 60, 127, + 9, 32, 40, 41, 58, 65, 67, 68, + 69, 72, 73, 75, 76, 77, 78, 80, + 82, 83, 84, 85, 92, 97, 99, 100, + 101, 104, 105, 107, 108, 109, 110, 112, + 114, 115, 116, 117, 1802, 1851, 2058, 2107, + 2314, 2363, 2570, 2619, -128, 8, 11, 47, + 48, 57, 60, 127, 9, 32, 40, 41, + 65, 68, 69, 78, 97, 100, 101, 110, + 778, 827, 1034, 1083, 9, 32, 40, 41, + 72, 78, 83, 104, 110, 115, 778, 827, + 1034, 1083, 9, 32, 40, 41, 85, 117, + 778, 827, 1034, 1083, 9, 32, 40, 41, + 73, 105, 778, 827, 1034, 1083, 9, 32, + 40, 41, 78, 80, 110, 112, 778, 827, + 1034, 1083, 9, 32, 40, 41, 69, 88, + 101, 120, 778, 827, 1034, 1083, 9, 32, + 40, 41, 51, 54, 79, 80, 111, 112, + 778, 827, 1034, 1083, 9, 32, 40, 41, + 73, 88, 105, 120, 778, 827, 1034, 1083, + 9, 32, 40, 41, 65, 73, 83, 97, + 105, 115, 778, 827, 1034, 1083, 9, 32, + 40, 41, 84, 116, 778, 827, 1034, 1083, + 9, 32, 40, 41, 80, 82, 84, 112, + 114, 116, 778, 827, 1034, 1083, 9, 32, + 40, 41, 79, 80, 82, 83, 111, 112, + 114, 115, 778, 827, 1034, 1083, 9, 32, + 40, 41, 76, 88, 89, 108, 120, 121, + 778, 827, 1034, 1083, 9, 32, 40, 41, + 82, 114, 778, 827, 1034, 1083, 778, 1034, + 640, 895, 896, 1151, 9, 32, 40, 41, + 73, 79, 84, 105, 111, 116, 778, 827, + 1034, 1083, 82, 114, 73, 105, 71, 103, + 73, 105, 78, 110, 32, 59, 9, 10, + 40, 41, 84, 116, 76, 108, 32, 59, + 9, 10, 40, 41, 9, 32, 40, 41, + 778, 827, 1034, 1083, 9, 32, 40, 41, + 68, 72, 77, 83, 87, 100, 104, 109, + 115, 119, 778, 827, 1034, 1083, 48, 57, + 9, 32, 40, 41, 65, 67, 68, 69, + 72, 73, 75, 76, 77, 78, 80, 82, + 83, 84, 85, 97, 99, 100, 101, 104, + 105, 107, 108, 109, 110, 112, 114, 115, + 116, 117, 778, 827, 1034, 1083, 48, 57, + 778, 1034, 640, 895, 896, 1151, 9, 32, + 40, 41, 65, 70, 80, 97, 102, 112, + 1802, 1851, 2058, 2107, 2314, 2363, 2570, 2619, + 9, 32, 40, 41, 58, 65, 67, 68, + 69, 72, 73, 75, 76, 77, 78, 80, + 82, 83, 84, 85, 92, 97, 99, 100, + 101, 104, 105, 107, 108, 109, 110, 112, + 114, 115, 116, 117, 1802, 1851, 2058, 2107, + 2314, 2363, 2570, 2619, -128, 8, 11, 47, + 48, 57, 60, 127, 9, 32, 40, 41, + 65, 68, 69, 78, 97, 100, 101, 110, + 778, 827, 1034, 1083, 9, 32, 40, 41, + 72, 78, 83, 104, 110, 115, 778, 827, + 1034, 1083, 9, 32, 40, 41, 85, 117, + 778, 827, 1034, 1083, 9, 32, 40, 41, + 73, 105, 778, 827, 1034, 1083, 9, 32, + 40, 41, 78, 80, 110, 112, 778, 827, + 1034, 1083, 9, 32, 40, 41, 69, 88, + 101, 120, 778, 827, 1034, 1083, 9, 32, + 40, 41, 51, 54, 79, 80, 111, 112, + 778, 827, 1034, 1083, 9, 32, 40, 41, + 73, 88, 105, 120, 778, 827, 1034, 1083, + 9, 32, 40, 41, 65, 73, 83, 97, + 105, 115, 778, 827, 1034, 1083, 9, 32, + 40, 41, 84, 116, 778, 827, 1034, 1083, + 9, 32, 40, 41, 80, 82, 84, 112, + 114, 116, 778, 827, 1034, 1083, 9, 32, + 40, 41, 79, 80, 82, 83, 111, 112, + 114, 115, 778, 827, 1034, 1083, 9, 32, + 40, 41, 76, 88, 89, 108, 120, 121, + 778, 827, 1034, 1083, 9, 32, 40, 41, + 82, 114, 778, 827, 1034, 1083, 9, 32, + 35, 40, 41, 778, 827, 1034, 1083, -128, + 8, 11, 58, 60, 127, 9, 32, 40, + 41, 65, 67, 68, 69, 72, 73, 75, + 76, 77, 78, 80, 82, 83, 84, 85, + 97, 99, 100, 101, 104, 105, 107, 108, + 109, 110, 112, 114, 115, 116, 117, 778, + 827, 1034, 1083, 48, 57, 777, 778, 800, + 808, 809, 827, 1033, 1034, 1056, 1064, 1065, + 1083, 896, 1151, 777, 778, 800, 808, 809, + 827, 1033, 1034, 1056, 1064, 1065, 1083, 896, + 1151, 777, 778, 800, 808, 809, 827, 1033, + 1034, 1056, 1064, 1065, 1083, 640, 895, 896, + 1151, 777, 778, 800, 808, 809, 827, 1033, + 1034, 1056, 1064, 1065, 1083, 640, 895, 896, + 1151, 65, 97, 65, 97, 9, 32, 40, + 41, 2058, 2107, 2314, 2363, 2570, 2619, 83, + 115, 68, 100, 66, 98, 9, 32, 40, + 41, 2058, 2107, 2314, 2363, 2570, 2619, 76, + 108, 9, 32, 40, 41, 2058, 2107, 2314, + 2363, 2570, 2619, 778, 1034, 640, 895, 896, + 1151, 9, 32, 40, 41, 778, 827, 1034, + 1083, -128, 8, 11, 58, 60, 127, 9, + 32, 40, 41, 778, 827, 1034, 1083, -128, + 8, 11, 58, 60, 127, 777, 778, 800, + 808, 809, 827, 1033, 1034, 1056, 1064, 1065, + 1083, 640, 895, 896, 1151, 777, 778, 800, + 808, 809, 827, 1033, 1034, 1056, 1064, 1065, + 1083, 640, 895, 896, 1151, 777, 778, 800, + 808, 809, 827, 1033, 1034, 1056, 1064, 1065, + 1083, 640, 895, 896, 1151, 777, 778, 800, + 808, 809, 827, 1033, 1034, 1056, 1064, 1065, + 1083, 640, 895, 896, 1151, 1034, 896, 1151, + 9, 32, 40, 41, 58, 65, 67, 68, + 69, 72, 73, 75, 76, 77, 78, 80, + 82, 83, 84, 85, 92, 97, 99, 100, + 101, 104, 105, 107, 108, 109, 110, 112, + 114, 115, 116, 117, 1802, 1851, 2058, 2107, + 2314, 2363, 2570, 2619, -128, 8, 11, 47, + 48, 57, 60, 127, 777, 778, 800, 808, + 809, 827, 1033, 1034, 1056, 1064, 1065, 1083, + 896, 1151, 777, 778, 800, 808, 809, 827, + 1033, 1034, 1056, 1064, 1065, 1083, 896, 1151, + 9, 32, 40, 41, 65, 68, 69, 78, + 97, 100, 101, 110, 1034, 1083, 9, 32, + 40, 41, 72, 78, 83, 104, 110, 115, + 1034, 1083, 9, 32, 40, 41, 85, 117, + 1034, 1083, 9, 32, 40, 41, 73, 105, + 1034, 1083, 9, 32, 40, 41, 78, 80, + 110, 112, 1034, 1083, 9, 32, 40, 41, + 69, 88, 101, 120, 1034, 1083, 9, 32, + 40, 41, 51, 54, 79, 80, 111, 112, + 1034, 1083, 9, 32, 40, 41, 73, 88, + 105, 120, 1034, 1083, 9, 32, 40, 41, + 65, 73, 83, 97, 105, 115, 1034, 1083, + 9, 32, 40, 41, 84, 116, 1034, 1083, + 9, 32, 40, 41, 80, 82, 84, 112, + 114, 116, 1034, 1083, 9, 32, 40, 41, + 79, 80, 82, 83, 111, 112, 114, 115, + 1034, 1083, 9, 32, 40, 41, 76, 88, + 89, 108, 120, 121, 1034, 1083, 9, 32, + 40, 41, 82, 114, 1034, 1083, 778, 1034, + 640, 895, 896, 1151, 778, 1034, 640, 895, + 896, 1151, 9, 32, 40, 41, 78, 80, + 110, 112, 1034, 1083, 778, 1034, 640, 895, + 896, 1151, 10, 10, 42, 46, 64, 92, + 95, 45, 57, 65, 90, 97, 122, 32, + 42, 46, 59, 92, 95, 9, 10, 40, + 41, 45, 57, 65, 90, 97, 122, 32, + 42, 45, 59, 92, 95, 9, 10, 40, + 41, 47, 57, 65, 90, 97, 122, 48, + 57, 48, 57, 48, 57, 32, 42, 46, + 59, 92, 95, 9, 10, 40, 41, 45, + 57, 65, 90, 97, 122, 32, 59, 9, + 10, 40, 41, 32, 59, 9, 10, 40, + 41, 34, 92, 33, 58, 60, 126, 32, + 33, 59, 92, 9, 10, 35, 39, 40, + 41, 42, 126, 32, 47, 48, 57, 58, + 126, 48, 57, 48, 57, 32, 33, 59, + 92, 9, 10, 35, 39, 40, 41, 42, + 126, 9, 34, 92, 522, 32, 126, 32, + 59, 9, 10, 40, 41, 32, 47, 48, + 57, 58, 126, 48, 57, 48, 57, 9, + 34, 92, 522, 32, 126, 9, 32, 40, + 41, 1034, 1083, 9, 32, 40, 41, 1034, + 1083, 48, 57, 9, 32, 40, 41, 68, + 72, 77, 83, 87, 100, 104, 109, 115, + 119, 778, 827, 1034, 1083, 48, 57, 9, + 32, 40, 41, 778, 827, 1034, 1083, 10, + 9, 32, 40, 41, 778, 827, 1034, 1083, + 48, 57, 68, 72, 77, 83, 87, 100, + 104, 109, 115, 119, 48, 57, 9, 32, + 40, 41, 778, 827, 1034, 1083, 48, 57, + 1034, 896, 1151, 9, 32, 40, 41, 1034, + 1083, 9, 32, 40, 41, 42, 46, 92, + 95, 1034, 1083, 45, 57, 65, 90, 97, + 122, 42, 46, 92, 95, 45, 57, 65, + 90, 97, 122, 9, 32, 40, 41, 42, + 45, 92, 95, 778, 827, 1034, 1083, 47, + 57, 65, 90, 97, 122, 9, 32, 40, + 41, 778, 827, 1034, 1083, 10, 48, 57, + 48, 57, 48, 57, 42, 46, 92, 95, + 45, 57, 65, 90, 97, 122, 9, 32, + 40, 41, 778, 827, 1034, 1083, 1034, 896, + 1151, 9, 32, 40, 41, 1034, 1083, 9, + 32, 40, 41, 1034, 1083, -128, 8, 11, + 58, 60, 127, 9, 32, 40, 41, 778, + 827, 1034, 1083, 9, 32, 40, 41, 42, + 46, 92, 95, 778, 827, 1034, 1083, 45, + 57, 65, 90, 97, 122, 42, 46, 92, + 95, 45, 57, 65, 90, 97, 122, 9, + 32, 40, 41, 42, 45, 92, 95, 778, + 827, 1034, 1083, 47, 57, 65, 90, 97, + 122, 9, 32, 40, 41, 778, 827, 1034, + 1083, 10, 48, 57, 48, 57, 48, 57, + 42, 46, 92, 95, 45, 57, 65, 90, + 97, 122, 9, 32, 40, 41, 778, 827, + 1034, 1083, 778, 1034, 640, 895, 896, 1151, + 9, 32, 40, 41, 42, 46, 92, 95, + 1034, 1083, 45, 57, 65, 90, 97, 122, + 1034, 896, 1151, 1034, 896, 1151, 43, 47, + 57, 65, 90, 97, 122, 43, 47, 57, + 65, 90, 97, 122, 43, 61, 47, 57, + 65, 90, 97, 122, 43, 61, 47, 57, + 65, 90, 97, 122, 9, 32, 40, 41, + 43, 2058, 2107, 2314, 2363, 2570, 2619, 47, + 57, 65, 90, 97, 122, 1034, 896, 1151, + 61, 9, 32, 40, 41, 2058, 2107, 2314, + 2363, 2570, 2619, 9, 32, 40, 41, 65, + 67, 68, 69, 72, 73, 75, 76, 77, + 78, 80, 82, 83, 84, 85, 97, 99, + 100, 101, 104, 105, 107, 108, 109, 110, + 112, 114, 115, 116, 117, 2058, 2107, 2314, + 2363, 2570, 2619, 9, 32, 40, 41, 65, + 70, 80, 97, 102, 112, 2058, 2107, 2314, + 2363, 2570, 2619, 65, 97, 65, 97, 9, + 32, 40, 41, 2058, 2107, 2314, 2363, 2570, + 2619, 1034, 896, 1151, 65, 68, 69, 78, + 97, 100, 101, 110, 65, 97, 9, 32, + 40, 41, 2058, 2107, 2314, 2363, 2570, 2619, + 78, 83, 110, 115, 83, 115, 75, 107, + 69, 101, 89, 121, 9, 32, 40, 41, + 2058, 2107, 2314, 2363, 2570, 2619, 9, 32, + 40, 41, 2058, 2107, 2314, 2363, 2570, 2619, + 82, 114, 84, 116, 9, 32, 40, 41, + 2058, 2107, 2314, 2363, 2570, 2619, 65, 97, + 77, 109, 69, 101, 9, 32, 40, 41, + 2058, 2107, 2314, 2363, 2570, 2619, 72, 78, + 83, 104, 110, 115, 67, 99, 73, 105, + 68, 100, 9, 32, 40, 41, 2058, 2107, + 2314, 2363, 2570, 2619, 65, 83, 97, 115, + 77, 109, 69, 101, 9, 32, 40, 41, + 2058, 2107, 2314, 2363, 2570, 2619, 75, 107, + 69, 101, 89, 121, 9, 32, 40, 41, + 2058, 2107, 2314, 2363, 2570, 2619, 9, 32, + 40, 41, 2058, 2107, 2314, 2363, 2570, 2619, + 85, 117, 73, 105, 52, 54, 56, 9, + 32, 40, 41, 2058, 2107, 2314, 2363, 2570, + 2619, 52, 9, 32, 40, 41, 2058, 2107, + 2314, 2363, 2570, 2619, 73, 105, 78, 110, + 70, 102, 79, 111, 9, 32, 40, 41, + 2058, 2107, 2314, 2363, 2570, 2619, 80, 112, + 83, 115, 69, 101, 67, 99, 75, 107, + 69, 101, 89, 121, 9, 32, 40, 41, + 2058, 2107, 2314, 2363, 2570, 2619, 69, 88, + 101, 120, 89, 121, 9, 32, 40, 41, + 2058, 2107, 2314, 2363, 2570, 2619, 9, 32, + 40, 41, 2058, 2107, 2314, 2363, 2570, 2619, + 51, 54, 79, 80, 111, 112, 50, 9, + 32, 40, 41, 2058, 2107, 2314, 2363, 2570, + 2619, 52, 9, 32, 40, 41, 2058, 2107, + 2314, 2363, 2570, 2619, 67, 99, 9, 32, + 40, 41, 2058, 2107, 2314, 2363, 2570, 2619, + 9, 32, 40, 41, 2058, 2107, 2314, 2363, + 2570, 2619, 73, 88, 105, 120, 78, 110, + 70, 102, 79, 111, 9, 32, 40, 41, + 2058, 2107, 2314, 2363, 2570, 2619, 9, 32, + 40, 41, 2058, 2107, 2314, 2363, 2570, 2619, + 65, 73, 83, 97, 105, 115, 80, 112, + 84, 116, 82, 114, 9, 32, 40, 41, + 2058, 2107, 2314, 2363, 2570, 2619, 68, 100, + 9, 32, 40, 41, 2058, 2107, 2314, 2363, + 2570, 2619, 9, 32, 40, 41, 69, 101, + 2058, 2107, 2314, 2363, 2570, 2619, 67, 99, + 9, 32, 40, 41, 51, 2058, 2107, 2314, + 2363, 2570, 2619, 9, 32, 40, 41, 80, + 112, 2058, 2107, 2314, 2363, 2570, 2619, 65, + 97, 82, 114, 65, 97, 77, 109, 9, + 32, 40, 41, 2058, 2107, 2314, 2363, 2570, + 2619, 84, 116, 82, 114, 9, 32, 40, + 41, 2058, 2107, 2314, 2363, 2570, 2619, 80, + 82, 84, 112, 114, 116, 9, 32, 40, + 41, 2058, 2107, 2314, 2363, 2570, 2619, 83, + 115, 73, 105, 71, 103, 9, 32, 40, + 41, 2058, 2107, 2314, 2363, 2570, 2619, 9, + 32, 40, 41, 2058, 2107, 2314, 2363, 2570, + 2619, 79, 80, 82, 83, 111, 112, 114, + 115, 65, 97, 9, 32, 40, 41, 2058, + 2107, 2314, 2363, 2570, 2619, 70, 102, 9, + 32, 40, 41, 2058, 2107, 2314, 2363, 2570, + 2619, 86, 118, 9, 32, 40, 41, 2058, + 2107, 2314, 2363, 2570, 2619, 72, 104, 70, + 102, 80, 112, 9, 32, 40, 41, 2058, + 2107, 2314, 2363, 2570, 2619, 76, 88, 89, + 108, 120, 121, 83, 115, 65, 97, 9, + 32, 40, 41, 2058, 2107, 2314, 2363, 2570, + 2619, 84, 116, 9, 32, 40, 41, 2058, + 2107, 2314, 2363, 2570, 2619, 80, 112, 69, + 101, 48, 57, 9, 32, 40, 41, 2058, + 2107, 2314, 2363, 2570, 2619, 48, 57, 82, + 114, 73, 105, 9, 32, 40, 41, 2058, + 2107, 2314, 2363, 2570, 2619, 83, 115, 68, + 100, 66, 98, 9, 32, 40, 41, 2058, + 2107, 2314, 2363, 2570, 2619, 76, 108, 9, + 32, 40, 41, 2058, 2107, 2314, 2363, 2570, + 2619, 9, 32, 40, 41, 1034, 1083, 9, + 32, 40, 41, 1034, 1083, 48, 57, 9, + 32, 40, 41, 1034, 1083, 48, 57, 9, + 32, 40, 41, 1034, 1083, 48, 57, 65, + 70, 97, 102, 48, 57, 65, 70, 97, + 102, 9, 32, 40, 41, 2058, 2107, 2314, + 2363, 2570, 2619, 48, 57, 65, 70, 97, + 102, 1034, 896, 1151, 1034, 896, 1151, 1034, + 896, 1151, 9, 32, 40, 41, 1034, 1083, + 9, 32, 40, 41, 48, 1034, 1083, 49, + 57, 9, 32, 40, 41, 778, 827, 1034, + 1083, 48, 57, 9, 32, 40, 41, 1034, + 1083, 48, 57, 65, 70, 97, 102, 48, + 57, 65, 70, 97, 102, 9, 32, 40, + 41, 2058, 2107, 2314, 2363, 2570, 2619, 48, + 57, 65, 70, 97, 102, 1034, 896, 1151, + 1034, 896, 1151, 9, 32, 40, 41, 1034, + 1083, 48, 57, 1034, 896, 1151, 68, 69, + 73, 80, 82, 100, 101, 105, 112, 114, + 48, 57, 32, 59, 9, 10, 40, 41, + 48, 57, 72, 83, 104, 115, 32, 59, + 9, 10, 40, 41, 65, 97, 32, 45, + 59, 9, 10, 40, 41, 78, 110, 83, + 115, 69, 101, 67, 99, 51, 45, 83, + 115, 72, 104, 65, 97, 49, 32, 59, + 9, 10, 40, 41, 67, 68, 99, 100, + 67, 68, 99, 100, 45, 71, 103, 79, + 111, 83, 115, 84, 116, 32, 59, 9, + 10, 40, 41, 83, 115, 65, 97, 80, + 112, 50, 51, 53, 54, 83, 115, 72, + 104, 65, 97, 50, 53, 54, 32, 59, + 9, 10, 40, 41, 56, 52, 83, 115, + 72, 104, 65, 97, 51, 56, 52, 32, + 59, 9, 10, 40, 41, 50, 52, 53, + 53, 49, 57, 32, 59, 9, 10, 40, + 41, 52, 56, 32, 59, 9, 10, 40, + 41, 78, 110, 68, 100, 73, 105, 82, + 114, 69, 101, 67, 99, 84, 116, 32, + 59, 9, 10, 40, 41, 82, 114, 73, + 105, 86, 118, 65, 97, 84, 116, 69, + 101, 68, 79, 100, 111, 78, 110, 83, + 115, 32, 59, 9, 10, 40, 41, 73, + 105, 68, 100, 32, 59, 9, 10, 40, + 41, 83, 115, 65, 97, 77, 83, 109, + 115, 68, 100, 53, 32, 59, 9, 10, + 40, 41, 72, 104, 65, 97, 49, 50, + 53, 32, 45, 59, 9, 10, 40, 41, + 78, 110, 83, 115, 69, 101, 67, 99, + 51, 45, 83, 115, 72, 104, 65, 97, + 49, 32, 59, 9, 10, 40, 41, 53, + 54, 32, 59, 9, 10, 40, 41, 49, + 50, 32, 59, 9, 10, 40, 41, 65, + 73, 79, 80, 83, 85, 97, 105, 111, + 112, 115, 117, 48, 57, 32, 59, 9, + 10, 40, 41, 48, 57, 67, 99, 80, + 112, 75, 107, 73, 105, 88, 120, 32, + 59, 9, 10, 40, 41, 65, 80, 83, + 97, 112, 115, 67, 99, 80, 112, 75, + 107, 73, 105, 88, 120, 32, 59, 9, + 10, 40, 41, 71, 75, 103, 107, 80, + 112, 32, 59, 9, 10, 40, 41, 73, + 105, 88, 120, 32, 59, 9, 10, 40, + 41, 80, 112, 75, 107, 73, 105, 32, + 59, 9, 10, 40, 41, 73, 105, 68, + 100, 32, 59, 9, 10, 40, 41, 71, + 75, 103, 107, 80, 112, 32, 59, 9, + 10, 40, 41, 73, 105, 88, 120, 32, + 59, 9, 10, 40, 41, 80, 112, 75, + 107, 73, 105, 32, 59, 9, 10, 40, + 41, 82, 114, 73, 105, 32, 59, 9, + 10, 40, 41, 46, 48, 57, 32, 46, + 59, 9, 10, 40, 41, 48, 57, 42, + 92, 95, 45, 57, 64, 90, 97, 122, + 32, 59, 9, 10, 40, 41, 42, 92, + 95, 45, 57, 64, 90, 97, 122, 9, + 32, 40, 41, 1034, 1083, 9, 32, 40, + 41, 42, 92, 95, 1034, 1083, 45, 57, + 64, 90, 97, 122, 9, 32, 40, 41, + 1034, 1083, 9, 32, 40, 41, 1034, 1083, + 48, 57, 9, 32, 40, 41, 1034, 1083, + 48, 57, 9, 32, 40, 41, 1034, 1083, + 48, 57, 9, 32, 40, 41, 68, 72, + 77, 83, 87, 100, 104, 109, 115, 119, + 1034, 1083, 48, 57, 9, 32, 40, 41, + 1034, 1083, 48, 57, 9, 32, 40, 41, + 68, 72, 77, 83, 87, 100, 104, 109, + 115, 119, 1034, 1083, 48, 57, 9, 32, + 40, 41, 1034, 1083, 48, 57, 9, 32, + 40, 41, 68, 72, 77, 83, 87, 100, + 104, 109, 115, 119, 1034, 1083, 48, 57, + 9, 32, 40, 41, 1034, 1083, 48, 57, + 32, 59, 68, 72, 77, 83, 87, 100, + 104, 109, 115, 119, 9, 10, 40, 41, + 48, 57, 32, 59, 9, 10, 40, 41, + 48, 57, 68, 72, 77, 83, 87, 100, + 104, 109, 115, 119, 48, 57, 32, 59, + 9, 10, 40, 41, 48, 57, 1034, 896, + 1151, 9, 32, 40, 41, 1034, 1083, 48, + 57, 68, 72, 77, 83, 87, 100, 104, + 109, 115, 119, 48, 57, 9, 32, 40, + 41, 1034, 1083, 48, 57, 1034, 896, 1151, + 9, 32, 40, 41, 1034, 1083, 48, 57, + 68, 72, 77, 83, 87, 100, 104, 109, + 115, 119, 48, 57, 9, 32, 40, 41, + 1034, 1083, 48, 57, 1034, 896, 1151, 9, + 32, 40, 41, 1034, 1083, 48, 57, 68, + 72, 77, 83, 87, 100, 104, 109, 115, + 119, 48, 57, 9, 32, 40, 41, 1034, + 1083, 48, 57, 1034, 896, 1151, 1034, 896, + 1151, 1034, 896, 1151, 32, 59, 9, 10, + 40, 41, 9, 32, 40, 41, 1034, 1083, + 9, 32, 40, 41, 1034, 1083, -128, 8, + 11, 58, 60, 127, 32, 59, 9, 10, + 40, 41, 1034, 896, 1151, 42, 92, 95, + 45, 57, 64, 90, 97, 122, 9, 32, + 40, 41, 1034, 1083, 9, 32, 40, 41, + 42, 92, 95, 1034, 1083, 45, 57, 64, + 90, 97, 122, 32, 59, 9, 10, 40, + 41, 1034, 896, 1151, 48, 57, 9, 32, + 40, 41, 1034, 1083, 48, 57, 9, 32, + 40, 41, 42, 92, 95, 1034, 1083, 45, + 57, 64, 90, 97, 122, 32, 59, 9, + 10, 40, 41, 1034, 896, 1151, 32, 59, + 9, 10, 40, 41, 9, 32, 40, 41, + 2058, 2107, 2314, 2363, 2570, 2619, 9, 32, + 40, 41, 2058, 2107, 2314, 2363, 2570, 2619, + -128, 8, 11, 58, 60, 127, 1034, 896, + 1151, 46, 48, 58, 65, 70, 97, 102, + 32, 46, 59, 9, 10, 40, 41, 48, + 58, 65, 70, 97, 102, 48, 57, 9, + 32, 40, 41, 1034, 1083, 48, 57, 9, + 32, 40, 41, 78, 83, 1034, 1083, 48, + 57, 9, 32, 40, 41, 1034, 1083, 48, + 57, 9, 32, 40, 41, 78, 83, 1034, + 1083, 48, 57, 9, 32, 40, 41, 46, + 1034, 1083, 48, 57, 9, 32, 40, 41, + 78, 83, 1034, 1083, 9, 32, 40, 41, + 1034, 1083, 9, 32, 40, 41, 1034, 1083, + 48, 57, 9, 32, 40, 41, 1034, 1083, + 48, 57, 9, 32, 40, 41, 69, 87, + 1034, 1083, 48, 57, 9, 32, 40, 41, + 1034, 1083, 48, 57, 9, 32, 40, 41, + 69, 87, 1034, 1083, 48, 57, 9, 32, + 40, 41, 46, 1034, 1083, 48, 57, 9, + 32, 40, 41, 69, 87, 1034, 1083, 9, + 32, 40, 41, 1034, 1083, 9, 32, 40, + 41, 45, 1034, 1083, 48, 57, 48, 57, + 9, 32, 40, 41, 46, 109, 2058, 2107, + 2314, 2363, 2570, 2619, 48, 57, 9, 32, + 40, 41, 2058, 2107, 2314, 2363, 2570, 2619, + 48, 57, 9, 32, 40, 41, 46, 109, + 2058, 2107, 2314, 2363, 2570, 2619, 48, 57, + 9, 32, 40, 41, 2058, 2107, 2314, 2363, + 2570, 2619, 48, 57, 9, 32, 40, 41, + 46, 109, 2058, 2107, 2314, 2363, 2570, 2619, + 48, 57, 9, 32, 40, 41, 2058, 2107, + 2314, 2363, 2570, 2619, 48, 57, 9, 32, + 40, 41, 46, 109, 2058, 2107, 2314, 2363, + 2570, 2619, 48, 57, 9, 32, 40, 41, + 2058, 2107, 2314, 2363, 2570, 2619, 1034, 896, + 1151, 9, 32, 40, 41, 109, 2058, 2107, + 2314, 2363, 2570, 2619, 48, 57, 9, 32, + 40, 41, 109, 2058, 2107, 2314, 2363, 2570, + 2619, 48, 57, 1034, 896, 1151, 9, 32, + 40, 41, 109, 2058, 2107, 2314, 2363, 2570, + 2619, 48, 57, 9, 32, 40, 41, 109, + 2058, 2107, 2314, 2363, 2570, 2619, 48, 57, + 9, 32, 40, 41, 2058, 2107, 2314, 2363, + 2570, 2619, 1034, 896, 1151, 9, 32, 40, + 41, 109, 2058, 2107, 2314, 2363, 2570, 2619, + 48, 57, 9, 32, 40, 41, 109, 2058, + 2107, 2314, 2363, 2570, 2619, 48, 57, 9, + 32, 40, 41, 2058, 2107, 2314, 2363, 2570, + 2619, 1034, 896, 1151, 9, 32, 40, 41, + 109, 2058, 2107, 2314, 2363, 2570, 2619, 48, + 57, 9, 32, 40, 41, 109, 2058, 2107, + 2314, 2363, 2570, 2619, 48, 57, 9, 32, + 40, 41, 2058, 2107, 2314, 2363, 2570, 2619, + 1034, 896, 1151, 9, 32, 40, 41, 1034, + 1083, 1034, 896, 1151, 9, 32, 40, 41, + 1034, 1083, 48, 57, 9, 32, 40, 41, + 1034, 1083, 48, 57, 1034, 896, 1151, 1034, + 896, 1151, 1034, 896, 1151, 9, 32, 40, + 41, 1034, 1083, 1034, 896, 1151, 9, 32, + 40, 41, 1034, 1083, 48, 57, 9, 32, + 40, 41, 1034, 1083, 48, 57, 1034, 896, + 1151, 1034, 896, 1151, 48, 57, 9, 32, + 40, 41, 1034, 1083, 48, 57, 9, 32, + 40, 41, 1034, 1083, 48, 57, 9, 32, + 40, 41, 1034, 1083, 48, 57, 9, 32, + 40, 41, 1034, 1083, 48, 57, 9, 32, + 40, 41, 1034, 1083, 48, 57, 9, 32, + 40, 41, 42, 92, 95, 1034, 1083, 45, + 57, 64, 90, 97, 122, 32, 59, 9, + 10, 40, 41, 1034, 896, 1151, 1034, 896, + 1151, 1034, 896, 1151, 48, 57, 9, 32, + 40, 41, 1034, 1083, 48, 57, 9, 32, + 40, 41, 1034, 1083, 48, 57, 9, 32, + 40, 41, 1034, 1083, 48, 57, 9, 32, + 40, 41, 1034, 1083, -128, 8, 11, 58, + 60, 127, 9, 32, 40, 41, 1034, 1083, + 9, 32, 40, 41, 1034, 1083, -128, 8, + 11, 58, 60, 127, 9, 32, 40, 41, + 1034, 1083, 9, 32, 40, 41, 1034, 1083, + -128, 8, 11, 58, 60, 127, 9, 32, + 40, 41, 1034, 1083, 9, 32, 40, 41, + 42, 92, 95, 1034, 1083, 45, 57, 64, + 90, 97, 122, 32, 59, 9, 10, 40, + 41, 1034, 896, 1151, 1034, 896, 1151, 1034, + 896, 1151, 1034, 896, 1151, 1034, 896, 1151, + 48, 57, 65, 90, 97, 122, 9, 32, + 40, 41, 1034, 1083, 9, 32, 40, 41, + 1034, 1083, 48, 57, 9, 32, 40, 41, + 1034, 1083, 48, 57, 9, 32, 40, 41, + 1034, 1083, 48, 57, 65, 90, 97, 122, + 9, 32, 40, 41, 1034, 1083, 9, 32, + 40, 41, 43, 1034, 1083, 47, 57, 65, + 90, 97, 122, 1546, 1595, 1034, 896, 1151, + 1034, 896, 1151, 1034, 896, 1151, 9, 32, + 33, 40, 41, 49, 50, 2058, 2107, 2314, + 2363, 2570, 2619, 49, 50, 58, 46, 48, + 57, 47, 46, 57, 48, 57, 9, 32, + 40, 41, 2058, 2107, 2314, 2363, 2570, 2619, + 48, 57, 1034, 896, 1151, 58, 46, 48, + 58, 65, 70, 97, 102, 47, 46, 58, + 65, 70, 97, 102, 48, 57, 9, 32, + 40, 41, 1034, 1083, 48, 57, 9, 32, + 40, 41, 1034, 1083, 48, 57, 65, 90, + 97, 122, 9, 32, 40, 41, 1034, 1083, + 9, 32, 40, 41, 1034, 1083, 48, 57, + 9, 32, 40, 41, 1034, 1083, 48, 57, + 9, 32, 40, 41, 1034, 1083, 48, 57, + 65, 70, 97, 102, 48, 57, 65, 70, + 97, 102, 9, 32, 40, 41, 2058, 2107, + 2314, 2363, 2570, 2619, 48, 57, 65, 70, + 97, 102, 1034, 896, 1151, 1034, 896, 1151, + 1034, 896, 1151, 1034, 896, 1151, 48, 57, + 9, 32, 40, 41, 1034, 1083, 48, 57, + 9, 32, 40, 41, 1034, 1083, 48, 57, + 9, 32, 40, 41, 1034, 1083, 48, 57, + 9, 32, 40, 41, 1034, 1083, 48, 57, + 65, 70, 97, 102, 48, 57, 65, 70, + 97, 102, 9, 32, 40, 41, 2058, 2107, + 2314, 2363, 2570, 2619, 48, 57, 65, 70, + 97, 102, 1034, 896, 1151, 1034, 896, 1151, + 1034, 896, 1151, 48, 57, 9, 32, 40, + 41, 1034, 1083, 48, 57, 9, 32, 40, + 41, 48, 49, 50, 51, 1034, 1083, 9, + 32, 40, 41, 1034, 1083, 9, 32, 40, + 41, 1034, 1083, 48, 57, 9, 32, 40, + 41, 1034, 1083, 48, 57, 9, 32, 40, + 41, 46, 1034, 1083, 6153, 6176, 6184, 6185, + 6409, 6432, 6440, 6441, 6665, 6688, 6696, 6697, + 9482, 9531, 9738, 9787, 10250, 10299, 10506, 10555, + 10762, 10811, 11274, 11323, 11530, 11579, 11786, 11835, + 12298, 12347, 12554, 12603, 12810, 12859, 3081, 3104, + 3112, 3113, 3115, 4106, 4155, 3119, 3129, 3137, + 3162, 3169, 3194, 1546, 1595, 4106, 3968, 4223, + 4617, 4640, 4648, 4649, 13578, 13627, 13834, 13883, + 14346, 14395, 14602, 14651, 14858, 14907, 5642, 5504, + 5759, 3115, 6153, 6176, 6184, 6185, 6409, 6432, + 6440, 6441, 6665, 6688, 6696, 6697, 9482, 9531, + 9738, 9787, 10250, 10299, 10506, 10555, 10762, 10811, + 11274, 11323, 11530, 11579, 11786, 11835, 12298, 12347, + 12554, 12603, 12810, 12859, 3119, 3129, 3137, 3162, + 3169, 3194, 7690, 8202, 8714, 7552, 7807, 8064, + 8319, 8576, 8831, 1034, 896, 1151, 1034, 896, + 1151, 9, 32, 40, 41, 1034, 1083, 9, + 32, 40, 41, 1034, 1083, 48, 57, 9, + 32, 40, 41, 1034, 1083, 48, 57, 9, + 32, 40, 41, 46, 1034, 1083, 48, 57, + 46, 6153, 6176, 6184, 6185, 6409, 6432, 6440, + 6441, 6665, 6688, 6696, 6697, 9482, 9531, 9738, + 9787, 10250, 10299, 10506, 10555, 10762, 10811, 11274, + 11323, 11530, 11579, 11786, 11835, 12298, 12347, 12554, + 12603, 12810, 12859, 48, 57, 1034, 896, 1151, + 1034, 896, 1151, 9, 32, 40, 41, 1034, + 1083, 9, 32, 40, 41, 1034, 1083, 48, + 57, 9, 32, 40, 41, 1034, 1083, 48, + 57, 9, 32, 40, 41, 46, 1034, 1083, + 48, 58, 65, 70, 97, 102, 46, 6153, + 6176, 6184, 6185, 6409, 6432, 6440, 6441, 6665, + 6688, 6696, 6697, 9482, 9531, 9738, 9787, 10250, + 10299, 10506, 10555, 10762, 10811, 11274, 11323, 11530, + 11579, 11786, 11835, 12298, 12347, 12554, 12603, 12810, + 12859, 48, 58, 65, 70, 97, 102, 1034, + 896, 1151, 1034, 896, 1151, 9, 32, 40, + 41, 1034, 1083, 9, 32, 40, 41, 1034, + 1083, 48, 57, 9, 32, 40, 41, 1034, + 1083, 48, 57, 9, 32, 40, 41, 42, + 92, 95, 1034, 1083, 45, 57, 64, 90, + 97, 122, 6153, 6176, 6184, 6185, 6409, 6432, + 6440, 6441, 6665, 6688, 6696, 6697, 9482, 9531, + 9738, 9787, 10250, 10299, 10506, 10555, 10762, 10811, + 11274, 11323, 11530, 11579, 11786, 11835, 12298, 12347, + 12554, 12603, 12810, 12859, 1034, 896, 1151, 1034, + 896, 1151, 1034, 896, 1151, 65, 67, 68, + 69, 72, 73, 75, 76, 77, 78, 80, + 82, 83, 84, 85, 97, 99, 100, 101, + 104, 105, 107, 108, 109, 110, 112, 114, + 115, 116, 117, 9, 32, 40, 41, 65, + 70, 80, 97, 102, 112, 1034, 1083, 9, + 32, 40, 41, 1034, 1083, 48, 57, 65, + 90, 97, 122, 9, 32, 40, 41, 1034, + 1083, 9, 32, 40, 41, 1034, 1083, 48, + 57, 9, 32, 40, 41, 1034, 1083, 48, + 57, 9, 32, 40, 41, 1034, 1083, 48, + 57, 9, 32, 40, 41, 1034, 1083, 48, + 57, 9, 32, 40, 41, 1034, 1083, 48, + 57, 9, 32, 40, 41, 1034, 1083, 48, + 57, 9, 32, 40, 41, 1034, 1083, 48, + 57, 9, 32, 40, 41, 1034, 1083, 48, + 57, 9, 32, 40, 41, 1034, 1083, 48, + 57, 9, 32, 40, 41, 1034, 1083, 48, + 57, 9, 32, 40, 41, 42, 92, 95, + 1034, 1083, 45, 57, 64, 90, 97, 122, + 9, 32, 40, 41, 1034, 1083, 9, 32, + 40, 41, 43, 1034, 1083, 47, 57, 65, + 90, 97, 122, 1546, 1595, 1034, 896, 1151, + 1034, 896, 1151, 1034, 896, 1151, 1034, 896, + 1151, 1034, 896, 1151, 1034, 896, 1151, 1034, + 896, 1151, 1034, 896, 1151, 65, 97, 65, + 97, 9, 32, 40, 41, 1034, 1083, 83, + 115, 68, 100, 66, 98, 9, 32, 40, + 41, 1034, 1083, 76, 108, 9, 32, 40, + 41, 1034, 1083, 65, 68, 69, 78, 97, + 100, 101, 110, 65, 97, 9, 32, 40, + 41, 1034, 1083, 78, 83, 110, 115, 83, + 115, 75, 107, 69, 101, 89, 121, 9, + 32, 40, 41, 1034, 1083, 9, 32, 40, + 41, 1034, 1083, 82, 114, 84, 116, 9, + 32, 40, 41, 1034, 1083, 65, 97, 77, + 109, 69, 101, 9, 32, 40, 41, 1034, + 1083, 72, 78, 83, 104, 110, 115, 67, + 99, 73, 105, 68, 100, 9, 32, 40, + 41, 1034, 1083, 65, 83, 97, 115, 77, + 109, 69, 101, 9, 32, 40, 41, 1034, + 1083, 75, 107, 69, 101, 89, 121, 9, + 32, 40, 41, 1034, 1083, 9, 32, 40, + 41, 1034, 1083, 85, 117, 73, 105, 52, + 54, 56, 9, 32, 40, 41, 1034, 1083, + 52, 9, 32, 40, 41, 1034, 1083, 73, + 105, 78, 110, 70, 102, 79, 111, 9, + 32, 40, 41, 1034, 1083, 80, 112, 83, + 115, 69, 101, 67, 99, 75, 107, 69, + 101, 89, 121, 9, 32, 40, 41, 1034, + 1083, 69, 88, 101, 120, 89, 121, 9, + 32, 40, 41, 1034, 1083, 9, 32, 40, + 41, 1034, 1083, 51, 54, 79, 80, 111, + 112, 50, 9, 32, 40, 41, 1034, 1083, + 52, 9, 32, 40, 41, 1034, 1083, 67, + 99, 9, 32, 40, 41, 1034, 1083, 9, + 32, 40, 41, 1034, 1083, 73, 88, 105, + 120, 78, 110, 70, 102, 79, 111, 9, + 32, 40, 41, 1034, 1083, 9, 32, 40, + 41, 1034, 1083, 65, 73, 83, 97, 105, + 115, 80, 112, 84, 116, 82, 114, 9, + 32, 40, 41, 1034, 1083, 68, 100, 9, + 32, 40, 41, 1034, 1083, 9, 32, 40, + 41, 69, 101, 1034, 1083, 67, 99, 9, + 32, 40, 41, 51, 1034, 1083, 9, 32, + 40, 41, 80, 112, 1034, 1083, 65, 97, + 82, 114, 65, 97, 77, 109, 9, 32, + 40, 41, 1034, 1083, 84, 116, 82, 114, + 9, 32, 40, 41, 1034, 1083, 80, 82, + 84, 112, 114, 116, 9, 32, 40, 41, + 1034, 1083, 83, 115, 73, 105, 71, 103, + 9, 32, 40, 41, 1034, 1083, 9, 32, + 40, 41, 1034, 1083, 79, 80, 82, 83, + 111, 112, 114, 115, 65, 97, 9, 32, + 40, 41, 1034, 1083, 70, 102, 9, 32, + 40, 41, 1034, 1083, 86, 118, 9, 32, + 40, 41, 1034, 1083, 72, 104, 70, 102, + 80, 112, 9, 32, 40, 41, 1034, 1083, + 76, 88, 89, 108, 120, 121, 83, 115, + 65, 97, 9, 32, 40, 41, 1034, 1083, + 84, 116, 9, 32, 40, 41, 1034, 1083, + 80, 112, 69, 101, 48, 57, 9, 32, + 40, 41, 1034, 1083, 48, 57, 82, 114, + 73, 105, 9, 32, 40, 41, 1034, 1083, + 42, 92, 95, 45, 57, 64, 90, 97, + 122, 32, 59, 9, 10, 40, 41, 32, + 59, 9, 10, 40, 41, 48, 57, 9, + 32, 40, 41, 1034, 1083, 48, 57, 9, + 32, 40, 41, 1034, 1083, 48, 57, 9, + 32, 40, 41, 1034, 1083, 48, 57, 9, + 32, 40, 41, 1034, 1083, 48, 57, 65, + 90, 97, 122, 9, 32, 40, 41, 1034, + 1083, 9, 32, 40, 41, 43, 1034, 1083, + 47, 57, 65, 90, 97, 122, 1546, 1595, + 1034, 896, 1151, 1034, 896, 1151, 1034, 896, + 1151, 43, 47, 57, 65, 90, 97, 122, + 1546, 1595, 48, 57, 9, 32, 40, 41, + 1034, 1083, 48, 57, 9, 32, 40, 41, + 1034, 1083, 48, 57, 9, 32, 40, 41, + 1034, 1083, 48, 57, 9, 32, 40, 41, + 1034, 1083, 48, 57, 9, 32, 40, 41, + 1034, 1083, 48, 57, 9, 32, 40, 41, + 45, 1034, 1083, 48, 57, 65, 70, 97, + 102, 9, 32, 40, 41, 1034, 1083, 9, + 32, 40, 41, 1034, 1083, 48, 57, 65, + 86, 97, 118, 48, 57, 65, 86, 97, + 118, 61, 48, 57, 65, 86, 97, 118, + 48, 57, 65, 86, 97, 118, 61, 48, + 57, 65, 86, 97, 118, 61, 48, 57, + 65, 86, 97, 118, 48, 57, 65, 86, + 97, 118, 61, 48, 57, 65, 86, 97, + 118, 32, 59, 9, 10, 40, 41, 48, + 57, 65, 86, 97, 118, 32, 59, 9, + 10, 40, 41, 61, 61, 61, 61, 61, + 1034, 896, 1151, 48, 57, 65, 70, 97, + 102, 9, 32, 40, 41, 1034, 1083, 48, + 57, 65, 70, 97, 102, 1034, 896, 1151, + 1034, 896, 1151, 1034, 896, 1151, 48, 57, + 9, 32, 40, 41, 1034, 1083, 48, 57, + 9, 32, 40, 41, 1034, 1083, 48, 57, + 9, 32, 40, 41, 1034, 1083, 48, 57, + 9, 32, 40, 41, 1034, 1083, 48, 57, + 9, 32, 40, 41, 1034, 1083, 48, 57, + 9, 32, 40, 41, 45, 1034, 1083, 48, + 57, 65, 70, 97, 102, 32, 59, 9, + 10, 40, 41, 48, 57, 65, 70, 97, + 102, 32, 59, 9, 10, 40, 41, 48, + 57, 65, 70, 97, 102, 1034, 896, 1151, + 1034, 896, 1151, 1034, 896, 1151, 48, 57, + 9, 32, 40, 41, 1034, 1083, 48, 57, + 9, 32, 40, 41, 1034, 1083, 48, 57, + 9, 32, 40, 41, 1034, 1083, 48, 57, + 9, 32, 40, 41, 1034, 1083, 48, 57, + 9, 32, 40, 41, 1034, 1083, 48, 57, + 9, 32, 40, 41, 1034, 1083, 48, 57, + 65, 70, 97, 102, 48, 57, 65, 70, + 97, 102, 9, 32, 40, 41, 2058, 2107, + 2314, 2363, 2570, 2619, 48, 57, 65, 70, + 97, 102, 1034, 896, 1151, 1034, 896, 1151, + 1034, 896, 1151, 1034, 896, 1151, 48, 57, + 9, 32, 40, 41, 1034, 1083, 48, 57, + 9, 32, 40, 41, 46, 1034, 1083, 48, + 57, 32, 46, 59, 9, 10, 40, 41, + 48, 57, 1034, 896, 1151, 48, 57, 9, + 32, 40, 41, 1034, 1083, 48, 57, 9, + 32, 40, 41, 1034, 1083, 48, 57, 65, + 70, 97, 102, 48, 57, 65, 70, 97, + 102, 48, 57, 65, 70, 97, 102, 48, + 57, 65, 70, 97, 102, 58, 48, 57, + 65, 70, 97, 102, 48, 57, 65, 70, + 97, 102, 48, 57, 65, 70, 97, 102, + 48, 57, 65, 70, 97, 102, 32, 58, + 59, 9, 10, 40, 41, 1034, 896, 1151, + 48, 57, 65, 70, 97, 102, 48, 57, + 65, 70, 97, 102, 45, 48, 57, 65, + 70, 97, 102, 48, 57, 65, 70, 97, + 102, 32, 45, 59, 9, 10, 40, 41, + 48, 57, 65, 70, 97, 102, 48, 57, + 65, 70, 97, 102, 45, 48, 57, 65, + 70, 97, 102, 48, 57, 65, 70, 97, + 102, 32, 45, 59, 9, 10, 40, 41, + 48, 57, 9, 32, 40, 41, 1034, 1083, + 48, 57, 9, 32, 40, 41, 1034, 1083, + 48, 57, 9, 32, 40, 41, 1034, 1083, + 48, 57, 9, 32, 40, 41, 1034, 1083, + -128, 8, 11, 58, 60, 127, 32, 59, + 9, 10, 40, 41, 1034, 896, 1151, 1034, + 896, 1151, 48, 57, 9, 32, 40, 41, + 1034, 1083, 48, 57, 9, 32, 40, 41, + 1034, 1083, -128, 8, 11, 58, 60, 127, + 9, 32, 40, 41, 1034, 1083, 9, 32, + 40, 41, 1034, 1083, -128, 8, 11, 58, + 60, 127, 32, 59, 9, 10, 40, 41, + 1034, 896, 1151, 1034, 896, 1151, 9, 32, + 36, 40, 41, 42, 92, 95, 778, 827, + 1034, 1083, 45, 57, 64, 90, 97, 122, + 9, 32, 36, 40, 41, 42, 58, 92, + 95, 1802, 1851, 2058, 2107, 2314, 2363, 2570, + 2619, -128, 8, 11, 44, 45, 57, 60, + 63, 64, 90, 91, 96, 97, 122, 123, + 127, 9, 32, 36, 40, 41, 42, 65, + 67, 68, 69, 72, 73, 75, 76, 77, + 78, 80, 82, 83, 84, 85, 92, 95, + 97, 99, 100, 101, 104, 105, 107, 108, + 109, 110, 112, 114, 115, 116, 117, 778, + 827, 1034, 1083, 45, 57, 64, 90, 98, + 122, 9, 32, 36, 40, 41, 42, 65, + 67, 68, 69, 72, 73, 75, 76, 77, + 78, 80, 82, 83, 84, 85, 92, 95, + 97, 99, 100, 101, 104, 105, 107, 108, + 109, 110, 112, 114, 115, 116, 117, 778, + 827, 1034, 1083, 45, 47, 48, 57, 64, + 90, 98, 122, 9, 32, 36, 40, 41, + 42, 92, 95, 778, 827, 1034, 1083, 45, + 57, 64, 90, 97, 122, 9, 32, 36, + 40, 41, 42, 65, 67, 68, 69, 72, + 73, 75, 76, 77, 78, 80, 82, 83, + 84, 85, 92, 95, 97, 99, 100, 101, + 104, 105, 107, 108, 109, 110, 112, 114, + 115, 116, 117, 778, 827, 1034, 1083, 45, + 47, 48, 57, 64, 90, 98, 122, 9, + 32, 36, 40, 41, 42, 92, 95, 778, + 827, 1034, 1083, 45, 57, 64, 90, 97, + 122, 9, 32, 36, 40, 41, 42, 58, + 92, 95, 1802, 1851, 2058, 2107, 2314, 2363, + 2570, 2619, -128, 8, 11, 44, 45, 57, + 60, 63, 64, 90, 91, 96, 97, 122, + 123, 127, 9, 32, 36, 40, 41, 42, + 58, 65, 67, 68, 69, 72, 73, 75, + 76, 77, 78, 80, 82, 83, 84, 85, + 92, 95, 97, 99, 100, 101, 104, 105, + 107, 108, 109, 110, 112, 114, 115, 116, + 117, 1802, 1851, 2058, 2107, 2314, 2363, 2570, + 2619, -128, 8, 11, 44, 45, 47, 48, + 57, 60, 63, 64, 90, 91, 96, 98, + 122, 123, 127, 9, 32, 36, 40, 41, + 42, 58, 65, 67, 68, 69, 72, 73, + 75, 76, 77, 78, 80, 82, 83, 84, + 85, 92, 95, 97, 99, 100, 101, 104, + 105, 107, 108, 109, 110, 112, 114, 115, + 116, 117, 1802, 1851, 2058, 2107, 2314, 2363, + 2570, 2619, -128, 8, 11, 44, 45, 47, + 48, 57, 60, 63, 64, 90, 91, 96, + 98, 122, 123, 127, 32, 33, 59, 92, + 9, 10, 35, 39, 40, 41, 42, 126, + 9, 32, 40, 41, 42, 46, 92, 95, + 1034, 1083, 45, 57, 65, 90, 97, 122, + 9, 32, 40, 41, 43, 2058, 2107, 2314, + 2363, 2570, 2619, 47, 57, 65, 90, 97, + 122, 1034, 896, 1151, 9, 32, 40, 41, + 65, 67, 68, 69, 72, 73, 75, 76, + 77, 78, 80, 82, 83, 84, 85, 97, + 99, 100, 101, 104, 105, 107, 108, 109, + 110, 112, 114, 115, 116, 117, 2058, 2107, + 2314, 2363, 2570, 2619, 1034, 896, 1151, 9, + 32, 40, 41, 2058, 2107, 2314, 2363, 2570, + 2619, 48, 57, 65, 70, 97, 102, 1034, + 896, 1151, 9, 32, 40, 41, 1034, 1083, + 48, 57, 65, 70, 97, 102, 9, 32, + 40, 41, 2058, 2107, 2314, 2363, 2570, 2619, + 48, 57, 65, 70, 97, 102, 1034, 896, + 1151, 1034, 896, 1151, 9, 32, 40, 41, + 2058, 2107, 2314, 2363, 2570, 2619, -128, 8, + 11, 58, 60, 127, 1034, 896, 1151, 9, + 32, 40, 41, 2058, 2107, 2314, 2363, 2570, + 2619, 1034, 896, 1151, 9, 32, 40, 41, + 2058, 2107, 2314, 2363, 2570, 2619, 48, 57, + 1034, 896, 1151, 9, 32, 40, 41, 2058, + 2107, 2314, 2363, 2570, 2619, 48, 57, 1034, + 896, 1151, 9, 32, 40, 41, 2058, 2107, + 2314, 2363, 2570, 2619, 48, 57, 1034, 896, + 1151, 9, 32, 33, 40, 41, 49, 50, + 2058, 2107, 2314, 2363, 2570, 2619, 1034, 896, + 1151, 9, 32, 40, 41, 2058, 2107, 2314, + 2363, 2570, 2619, 48, 57, 65, 70, 97, + 102, 1034, 896, 1151, 9, 32, 40, 41, + 2058, 2107, 2314, 2363, 2570, 2619, 48, 57, + 65, 70, 97, 102, 1034, 896, 1151, 4617, + 4640, 4648, 4649, 13578, 13627, 13834, 13883, 14346, + 14395, 14602, 14651, 14858, 14907, 5642, 5504, 5759, + 3081, 3104, 3112, 3113, 3115, 4106, 4155, 3119, + 3129, 3137, 3162, 3169, 3194, 4106, 3968, 4223, + 3115, 6153, 6176, 6184, 6185, 6409, 6432, 6440, + 6441, 6665, 6688, 6696, 6697, 9482, 9531, 9738, + 9787, 10250, 10299, 10506, 10555, 10762, 10811, 11274, + 11323, 11530, 11579, 11786, 11835, 12298, 12347, 12554, + 12603, 12810, 12859, 3119, 3129, 3137, 3162, 3169, + 3194, 7690, 8202, 8714, 7552, 7807, 8064, 8319, + 8576, 8831, 9, 32, 40, 41, 2058, 2107, + 2314, 2363, 2570, 2619, 48, 57, 65, 70, + 97, 102, 1034, 896, 1151, 0 +}; + +static const char _zone_scanner_single_lengths[] = { + 0, 38, 16, 36, 16, 11, 8, 8, + 1, 1, 1, 13, 13, 2, 44, 18, + 38, 8, 2, 10, 12, 12, 4, 2, + 2, 2, 2, 10, 10, 2, 2, 10, + 2, 2, 2, 10, 6, 2, 2, 2, + 10, 4, 2, 2, 10, 2, 2, 2, + 10, 10, 2, 2, 2, 1, 10, 1, + 10, 2, 2, 2, 2, 10, 4, 6, + 36, 2, 2, 2, 2, 2, 2, 2, + 10, 4, 2, 10, 10, 6, 1, 10, + 1, 10, 2, 10, 10, 4, 2, 2, + 2, 10, 10, 6, 2, 2, 2, 10, + 2, 10, 12, 2, 11, 12, 2, 2, + 2, 2, 10, 2, 2, 10, 6, 10, + 2, 2, 2, 10, 10, 8, 2, 10, + 2, 10, 2, 10, 2, 2, 2, 10, + 6, 2, 2, 10, 2, 10, 2, 2, + 0, 10, 2, 2, 10, 1, 38, 4, + 6, 36, 16, 6, 10, 6, 1, 1, + 6, 2, 2, 2, 2, 2, 2, 2, + 6, 36, 1, 16, 36, 1, 6, 10, + 6, 1, 16, 42, 18, 13, 38, 2, + 8, 8, 44, 16, 14, 10, 10, 12, + 12, 14, 12, 14, 10, 14, 16, 14, + 10, 2, 14, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 8, 18, 38, 2, + 18, 44, 16, 14, 10, 10, 12, 12, + 14, 12, 14, 10, 14, 16, 14, 10, + 9, 38, 12, 12, 12, 12, 2, 2, + 10, 2, 2, 2, 10, 2, 10, 2, + 8, 8, 12, 12, 12, 12, 1, 44, + 12, 12, 14, 12, 8, 8, 10, 10, + 12, 10, 12, 8, 12, 14, 12, 8, + 2, 2, 10, 2, 1, 1, 5, 6, + 6, 0, 0, 0, 6, 2, 2, 2, + 4, 0, 0, 0, 4, 4, 2, 0, + 0, 0, 4, 6, 6, 18, 8, 1, + 8, 10, 8, 1, 6, 10, 4, 12, + 8, 1, 0, 0, 0, 4, 8, 1, + 6, 6, 8, 12, 4, 12, 8, 1, + 0, 0, 0, 4, 8, 2, 10, 1, + 1, 1, 1, 2, 2, 11, 1, 1, + 10, 40, 16, 2, 2, 10, 1, 8, + 2, 10, 4, 2, 2, 2, 2, 10, + 10, 2, 2, 10, 2, 2, 2, 10, + 6, 2, 2, 2, 10, 4, 2, 2, + 10, 2, 2, 2, 10, 10, 2, 2, + 2, 1, 10, 1, 10, 2, 2, 2, + 2, 10, 2, 2, 2, 2, 2, 2, + 2, 10, 4, 2, 10, 10, 6, 1, + 10, 1, 10, 2, 10, 10, 4, 2, + 2, 2, 10, 10, 6, 2, 2, 2, + 10, 2, 10, 12, 2, 11, 12, 2, + 2, 2, 2, 10, 2, 2, 10, 6, + 10, 2, 2, 2, 10, 10, 8, 2, + 10, 2, 10, 2, 10, 2, 2, 2, + 10, 6, 2, 2, 10, 2, 10, 2, + 2, 0, 10, 2, 2, 10, 2, 2, + 2, 10, 2, 10, 6, 6, 6, 6, + 0, 10, 1, 1, 1, 6, 7, 8, + 6, 0, 10, 1, 1, 6, 1, 10, + 2, 4, 2, 2, 3, 2, 2, 2, + 2, 1, 1, 2, 2, 2, 1, 2, + 4, 4, 1, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 1, 1, 2, 2, + 2, 1, 1, 1, 2, 1, 1, 2, + 2, 2, 1, 1, 1, 2, 2, 1, + 1, 1, 1, 2, 1, 1, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 4, 2, 2, + 2, 2, 2, 2, 2, 2, 4, 2, + 1, 2, 2, 2, 3, 3, 2, 2, + 2, 2, 1, 1, 2, 2, 2, 1, + 2, 1, 1, 2, 1, 1, 2, 12, + 2, 2, 2, 2, 2, 2, 2, 6, + 2, 2, 2, 2, 2, 2, 4, 2, + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 4, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, + 1, 3, 3, 2, 3, 6, 9, 6, + 6, 6, 6, 16, 6, 16, 6, 16, + 6, 12, 2, 10, 2, 1, 6, 10, + 6, 1, 6, 10, 6, 1, 6, 10, + 6, 1, 1, 1, 2, 6, 6, 2, + 1, 3, 6, 9, 2, 1, 0, 6, + 9, 2, 1, 2, 10, 10, 1, 1, + 3, 0, 6, 8, 6, 8, 7, 8, + 6, 6, 6, 8, 6, 8, 7, 8, + 6, 7, 0, 12, 10, 12, 10, 12, + 10, 12, 10, 1, 11, 11, 1, 11, + 11, 10, 1, 11, 11, 10, 1, 11, + 11, 10, 1, 6, 1, 6, 6, 1, + 1, 1, 6, 1, 6, 6, 1, 1, + 0, 6, 6, 6, 6, 6, 9, 2, + 1, 1, 1, 0, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 9, 2, 1, + 1, 1, 1, 1, 0, 6, 6, 6, + 6, 6, 7, 2, 1, 1, 1, 13, + 2, 1, 1, 1, 0, 10, 1, 1, + 1, 1, 0, 6, 6, 6, 6, 6, + 6, 0, 10, 1, 1, 1, 1, 0, + 6, 6, 6, 6, 0, 10, 1, 1, + 1, 0, 6, 10, 6, 6, 6, 7, + 34, 7, 2, 1, 14, 1, 35, 3, + 1, 1, 6, 6, 6, 7, 35, 1, + 1, 6, 6, 6, 7, 35, 1, 1, + 6, 6, 6, 9, 34, 1, 1, 1, + 30, 12, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 9, 6, + 7, 2, 1, 1, 1, 1, 1, 1, + 1, 1, 2, 2, 6, 2, 2, 2, + 6, 2, 6, 8, 2, 6, 4, 2, + 2, 2, 2, 6, 6, 2, 2, 6, + 2, 2, 2, 6, 6, 2, 2, 2, + 6, 4, 2, 2, 6, 2, 2, 2, + 6, 6, 2, 2, 2, 1, 6, 1, + 6, 2, 2, 2, 2, 6, 2, 2, + 2, 2, 2, 2, 2, 6, 4, 2, + 6, 6, 6, 1, 6, 1, 6, 2, + 6, 6, 4, 2, 2, 2, 6, 6, + 6, 2, 2, 2, 6, 2, 6, 8, + 2, 7, 8, 2, 2, 2, 2, 6, + 2, 2, 6, 6, 6, 2, 2, 2, + 6, 6, 8, 2, 6, 2, 6, 2, + 6, 2, 2, 2, 6, 6, 2, 2, + 6, 2, 6, 2, 2, 0, 6, 2, + 2, 6, 3, 2, 2, 0, 6, 6, + 6, 6, 6, 7, 2, 1, 1, 1, + 1, 2, 0, 6, 6, 6, 6, 6, + 7, 6, 6, 0, 1, 0, 1, 1, + 0, 1, 2, 2, 1, 1, 1, 1, + 1, 1, 0, 6, 1, 1, 1, 0, + 6, 6, 6, 6, 6, 7, 2, 0, + 2, 1, 1, 1, 0, 6, 6, 6, + 6, 6, 6, 0, 10, 1, 1, 1, + 1, 0, 6, 7, 3, 1, 0, 6, + 6, 0, 0, 0, 1, 0, 0, 0, + 0, 3, 1, 0, 0, 1, 0, 0, + 3, 0, 0, 1, 0, 0, 3, 0, + 6, 6, 6, 6, 2, 1, 1, 0, + 6, 6, 6, 6, 2, 1, 1, 12, + 17, 42, 42, 12, 42, 12, 17, 47, + 47, 0, 0, 0, 4, 0, 0, 0, + 10, 0, 11, 1, 0, 40, 1, 0, + 10, 1, 6, 0, 10, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 10, 1, 0, 0, 10, 1, 10, 1, + 10, 1, 10, 1, 0, 0, 0, 0, + 13, 1, 0, 10, 1, 0, 10, 1, + 0, 14, 1, 7, 1, 35, 3, 0, + 0, 0, 0, 0, 0, 0, 10, 1, + 0, 0, 0, 0, 0, 0 +}; + +static const char _zone_scanner_range_lengths[] = { + 0, 1, 1, 0, 0, 3, 0, 0, + 0, 0, 1, 3, 3, 2, 4, 1, + 0, 0, 0, 0, 1, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 0, 0, 0, 1, 1, 0, + 0, 1, 1, 1, 1, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 2, + 0, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 0, 4, 0, 3, 1, 2, + 3, 3, 4, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 2, 0, 0, 0, 0, 0, 0, + 2, 0, 0, 2, 0, 1, 1, 2, + 0, 4, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 3, 1, 1, 1, 2, 2, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 2, + 3, 3, 2, 2, 2, 2, 1, 4, + 1, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 2, 2, 0, 2, 0, 0, 3, 5, + 5, 1, 1, 1, 5, 2, 2, 2, + 4, 3, 1, 1, 4, 1, 2, 3, + 1, 1, 1, 0, 1, 1, 0, 0, + 1, 1, 1, 1, 0, 3, 3, 3, + 0, 0, 1, 1, 1, 3, 0, 1, + 0, 3, 0, 3, 3, 3, 0, 0, + 1, 1, 1, 3, 0, 2, 3, 1, + 1, 3, 3, 3, 3, 3, 1, 0, + 0, 0, 0, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 1, 3, + 3, 3, 1, 1, 1, 0, 1, 1, + 3, 3, 3, 1, 1, 1, 1, 1, + 3, 0, 2, 0, 2, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 2, + 0, 0, 0, 0, 0, 0, 0, 2, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 2, 0, 0, 0, + 0, 0, 0, 0, 0, 2, 0, 0, + 0, 0, 0, 2, 0, 0, 2, 0, + 0, 0, 0, 0, 0, 0, 2, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 2, 0, 0, 2, 0, 0, 0, 0, + 0, 2, 0, 0, 0, 2, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 2, 0, 0, 2, 0, 0, 2, 1, + 3, 0, 0, 0, 0, 0, 2, 0, + 0, 0, 0, 0, 0, 2, 0, 0, + 2, 0, 0, 2, 0, 0, 0, 2, + 0, 0, 2, 0, 0, 2, 0, 0, + 2, 0, 0, 0, 2, 0, 0, 2, + 1, 3, 3, 2, 3, 0, 3, 0, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 3, 3, 1, 3, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 2, 0, 3, 2, + 1, 3, 0, 3, 2, 1, 1, 1, + 3, 2, 1, 2, 0, 3, 1, 3, + 5, 1, 1, 1, 1, 1, 1, 0, + 0, 1, 1, 1, 1, 1, 1, 0, + 0, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 0, 1, 1, 1, 1, 1, + 1, 0, 1, 1, 1, 0, 1, 1, + 1, 0, 1, 0, 1, 1, 1, 1, + 1, 1, 0, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 3, 2, + 1, 1, 1, 1, 1, 1, 1, 3, + 0, 3, 0, 3, 0, 3, 2, 1, + 1, 1, 1, 1, 3, 0, 1, 1, + 3, 0, 3, 0, 1, 1, 1, 0, + 0, 0, 1, 1, 1, 1, 1, 0, + 3, 3, 1, 1, 3, 0, 1, 1, + 3, 3, 3, 1, 1, 1, 1, 1, + 1, 1, 1, 3, 3, 3, 1, 1, + 1, 1, 1, 0, 0, 1, 1, 0, + 0, 3, 0, 1, 0, 1, 3, 3, + 1, 1, 0, 1, 1, 1, 1, 1, + 1, 0, 1, 1, 3, 3, 1, 1, + 0, 1, 1, 3, 0, 1, 1, 1, + 0, 0, 3, 0, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 3, 0, + 3, 0, 1, 1, 1, 1, 1, 1, + 1, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 1, 0, + 0, 0, 3, 2, 2, 1, 1, 1, + 1, 3, 0, 3, 0, 1, 1, 1, + 3, 0, 1, 1, 1, 1, 1, 1, + 3, 0, 3, 3, 3, 3, 3, 3, + 3, 3, 5, 2, 0, 0, 0, 0, + 0, 1, 3, 3, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 3, 2, 3, + 5, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 3, 3, 3, 1, 1, 1, + 1, 1, 1, 1, 3, 1, 1, 1, + 3, 3, 3, 3, 0, 3, 3, 3, + 3, 2, 1, 3, 3, 0, 3, 3, + 2, 3, 3, 0, 3, 3, 2, 1, + 1, 1, 1, 3, 2, 1, 1, 1, + 1, 3, 0, 3, 2, 1, 1, 3, + 8, 3, 4, 3, 4, 3, 8, 9, + 9, 0, 0, 0, 4, 0, 0, 0, + 3, 0, 3, 1, 0, 0, 1, 0, + 3, 1, 3, 0, 3, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 3, 1, 0, 0, 0, 1, 1, 1, + 1, 1, 1, 1, 0, 0, 0, 0, + 0, 1, 0, 3, 1, 0, 3, 1, + 0, 0, 1, 3, 1, 3, 3, 0, + 0, 0, 0, 0, 0, 0, 3, 1, + 0, 0, 0, 0, 0, 0 +}; + +static const short _zone_scanner_index_offsets[] = { + 0, 0, 40, 58, 95, 112, 127, 136, + 145, 147, 149, 152, 169, 186, 191, 240, + 260, 299, 308, 311, 322, 336, 350, 355, + 358, 361, 364, 367, 378, 389, 392, 395, + 406, 409, 412, 415, 426, 433, 436, 439, + 442, 453, 458, 461, 464, 475, 478, 481, + 484, 495, 506, 509, 512, 515, 517, 528, + 530, 541, 544, 547, 550, 553, 564, 569, + 576, 613, 616, 619, 622, 625, 628, 631, + 634, 645, 650, 653, 664, 675, 682, 684, + 695, 697, 708, 711, 722, 733, 738, 741, + 744, 747, 758, 769, 776, 779, 782, 785, + 796, 799, 810, 823, 826, 838, 851, 854, + 857, 860, 863, 874, 877, 880, 891, 898, + 909, 912, 915, 918, 929, 940, 949, 952, + 963, 966, 977, 980, 991, 994, 997, 1000, + 1011, 1018, 1021, 1024, 1035, 1038, 1049, 1052, + 1055, 1057, 1069, 1072, 1075, 1086, 1089, 1129, + 1134, 1141, 1179, 1197, 1205, 1217, 1225, 1228, + 1230, 1237, 1240, 1243, 1246, 1249, 1252, 1255, + 1260, 1267, 1305, 1308, 1326, 1364, 1367, 1375, + 1387, 1395, 1398, 1415, 1462, 1481, 1498, 1538, + 1543, 1555, 1567, 1616, 1633, 1648, 1659, 1670, + 1683, 1696, 1711, 1724, 1739, 1750, 1765, 1782, + 1797, 1808, 1813, 1828, 1831, 1834, 1837, 1840, + 1843, 1848, 1851, 1854, 1859, 1868, 1888, 1928, + 1933, 1952, 2001, 2018, 2033, 2044, 2055, 2068, + 2081, 2096, 2109, 2124, 2135, 2150, 2167, 2182, + 2193, 2206, 2246, 2260, 2274, 2289, 2304, 2307, + 2310, 2321, 2324, 2327, 2330, 2341, 2344, 2355, + 2360, 2372, 2384, 2399, 2414, 2429, 2444, 2447, + 2496, 2510, 2524, 2539, 2552, 2561, 2570, 2581, + 2592, 2605, 2616, 2629, 2638, 2651, 2666, 2679, + 2688, 2693, 2698, 2709, 2714, 2716, 2718, 2727, + 2739, 2751, 2753, 2755, 2757, 2769, 2774, 2779, + 2784, 2793, 2797, 2799, 2801, 2810, 2816, 2821, + 2825, 2827, 2829, 2835, 2842, 2850, 2870, 2879, + 2881, 2891, 2903, 2913, 2916, 2923, 2937, 2945, + 2961, 2970, 2972, 2974, 2976, 2978, 2986, 2995, + 2998, 3005, 3015, 3024, 3040, 3048, 3064, 3073, + 3075, 3077, 3079, 3081, 3089, 3098, 3103, 3117, + 3120, 3123, 3128, 3133, 3139, 3145, 3160, 3163, + 3165, 3176, 3217, 3234, 3237, 3240, 3251, 3254, + 3263, 3266, 3277, 3282, 3285, 3288, 3291, 3294, + 3305, 3316, 3319, 3322, 3333, 3336, 3339, 3342, + 3353, 3360, 3363, 3366, 3369, 3380, 3385, 3388, + 3391, 3402, 3405, 3408, 3411, 3422, 3433, 3436, + 3439, 3442, 3444, 3455, 3457, 3468, 3471, 3474, + 3477, 3480, 3491, 3494, 3497, 3500, 3503, 3506, + 3509, 3512, 3523, 3528, 3531, 3542, 3553, 3560, + 3562, 3573, 3575, 3586, 3589, 3600, 3611, 3616, + 3619, 3622, 3625, 3636, 3647, 3654, 3657, 3660, + 3663, 3674, 3677, 3688, 3701, 3704, 3716, 3729, + 3732, 3735, 3738, 3741, 3752, 3755, 3758, 3769, + 3776, 3787, 3790, 3793, 3796, 3807, 3818, 3827, + 3830, 3841, 3844, 3855, 3858, 3869, 3872, 3875, + 3878, 3889, 3896, 3899, 3902, 3913, 3916, 3927, + 3930, 3933, 3935, 3947, 3950, 3953, 3964, 3967, + 3970, 3973, 3984, 3987, 3998, 4005, 4013, 4021, + 4031, 4035, 4049, 4052, 4055, 4058, 4065, 4074, + 4084, 4094, 4098, 4112, 4115, 4118, 4126, 4129, + 4141, 4147, 4152, 4157, 4160, 4166, 4169, 4172, + 4175, 4178, 4180, 4182, 4185, 4188, 4191, 4193, + 4198, 4203, 4208, 4210, 4213, 4216, 4219, 4222, + 4227, 4230, 4233, 4236, 4239, 4241, 4243, 4246, + 4249, 4252, 4254, 4256, 4258, 4263, 4265, 4267, + 4270, 4273, 4276, 4278, 4280, 4282, 4287, 4290, + 4292, 4294, 4296, 4298, 4303, 4305, 4307, 4312, + 4315, 4318, 4321, 4324, 4327, 4330, 4333, 4338, + 4341, 4344, 4347, 4350, 4353, 4356, 4361, 4364, + 4367, 4372, 4375, 4378, 4383, 4386, 4389, 4394, + 4397, 4399, 4404, 4407, 4410, 4414, 4420, 4423, + 4426, 4429, 4432, 4434, 4436, 4439, 4442, 4445, + 4447, 4452, 4454, 4456, 4461, 4463, 4465, 4470, + 4484, 4490, 4493, 4496, 4499, 4502, 4505, 4510, + 4517, 4520, 4523, 4526, 4529, 4532, 4537, 4542, + 4545, 4550, 4553, 4556, 4561, 4564, 4567, 4570, + 4575, 4578, 4581, 4586, 4591, 4594, 4599, 4602, + 4605, 4610, 4613, 4616, 4619, 4624, 4627, 4630, + 4635, 4638, 4645, 4652, 4657, 4664, 4671, 4684, + 4691, 4699, 4707, 4715, 4733, 4741, 4759, 4767, + 4785, 4793, 4809, 4815, 4827, 4833, 4836, 4844, + 4856, 4864, 4867, 4875, 4887, 4895, 4898, 4906, + 4918, 4926, 4929, 4932, 4935, 4940, 4947, 4957, + 4962, 4965, 4972, 4979, 4992, 4997, 5000, 5002, + 5010, 5023, 5028, 5031, 5036, 5047, 5061, 5064, + 5069, 5078, 5080, 5088, 5098, 5106, 5116, 5125, + 5134, 5141, 5149, 5157, 5167, 5175, 5185, 5194, + 5203, 5210, 5219, 5221, 5235, 5247, 5261, 5273, + 5287, 5299, 5313, 5324, 5327, 5340, 5353, 5356, + 5369, 5382, 5393, 5396, 5409, 5422, 5433, 5436, + 5449, 5462, 5473, 5476, 5483, 5486, 5494, 5502, + 5505, 5508, 5511, 5518, 5521, 5529, 5537, 5540, + 5543, 5545, 5553, 5561, 5569, 5577, 5585, 5598, + 5603, 5606, 5609, 5612, 5614, 5622, 5630, 5638, + 5648, 5655, 5665, 5672, 5682, 5689, 5702, 5707, + 5710, 5713, 5716, 5719, 5722, 5726, 5733, 5741, + 5749, 5759, 5766, 5777, 5780, 5783, 5786, 5789, + 5803, 5806, 5808, 5811, 5814, 5816, 5828, 5831, + 5833, 5838, 5843, 5845, 5853, 5863, 5870, 5878, + 5886, 5896, 5900, 5914, 5917, 5920, 5923, 5926, + 5928, 5936, 5944, 5952, 5962, 5966, 5980, 5983, + 5986, 5989, 5991, 5999, 6010, 6017, 6025, 6033, + 6041, 6076, 6087, 6090, 6093, 6108, 6111, 6150, + 6157, 6160, 6163, 6170, 6178, 6186, 6195, 6232, + 6235, 6238, 6245, 6253, 6261, 6272, 6311, 6314, + 6317, 6324, 6332, 6340, 6353, 6388, 6391, 6394, + 6397, 6428, 6441, 6451, 6458, 6466, 6474, 6482, + 6490, 6498, 6506, 6514, 6522, 6530, 6538, 6551, + 6558, 6569, 6572, 6575, 6578, 6581, 6584, 6587, + 6590, 6593, 6596, 6599, 6602, 6609, 6612, 6615, + 6618, 6625, 6628, 6635, 6644, 6647, 6654, 6659, + 6662, 6665, 6668, 6671, 6678, 6685, 6688, 6691, + 6698, 6701, 6704, 6707, 6714, 6721, 6724, 6727, + 6730, 6737, 6742, 6745, 6748, 6755, 6758, 6761, + 6764, 6771, 6778, 6781, 6784, 6787, 6789, 6796, + 6798, 6805, 6808, 6811, 6814, 6817, 6824, 6827, + 6830, 6833, 6836, 6839, 6842, 6845, 6852, 6857, + 6860, 6867, 6874, 6881, 6883, 6890, 6892, 6899, + 6902, 6909, 6916, 6921, 6924, 6927, 6930, 6937, + 6944, 6951, 6954, 6957, 6960, 6967, 6970, 6977, + 6986, 6989, 6997, 7006, 7009, 7012, 7015, 7018, + 7025, 7028, 7031, 7038, 7045, 7052, 7055, 7058, + 7061, 7068, 7075, 7084, 7087, 7094, 7097, 7104, + 7107, 7114, 7117, 7120, 7123, 7130, 7137, 7140, + 7143, 7150, 7153, 7160, 7163, 7166, 7168, 7176, + 7179, 7182, 7189, 7196, 7201, 7206, 7208, 7216, + 7224, 7232, 7242, 7249, 7260, 7263, 7266, 7269, + 7272, 7277, 7280, 7282, 7290, 7298, 7306, 7314, + 7322, 7333, 7340, 7350, 7354, 7359, 7363, 7368, + 7373, 7377, 7382, 7390, 7395, 7397, 7399, 7401, + 7403, 7405, 7408, 7412, 7422, 7425, 7428, 7431, + 7433, 7441, 7449, 7457, 7465, 7473, 7484, 7489, + 7493, 7501, 7504, 7507, 7510, 7512, 7520, 7528, + 7536, 7544, 7552, 7562, 7566, 7580, 7583, 7586, + 7589, 7592, 7594, 7602, 7611, 7618, 7621, 7623, + 7631, 7641, 7645, 7649, 7653, 7655, 7659, 7663, + 7667, 7671, 7677, 7680, 7684, 7688, 7690, 7694, + 7698, 7704, 7708, 7712, 7714, 7718, 7722, 7728, + 7730, 7738, 7746, 7754, 7764, 7769, 7772, 7775, + 7777, 7785, 7795, 7802, 7812, 7817, 7820, 7823, + 7839, 7865, 7911, 7958, 7974, 8021, 8037, 8063, + 8120, 8177, 8178, 8179, 8180, 8189, 8190, 8191, + 8192, 8206, 8207, 8222, 8225, 8226, 8267, 8270, + 8271, 8285, 8288, 8298, 8299, 8313, 8316, 8319, + 8320, 8321, 8322, 8323, 8324, 8325, 8326, 8327, + 8328, 8342, 8345, 8346, 8347, 8358, 8361, 8373, + 8376, 8388, 8391, 8403, 8406, 8407, 8408, 8409, + 8410, 8424, 8427, 8428, 8442, 8445, 8446, 8460, + 8463, 8464, 8479, 8482, 8493, 8496, 8535, 8542, + 8543, 8544, 8545, 8546, 8547, 8548, 8549, 8563, + 8566, 8567, 8568, 8569, 8570, 8571 +}; + +static const short _zone_scanner_indicies[] = { + 1, 1, 2, 3, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 4, 0, + 25, 25, 26, 27, 29, 30, 31, 32, + 33, 29, 30, 31, 32, 33, 34, 35, + 28, 24, 37, 37, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 40, 41, 42, + 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 56, 36, 58, + 58, 59, 60, 61, 62, 63, 61, 62, + 63, 64, 65, 66, 66, 67, 68, 57, + 70, 70, 72, 73, 74, 75, 76, 69, + 69, 77, 78, 69, 69, 69, 71, 80, + 80, 81, 82, 83, 84, 83, 84, 79, + 86, 86, 87, 88, 89, 90, 89, 90, + 85, 92, 91, 94, 93, 96, 95, 71, + 97, 97, 98, 99, 74, 83, 84, 83, + 100, 101, 102, 83, 103, 69, 69, 69, + 79, 104, 104, 105, 106, 74, 89, 90, + 89, 107, 108, 109, 89, 110, 69, 69, + 69, 79, 92, 112, 91, 111, 79, 113, + 113, 115, 116, 69, 118, 119, 120, 121, + 122, 123, 124, 125, 126, 127, 128, 129, + 130, 131, 132, 74, 118, 119, 120, 121, + 122, 123, 124, 125, 126, 127, 128, 129, + 130, 131, 132, 20, 21, 133, 134, 135, + 136, 137, 138, 69, 69, 117, 69, 114, + 140, 140, 141, 142, 29, 30, 31, 32, + 33, 29, 30, 31, 32, 33, 83, 84, + 143, 144, 28, 139, 146, 146, 147, 148, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 40, + 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 89, 90, + 149, 150, 145, 151, 152, 153, 154, 151, + 152, 153, 154, 36, 155, 155, 36, 156, + 156, 157, 158, 159, 160, 161, 161, 162, + 163, 57, 80, 83, 80, 81, 82, 84, + 164, 83, 164, 165, 166, 167, 95, 79, + 86, 89, 86, 87, 88, 90, 168, 89, + 168, 169, 170, 171, 95, 79, 172, 173, + 172, 173, 36, 174, 174, 36, 175, 175, + 36, 176, 176, 36, 177, 177, 36, 178, + 178, 179, 180, 181, 182, 183, 183, 184, + 185, 57, 186, 186, 187, 188, 189, 190, + 191, 191, 192, 193, 57, 194, 194, 36, + 195, 195, 36, 196, 196, 197, 198, 199, + 200, 201, 201, 202, 203, 57, 204, 204, + 36, 205, 205, 36, 206, 206, 36, 207, + 207, 208, 209, 210, 211, 212, 212, 213, + 214, 57, 215, 216, 217, 215, 216, 217, + 36, 218, 218, 36, 219, 219, 36, 220, + 220, 36, 221, 221, 222, 223, 224, 225, + 226, 226, 227, 228, 57, 229, 230, 229, + 230, 36, 231, 231, 36, 232, 232, 36, + 233, 233, 234, 235, 236, 237, 238, 238, + 239, 240, 57, 241, 241, 36, 242, 242, + 36, 243, 243, 36, 244, 244, 245, 246, + 247, 248, 249, 249, 250, 251, 57, 252, + 252, 253, 254, 255, 256, 257, 257, 258, + 259, 57, 260, 260, 36, 261, 261, 36, + 262, 263, 36, 264, 36, 265, 265, 266, + 267, 268, 269, 270, 270, 271, 272, 57, + 273, 36, 274, 274, 275, 276, 277, 278, + 279, 279, 280, 281, 57, 282, 282, 36, + 283, 283, 36, 284, 284, 36, 285, 285, + 36, 286, 286, 287, 288, 289, 290, 291, + 291, 292, 293, 57, 294, 295, 294, 295, + 36, 296, 296, 297, 298, 299, 300, 36, + 301, 301, 302, 303, 304, 305, 306, 307, + 308, 309, 310, 311, 312, 313, 314, 315, + 316, 317, 318, 304, 305, 306, 307, 308, + 309, 310, 311, 312, 313, 314, 315, 316, + 317, 318, 319, 320, 36, 295, 295, 36, + 321, 321, 36, 322, 322, 36, 323, 323, + 36, 324, 324, 36, 325, 325, 36, 326, + 326, 36, 327, 327, 328, 329, 330, 331, + 332, 332, 333, 334, 57, 335, 336, 335, + 336, 36, 337, 337, 36, 338, 338, 339, + 340, 341, 342, 343, 343, 344, 345, 57, + 346, 346, 347, 348, 349, 350, 351, 351, + 352, 353, 57, 354, 355, 356, 357, 356, + 357, 36, 358, 36, 359, 359, 360, 361, + 362, 363, 364, 364, 365, 366, 57, 367, + 36, 368, 368, 369, 370, 371, 372, 373, + 373, 374, 375, 57, 376, 376, 36, 377, + 377, 378, 379, 380, 381, 382, 382, 383, + 384, 57, 385, 385, 386, 387, 388, 389, + 390, 390, 391, 392, 57, 393, 394, 393, + 394, 36, 395, 395, 36, 396, 396, 36, + 397, 397, 36, 398, 398, 399, 400, 401, + 402, 403, 403, 404, 405, 57, 406, 406, + 407, 408, 409, 410, 411, 411, 412, 413, + 57, 414, 415, 416, 414, 415, 416, 36, + 417, 417, 36, 418, 418, 36, 419, 419, + 36, 420, 420, 421, 422, 423, 424, 425, + 425, 426, 427, 57, 428, 428, 36, 429, + 429, 430, 431, 432, 433, 434, 434, 435, + 436, 57, 437, 437, 438, 439, 440, 440, + 441, 442, 443, 443, 444, 445, 57, 446, + 446, 36, 447, 447, 448, 449, 450, 451, + 452, 453, 453, 454, 455, 57, 456, 456, + 457, 458, 459, 459, 460, 461, 462, 462, + 463, 464, 57, 465, 465, 36, 466, 466, + 36, 467, 467, 36, 468, 468, 36, 469, + 469, 470, 471, 472, 473, 474, 474, 475, + 476, 57, 477, 477, 36, 478, 478, 36, + 479, 479, 480, 481, 482, 483, 484, 484, + 485, 486, 57, 487, 488, 489, 487, 488, + 489, 36, 490, 490, 491, 492, 493, 494, + 495, 495, 496, 497, 57, 498, 498, 36, + 499, 499, 36, 500, 500, 36, 501, 501, + 502, 503, 504, 505, 506, 506, 507, 508, + 57, 509, 509, 510, 511, 512, 513, 514, + 514, 515, 516, 57, 517, 518, 519, 520, + 517, 518, 519, 520, 36, 521, 521, 36, + 522, 522, 523, 524, 525, 526, 527, 527, + 528, 529, 57, 530, 530, 36, 531, 531, + 532, 533, 534, 535, 536, 536, 537, 538, + 57, 539, 539, 36, 540, 540, 541, 542, + 543, 544, 545, 545, 546, 547, 57, 548, + 548, 36, 549, 549, 36, 550, 550, 36, + 551, 551, 552, 553, 554, 555, 556, 556, + 557, 558, 57, 559, 560, 561, 559, 560, + 561, 36, 562, 562, 36, 563, 563, 36, + 564, 564, 565, 566, 567, 568, 569, 569, + 570, 571, 57, 572, 572, 36, 573, 573, + 574, 575, 576, 577, 578, 578, 579, 580, + 57, 581, 581, 36, 582, 582, 36, 584, + 583, 586, 586, 587, 588, 590, 591, 592, + 592, 593, 594, 589, 585, 595, 595, 36, + 596, 596, 36, 597, 597, 598, 599, 600, + 601, 602, 602, 603, 604, 57, 606, 605, + 36, 607, 607, 608, 609, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 610, 611, 4, + 0, 612, 295, 612, 295, 36, 613, 613, + 614, 615, 616, 617, 36, 618, 618, 619, + 620, 622, 623, 624, 625, 626, 627, 628, + 629, 630, 631, 632, 633, 634, 635, 636, + 622, 623, 624, 625, 626, 627, 628, 629, + 630, 631, 632, 633, 634, 635, 636, 637, + 638, 621, 583, 639, 639, 640, 641, 643, + 644, 645, 646, 647, 643, 644, 645, 646, + 647, 648, 649, 642, 24, 639, 639, 640, + 641, 648, 649, 650, 24, 652, 653, 654, + 655, 656, 652, 653, 654, 655, 656, 651, + 24, 657, 657, 658, 659, 661, 662, 660, + 24, 664, 663, 36, 666, 665, 668, 669, + 670, 668, 669, 670, 667, 671, 671, 667, + 672, 672, 667, 673, 673, 667, 674, 674, + 667, 675, 675, 667, 676, 676, 667, 677, + 677, 677, 677, 667, 679, 679, 680, 681, + 682, 683, 678, 684, 684, 685, 686, 5, + 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 687, 688, 4, + 583, 691, 689, 690, 693, 693, 694, 695, + 29, 30, 31, 32, 33, 29, 30, 31, + 32, 33, 696, 697, 28, 692, 698, 698, + 699, 700, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, + 19, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 16, 17, 18, 19, + 701, 702, 4, 583, 704, 703, 36, 25, + 25, 26, 27, 34, 35, 705, 24, 707, + 708, 709, 710, 711, 707, 708, 709, 710, + 711, 706, 24, 712, 712, 713, 714, 716, + 717, 715, 24, 719, 718, 36, 721, 721, + 722, 723, 61, 62, 63, 61, 62, 63, + 724, 725, 66, 66, 726, 727, 720, 728, + 728, 729, 730, 69, 118, 119, 120, 121, + 122, 123, 124, 125, 126, 127, 128, 129, + 130, 131, 132, 74, 118, 119, 120, 121, + 122, 123, 124, 125, 126, 127, 128, 129, + 130, 131, 132, 731, 732, 69, 69, 733, + 734, 69, 69, 117, 69, 585, 736, 736, + 737, 738, 61, 62, 63, 61, 62, 63, + 83, 84, 739, 740, 741, 742, 743, 744, + 735, 745, 745, 746, 747, 74, 89, 90, + 748, 107, 108, 109, 749, 110, 69, 69, + 69, 79, 750, 750, 751, 752, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 89, 90, 753, 754, + 4, 0, 92, 756, 91, 755, 85, 757, + 757, 758, 759, 760, 761, 760, 761, 91, + 91, 91, 79, 762, 762, 763, 764, 92, + 765, 92, 765, 91, 91, 91, 85, 766, + 766, 767, 768, 69, 118, 119, 120, 121, + 122, 123, 124, 125, 126, 127, 128, 129, + 130, 131, 132, 74, 118, 119, 120, 121, + 122, 123, 124, 125, 126, 127, 128, 129, + 130, 131, 132, 89, 90, 753, 769, 108, + 109, 753, 770, 69, 69, 117, 69, 114, + 80, 80, 81, 82, 151, 152, 153, 154, + 151, 152, 153, 154, 83, 84, 83, 84, + 735, 80, 80, 81, 82, 215, 216, 217, + 215, 216, 217, 83, 84, 83, 84, 735, + 80, 80, 81, 82, 260, 260, 83, 84, + 83, 84, 735, 80, 80, 81, 82, 282, + 282, 83, 84, 83, 84, 735, 80, 80, + 81, 82, 612, 295, 612, 295, 83, 84, + 83, 84, 735, 80, 80, 81, 82, 335, + 336, 335, 336, 83, 84, 83, 84, 735, + 80, 80, 81, 82, 354, 355, 356, 357, + 356, 357, 83, 84, 83, 84, 735, 80, + 80, 81, 82, 393, 394, 393, 394, 83, + 84, 83, 84, 735, 80, 80, 81, 82, + 414, 415, 416, 414, 415, 416, 83, 84, + 83, 84, 735, 80, 80, 81, 82, 477, + 477, 83, 84, 83, 84, 735, 80, 80, + 81, 82, 487, 488, 489, 487, 488, 489, + 83, 84, 83, 84, 735, 80, 80, 81, + 82, 517, 518, 519, 520, 517, 518, 519, + 520, 83, 84, 83, 84, 735, 80, 80, + 81, 82, 559, 560, 561, 559, 560, 561, + 83, 84, 83, 84, 735, 80, 80, 81, + 82, 595, 595, 83, 84, 83, 84, 735, + 92, 772, 91, 771, 79, 80, 80, 81, + 82, 668, 669, 670, 668, 669, 670, 83, + 84, 83, 84, 773, 774, 774, 667, 775, + 775, 667, 776, 776, 667, 777, 777, 667, + 778, 778, 667, 779, 779, 779, 779, 667, + 780, 780, 667, 781, 781, 667, 782, 782, + 782, 782, 667, 784, 784, 785, 786, 83, + 84, 787, 788, 783, 790, 790, 791, 792, + 29, 30, 31, 32, 33, 29, 30, 31, + 32, 33, 83, 84, 793, 794, 28, 789, + 795, 795, 796, 797, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, + 18, 19, 89, 90, 798, 799, 4, 0, + 92, 801, 91, 800, 145, 803, 803, 804, + 805, 61, 62, 63, 61, 62, 63, 83, + 84, 806, 807, 741, 742, 808, 809, 802, + 810, 810, 811, 812, 69, 118, 119, 120, + 121, 122, 123, 124, 125, 126, 127, 128, + 129, 130, 131, 132, 74, 118, 119, 120, + 121, 122, 123, 124, 125, 126, 127, 128, + 129, 130, 131, 132, 89, 90, 813, 769, + 108, 109, 814, 770, 69, 69, 117, 69, + 114, 784, 784, 785, 786, 151, 152, 153, + 154, 151, 152, 153, 154, 83, 84, 787, + 788, 802, 784, 784, 785, 786, 215, 216, + 217, 215, 216, 217, 83, 84, 787, 788, + 802, 784, 784, 785, 786, 260, 260, 83, + 84, 787, 788, 802, 784, 784, 785, 786, + 282, 282, 83, 84, 787, 788, 802, 784, + 784, 785, 786, 612, 295, 612, 295, 83, + 84, 787, 788, 802, 784, 784, 785, 786, + 335, 336, 335, 336, 83, 84, 787, 788, + 802, 784, 784, 785, 786, 354, 355, 356, + 357, 356, 357, 83, 84, 787, 788, 802, + 784, 784, 785, 786, 393, 394, 393, 394, + 83, 84, 787, 788, 802, 784, 784, 785, + 786, 414, 415, 416, 414, 415, 416, 83, + 84, 787, 788, 802, 784, 784, 785, 786, + 477, 477, 83, 84, 787, 788, 802, 784, + 784, 785, 786, 487, 488, 489, 487, 488, + 489, 83, 84, 787, 788, 802, 784, 784, + 785, 786, 517, 518, 519, 520, 517, 518, + 519, 520, 83, 84, 787, 788, 802, 784, + 784, 785, 786, 559, 560, 561, 559, 560, + 561, 83, 84, 787, 788, 802, 784, 784, + 785, 786, 595, 595, 83, 84, 787, 788, + 802, 815, 815, 94, 817, 818, 93, 93, + 819, 820, 93, 93, 93, 816, 821, 821, + 822, 823, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, + 19, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 16, 17, 18, 19, + 83, 84, 824, 825, 4, 114, 80, 83, + 80, 81, 82, 84, 826, 827, 826, 828, + 829, 830, 689, 79, 86, 89, 86, 87, + 88, 90, 831, 756, 831, 832, 833, 834, + 689, 85, 757, 760, 757, 758, 759, 761, + 835, 827, 835, 836, 837, 838, 91, 771, + 79, 762, 92, 762, 763, 764, 765, 839, + 756, 839, 840, 841, 842, 91, 771, 79, + 843, 843, 36, 844, 844, 36, 845, 845, + 846, 847, 848, 849, 850, 850, 851, 852, + 57, 853, 853, 36, 854, 854, 36, 855, + 855, 36, 856, 856, 857, 858, 859, 860, + 861, 861, 862, 863, 57, 864, 864, 36, + 865, 865, 866, 867, 868, 869, 870, 870, + 871, 872, 57, 666, 874, 665, 873, 79, + 875, 875, 876, 877, 878, 761, 878, 761, + 665, 665, 665, 79, 879, 879, 880, 881, + 882, 765, 882, 765, 665, 665, 665, 85, + 875, 878, 875, 876, 877, 761, 883, 827, + 883, 884, 885, 838, 665, 873, 79, 879, + 882, 879, 880, 881, 765, 886, 756, 886, + 887, 888, 842, 665, 873, 79, 757, 760, + 757, 758, 759, 761, 889, 760, 889, 890, + 891, 167, 91, 111, 79, 762, 92, 762, + 763, 764, 765, 892, 92, 892, 893, 894, + 171, 91, 111, 79, 896, 895, 71, 897, + 897, 898, 899, 69, 118, 119, 120, 121, + 122, 123, 124, 125, 126, 127, 128, 129, + 130, 131, 132, 74, 118, 119, 120, 121, + 122, 123, 124, 125, 126, 127, 128, 129, + 130, 131, 132, 83, 84, 824, 900, 101, + 102, 824, 901, 69, 69, 117, 69, 114, + 80, 83, 80, 81, 82, 84, 902, 827, + 902, 903, 904, 838, 895, 79, 86, 89, + 86, 87, 88, 90, 905, 756, 905, 906, + 907, 842, 895, 79, 679, 679, 680, 681, + 151, 152, 153, 154, 151, 152, 153, 154, + 682, 683, 908, 679, 679, 680, 681, 215, + 216, 217, 215, 216, 217, 682, 683, 908, + 679, 679, 680, 681, 260, 260, 682, 683, + 908, 679, 679, 680, 681, 282, 282, 682, + 683, 908, 679, 679, 680, 681, 612, 295, + 612, 295, 682, 683, 908, 679, 679, 680, + 681, 335, 336, 335, 336, 682, 683, 908, + 679, 679, 680, 681, 354, 355, 356, 357, + 356, 357, 682, 683, 908, 679, 679, 680, + 681, 393, 394, 393, 394, 682, 683, 908, + 679, 679, 680, 681, 414, 415, 416, 414, + 415, 416, 682, 683, 908, 679, 679, 680, + 681, 477, 477, 682, 683, 908, 679, 679, + 680, 681, 487, 488, 489, 487, 488, 489, + 682, 683, 908, 679, 679, 680, 681, 517, + 518, 519, 520, 517, 518, 519, 520, 682, + 683, 908, 679, 679, 680, 681, 559, 560, + 561, 559, 560, 561, 682, 683, 908, 679, + 679, 680, 681, 595, 595, 682, 683, 908, + 666, 910, 665, 909, 85, 666, 912, 665, + 911, 145, 679, 679, 680, 681, 294, 295, + 294, 295, 682, 683, 908, 92, 914, 91, + 913, 145, 916, 915, 918, 917, 920, 921, + 922, 923, 920, 920, 920, 920, 919, 924, + 925, 926, 924, 927, 925, 924, 924, 925, + 925, 925, 919, 928, 929, 929, 928, 930, + 929, 928, 928, 929, 929, 929, 919, 931, + 925, 933, 932, 934, 932, 935, 936, 937, + 935, 938, 936, 935, 935, 936, 936, 936, + 932, 928, 928, 928, 928, 919, 939, 939, + 939, 939, 919, 942, 943, 941, 941, 940, + 944, 941, 944, 943, 944, 941, 945, 941, + 940, 941, 947, 941, 946, 948, 946, 949, + 946, 950, 951, 950, 953, 950, 951, 952, + 951, 946, 954, 955, 956, 954, 954, 940, + 944, 944, 944, 944, 957, 954, 958, 954, + 946, 959, 946, 960, 946, 961, 962, 963, + 961, 961, 946, 965, 965, 966, 967, 968, + 969, 964, 965, 965, 966, 967, 968, 969, + 971, 970, 973, 973, 974, 975, 977, 978, + 979, 980, 981, 977, 978, 979, 980, 981, + 982, 983, 982, 983, 976, 972, 985, 985, + 986, 987, 988, 989, 988, 989, 984, 991, + 990, 973, 973, 974, 975, 982, 983, 982, + 983, 992, 972, 995, 996, 997, 998, 999, + 995, 996, 997, 998, 999, 994, 993, 1000, + 1000, 1001, 1002, 1004, 1005, 1004, 1005, 1003, + 972, 1007, 1006, 964, 1009, 1009, 1010, 1011, + 1012, 1013, 1008, 1009, 1009, 1010, 1011, 1014, + 1015, 1016, 1014, 1012, 1013, 1014, 1014, 1014, + 1008, 1017, 1018, 1019, 1017, 1017, 1017, 1017, + 1008, 1021, 1021, 1022, 1023, 1024, 1024, 1025, + 1024, 1026, 1027, 1026, 1027, 1024, 1024, 1024, + 1020, 1028, 1028, 1029, 1030, 1031, 1032, 1031, + 1032, 1020, 1034, 1033, 1035, 1017, 1037, 1036, + 1038, 1036, 1039, 1040, 1041, 1039, 1039, 1039, + 1039, 1036, 1021, 1021, 1022, 1023, 1026, 1027, + 1026, 1027, 1020, 1043, 1042, 1008, 1044, 1044, + 1045, 1046, 1047, 1048, 690, 1044, 1044, 1045, + 1046, 1047, 1048, 1049, 1049, 1049, 1050, 1052, + 1052, 1053, 1054, 1055, 1056, 1055, 1057, 1051, + 1059, 1059, 1060, 1061, 1062, 1063, 1064, 1062, + 1065, 1066, 1065, 1067, 1062, 1062, 1062, 1058, + 1069, 1070, 1071, 1069, 1069, 1069, 1069, 1068, + 1072, 1072, 1073, 1074, 1075, 1075, 1076, 1075, + 1077, 1078, 1077, 1078, 1075, 1075, 1075, 1058, + 1079, 1079, 1080, 1081, 1065, 1066, 1065, 1066, + 85, 1083, 1082, 1084, 1069, 1086, 1085, 1087, + 1085, 1088, 1089, 1090, 1088, 1088, 1088, 1088, + 1085, 1072, 1072, 1073, 1074, 1077, 1078, 1077, + 1078, 1058, 1083, 1092, 1082, 1091, 85, 1093, + 1093, 1094, 1095, 1062, 1063, 1064, 1062, 1096, + 1097, 1062, 1062, 1062, 1068, 1099, 1098, 690, + 1101, 1100, 690, 1103, 1103, 1103, 1103, 1102, + 1104, 1104, 1104, 1104, 1102, 1105, 1106, 1105, + 1105, 1105, 1102, 1107, 1108, 1107, 1107, 1107, + 1102, 1108, 1108, 1109, 1110, 1103, 1111, 1112, + 1113, 1113, 1114, 1115, 1103, 1103, 1103, 1102, + 1117, 1116, 1102, 1108, 1102, 1119, 1119, 1120, + 1121, 1122, 1123, 1124, 1124, 1125, 1126, 1118, + 1127, 1127, 1128, 1129, 1130, 1131, 1132, 1133, + 1134, 1135, 1136, 1137, 1138, 1139, 1140, 1141, + 1142, 1143, 1144, 1130, 1131, 1132, 1133, 1134, + 1135, 1136, 1137, 1138, 1139, 1140, 1141, 1142, + 1143, 1144, 1145, 1146, 1147, 1147, 1148, 1149, + 1118, 1150, 1150, 1151, 1152, 1153, 1154, 1155, + 1153, 1154, 1155, 1156, 1157, 1158, 1158, 1159, + 1160, 1118, 1161, 1161, 1118, 1162, 1162, 1118, + 1163, 1163, 1164, 1165, 1166, 1167, 1168, 1168, + 1169, 1170, 1118, 1172, 1171, 1118, 1173, 1174, + 1175, 1176, 1173, 1174, 1175, 1176, 1118, 1177, + 1177, 1118, 1178, 1178, 1179, 1180, 1181, 1182, + 1183, 1183, 1184, 1185, 1118, 1186, 1187, 1186, + 1187, 1118, 1188, 1188, 1118, 1189, 1189, 1118, + 1190, 1190, 1118, 1191, 1191, 1118, 1192, 1192, + 1193, 1194, 1195, 1196, 1197, 1197, 1198, 1199, + 1118, 1200, 1200, 1201, 1202, 1203, 1204, 1205, + 1205, 1206, 1207, 1118, 1208, 1208, 1118, 1209, + 1209, 1118, 1210, 1210, 1211, 1212, 1213, 1214, + 1215, 1215, 1216, 1217, 1118, 1218, 1218, 1118, + 1219, 1219, 1118, 1220, 1220, 1118, 1221, 1221, + 1222, 1223, 1224, 1225, 1226, 1226, 1227, 1228, + 1118, 1229, 1230, 1231, 1229, 1230, 1231, 1118, + 1232, 1232, 1118, 1233, 1233, 1118, 1234, 1234, + 1118, 1235, 1235, 1236, 1237, 1238, 1239, 1240, + 1240, 1241, 1242, 1118, 1243, 1244, 1243, 1244, + 1118, 1245, 1245, 1118, 1246, 1246, 1118, 1247, + 1247, 1248, 1249, 1250, 1251, 1252, 1252, 1253, + 1254, 1118, 1255, 1255, 1118, 1256, 1256, 1118, + 1257, 1257, 1118, 1258, 1258, 1259, 1260, 1261, + 1262, 1263, 1263, 1264, 1265, 1118, 1266, 1266, + 1267, 1268, 1269, 1270, 1271, 1271, 1272, 1273, + 1118, 1274, 1274, 1118, 1275, 1275, 1118, 1276, + 1277, 1118, 1278, 1118, 1279, 1279, 1280, 1281, + 1282, 1283, 1284, 1284, 1285, 1286, 1118, 1287, + 1118, 1288, 1288, 1289, 1290, 1291, 1292, 1293, + 1293, 1294, 1295, 1118, 1296, 1296, 1118, 1297, + 1297, 1118, 1298, 1298, 1118, 1299, 1299, 1118, + 1300, 1300, 1301, 1302, 1303, 1304, 1305, 1305, + 1306, 1307, 1118, 1308, 1308, 1118, 1309, 1309, + 1118, 1310, 1310, 1118, 1311, 1311, 1118, 1312, + 1312, 1118, 1313, 1313, 1118, 1314, 1314, 1118, + 1315, 1315, 1316, 1317, 1318, 1319, 1320, 1320, + 1321, 1322, 1118, 1323, 1324, 1323, 1324, 1118, + 1325, 1325, 1118, 1326, 1326, 1327, 1328, 1329, + 1330, 1331, 1331, 1332, 1333, 1118, 1334, 1334, + 1335, 1336, 1337, 1338, 1339, 1339, 1340, 1341, + 1118, 1342, 1343, 1344, 1345, 1344, 1345, 1118, + 1346, 1118, 1347, 1347, 1348, 1349, 1350, 1351, + 1352, 1352, 1353, 1354, 1118, 1355, 1118, 1356, + 1356, 1357, 1358, 1359, 1360, 1361, 1361, 1362, + 1363, 1118, 1364, 1364, 1118, 1365, 1365, 1366, + 1367, 1368, 1369, 1370, 1370, 1371, 1372, 1118, + 1373, 1373, 1374, 1375, 1376, 1377, 1378, 1378, + 1379, 1380, 1118, 1381, 1382, 1381, 1382, 1118, + 1383, 1383, 1118, 1384, 1384, 1118, 1385, 1385, + 1118, 1386, 1386, 1387, 1388, 1389, 1390, 1391, + 1391, 1392, 1393, 1118, 1394, 1394, 1395, 1396, + 1397, 1398, 1399, 1399, 1400, 1401, 1118, 1402, + 1403, 1404, 1402, 1403, 1404, 1118, 1405, 1405, + 1118, 1406, 1406, 1118, 1407, 1407, 1118, 1408, + 1408, 1409, 1410, 1411, 1412, 1413, 1413, 1414, + 1415, 1118, 1416, 1416, 1118, 1417, 1417, 1418, + 1419, 1420, 1421, 1422, 1422, 1423, 1424, 1118, + 1425, 1425, 1426, 1427, 1428, 1428, 1429, 1430, + 1431, 1431, 1432, 1433, 1118, 1434, 1434, 1118, + 1435, 1435, 1436, 1437, 1438, 1439, 1440, 1441, + 1441, 1442, 1443, 1118, 1444, 1444, 1445, 1446, + 1447, 1447, 1448, 1449, 1450, 1450, 1451, 1452, + 1118, 1453, 1453, 1118, 1454, 1454, 1118, 1455, + 1455, 1118, 1456, 1456, 1118, 1457, 1457, 1458, + 1459, 1460, 1461, 1462, 1462, 1463, 1464, 1118, + 1465, 1465, 1118, 1466, 1466, 1118, 1467, 1467, + 1468, 1469, 1470, 1471, 1472, 1472, 1473, 1474, + 1118, 1475, 1476, 1477, 1475, 1476, 1477, 1118, + 1478, 1478, 1479, 1480, 1481, 1482, 1483, 1483, + 1484, 1485, 1118, 1486, 1486, 1118, 1487, 1487, + 1118, 1488, 1488, 1118, 1489, 1489, 1490, 1491, + 1492, 1493, 1494, 1494, 1495, 1496, 1118, 1497, + 1497, 1498, 1499, 1500, 1501, 1502, 1502, 1503, + 1504, 1118, 1505, 1506, 1507, 1508, 1505, 1506, + 1507, 1508, 1118, 1509, 1509, 1118, 1510, 1510, + 1511, 1512, 1513, 1514, 1515, 1515, 1516, 1517, + 1118, 1518, 1518, 1118, 1519, 1519, 1520, 1521, + 1522, 1523, 1524, 1524, 1525, 1526, 1118, 1527, + 1527, 1118, 1528, 1528, 1529, 1530, 1531, 1532, + 1533, 1533, 1534, 1535, 1118, 1536, 1536, 1118, + 1537, 1537, 1118, 1538, 1538, 1118, 1539, 1539, + 1540, 1541, 1542, 1543, 1544, 1544, 1545, 1546, + 1118, 1547, 1548, 1549, 1547, 1548, 1549, 1118, + 1550, 1550, 1118, 1551, 1551, 1118, 1552, 1552, + 1553, 1554, 1555, 1556, 1557, 1557, 1558, 1559, + 1118, 1560, 1560, 1118, 1561, 1561, 1562, 1563, + 1564, 1565, 1566, 1566, 1567, 1568, 1118, 1569, + 1569, 1118, 1570, 1570, 1118, 1571, 1118, 1572, + 1572, 1573, 1574, 1576, 1577, 1578, 1578, 1579, + 1580, 1575, 1118, 1581, 1581, 1118, 1582, 1582, + 1118, 1583, 1583, 1584, 1585, 1586, 1587, 1588, + 1588, 1589, 1590, 1118, 1591, 1591, 1118, 1592, + 1592, 1118, 1593, 1593, 1118, 1594, 1594, 1595, + 1596, 1597, 1598, 1599, 1599, 1600, 1601, 1118, + 1602, 1602, 1118, 1603, 1603, 1604, 1605, 1606, + 1607, 1608, 1608, 1609, 1610, 1118, 1612, 1612, + 1613, 1614, 1615, 1616, 1611, 1612, 1612, 1613, + 1614, 1615, 1616, 1618, 1617, 1619, 1619, 1620, + 1621, 1623, 1624, 1622, 1617, 1626, 1626, 1627, + 1628, 1630, 1631, 1629, 1629, 1629, 1625, 1632, + 1632, 1632, 1625, 1633, 1633, 1634, 1635, 1636, + 1637, 1638, 1638, 1639, 1640, 1629, 1629, 1629, + 1625, 1642, 1641, 1625, 1644, 1643, 1611, 1646, + 1645, 1611, 1647, 1647, 1648, 1649, 1650, 1651, + 1611, 1647, 1647, 1648, 1649, 1652, 1650, 1651, + 1653, 1617, 1654, 1654, 1655, 1656, 1658, 1658, + 1659, 1660, 1657, 1617, 1661, 1661, 1662, 1663, + 1665, 1666, 1664, 1664, 1664, 1625, 1667, 1667, + 1667, 1625, 1668, 1668, 1669, 1670, 1671, 1672, + 1673, 1673, 1674, 1675, 1664, 1664, 1664, 1625, + 1677, 1676, 1625, 1679, 1678, 1611, 1680, 1680, + 1681, 1682, 1683, 1684, 1657, 1617, 1686, 1685, + 1611, 1689, 1690, 1691, 1692, 1693, 1689, 1690, + 1691, 1692, 1693, 1688, 1687, 1694, 1694, 1694, + 1694, 1695, 1687, 1696, 1697, 1696, 1697, 1687, + 1698, 1698, 1698, 1698, 1687, 1699, 1699, 1687, + 1700, 1701, 1700, 1700, 1700, 1687, 1702, 1702, + 1687, 1703, 1703, 1687, 1704, 1704, 1687, 1705, + 1705, 1687, 1706, 1687, 1707, 1687, 1708, 1708, + 1687, 1709, 1709, 1687, 1710, 1710, 1687, 1711, + 1687, 1712, 1712, 1712, 1712, 1687, 1713, 1714, + 1713, 1714, 1687, 1715, 1716, 1715, 1716, 1687, + 1717, 1687, 1718, 1718, 1687, 1719, 1719, 1687, + 1720, 1720, 1687, 1721, 1721, 1687, 1722, 1722, + 1722, 1722, 1687, 1723, 1723, 1687, 1724, 1724, + 1687, 1725, 1725, 1687, 1726, 1727, 1687, 1728, + 1687, 1729, 1687, 1730, 1730, 1687, 1731, 1731, + 1687, 1732, 1732, 1687, 1733, 1687, 1734, 1687, + 1735, 1687, 1736, 1736, 1736, 1736, 1687, 1737, + 1687, 1738, 1687, 1739, 1739, 1687, 1740, 1740, + 1687, 1741, 1741, 1687, 1742, 1687, 1743, 1687, + 1744, 1687, 1745, 1745, 1745, 1745, 1687, 1746, + 1747, 1687, 1748, 1687, 1749, 1687, 1750, 1687, + 1751, 1687, 1752, 1752, 1752, 1752, 1687, 1753, + 1687, 1754, 1687, 1755, 1755, 1755, 1755, 1687, + 1756, 1756, 1687, 1757, 1757, 1687, 1758, 1758, + 1687, 1759, 1759, 1687, 1760, 1760, 1687, 1761, + 1761, 1687, 1762, 1762, 1687, 1763, 1763, 1763, + 1763, 1687, 1764, 1764, 1687, 1765, 1765, 1687, + 1766, 1766, 1687, 1767, 1767, 1687, 1768, 1768, + 1687, 1769, 1769, 1687, 1770, 1771, 1770, 1771, + 1687, 1772, 1772, 1687, 1773, 1773, 1687, 1774, + 1774, 1774, 1774, 1687, 1775, 1775, 1687, 1776, + 1776, 1687, 1777, 1777, 1777, 1777, 1687, 1778, + 1778, 1687, 1779, 1779, 1687, 1780, 1781, 1780, + 1781, 1687, 1782, 1782, 1687, 1783, 1687, 1784, + 1784, 1784, 1784, 1687, 1785, 1785, 1687, 1786, + 1786, 1687, 1787, 1788, 1789, 1687, 1790, 1791, + 1790, 1790, 1790, 1687, 1792, 1792, 1687, 1793, + 1793, 1687, 1794, 1794, 1687, 1795, 1795, 1687, + 1796, 1687, 1797, 1687, 1798, 1798, 1687, 1799, + 1799, 1687, 1800, 1800, 1687, 1801, 1687, 1802, + 1802, 1802, 1802, 1687, 1803, 1687, 1804, 1687, + 1805, 1805, 1805, 1805, 1687, 1806, 1687, 1807, + 1687, 1808, 1808, 1808, 1808, 1687, 1811, 1812, + 1813, 1814, 1815, 1816, 1811, 1812, 1813, 1814, + 1815, 1816, 1810, 1809, 1817, 1817, 1817, 1817, + 1818, 1809, 1819, 1819, 1809, 1820, 1820, 1809, + 1821, 1821, 1809, 1822, 1822, 1809, 1823, 1823, + 1809, 1824, 1824, 1824, 1824, 1809, 1825, 1826, + 1827, 1825, 1826, 1827, 1809, 1828, 1828, 1809, + 1829, 1829, 1809, 1830, 1830, 1809, 1831, 1831, + 1809, 1832, 1832, 1809, 1833, 1833, 1833, 1833, + 1809, 1834, 1835, 1834, 1835, 1809, 1836, 1836, + 1809, 1837, 1837, 1837, 1837, 1809, 1838, 1838, + 1809, 1839, 1839, 1809, 1840, 1840, 1840, 1840, + 1809, 1841, 1841, 1809, 1842, 1842, 1809, 1843, + 1843, 1809, 1844, 1844, 1844, 1844, 1809, 1845, + 1845, 1809, 1846, 1846, 1809, 1847, 1847, 1847, + 1847, 1809, 1848, 1849, 1848, 1849, 1809, 1850, + 1850, 1809, 1851, 1851, 1851, 1851, 1809, 1852, + 1852, 1809, 1853, 1853, 1809, 1854, 1854, 1854, + 1854, 1809, 1855, 1855, 1809, 1856, 1856, 1809, + 1857, 1857, 1809, 1858, 1858, 1858, 1858, 1809, + 1859, 1859, 1809, 1860, 1860, 1809, 1861, 1861, + 1861, 1861, 1809, 1863, 1863, 1862, 1864, 1865, + 1864, 1864, 1864, 1865, 1862, 1866, 1866, 1866, + 1866, 1866, 1866, 71, 1867, 1867, 1867, 1867, + 71, 1868, 1868, 1868, 1868, 1868, 1868, 71, + 1869, 1869, 1870, 1871, 1872, 1873, 71, 1874, + 1874, 1875, 1876, 1877, 1877, 1877, 1878, 1879, + 1877, 1877, 1877, 71, 1880, 1880, 1881, 1882, + 1883, 1884, 71, 1886, 1886, 1887, 1888, 1890, + 1891, 1889, 1885, 1892, 1892, 1893, 1894, 1896, + 1897, 1895, 1885, 1898, 1898, 1899, 1900, 1902, + 1903, 1901, 1885, 1905, 1905, 1906, 1907, 1909, + 1910, 1911, 1912, 1913, 1909, 1910, 1911, 1912, + 1913, 1914, 1915, 1908, 1904, 1916, 1916, 1917, + 1918, 1920, 1921, 1919, 1885, 1922, 1922, 1923, + 1924, 1926, 1927, 1928, 1929, 1930, 1926, 1927, + 1928, 1929, 1930, 1931, 1932, 1925, 1904, 1933, + 1933, 1934, 1935, 1937, 1938, 1936, 1885, 1939, + 1939, 1940, 1941, 1943, 1944, 1945, 1946, 1947, + 1943, 1944, 1945, 1946, 1947, 1948, 1949, 1942, + 1904, 1950, 1950, 1951, 1952, 1954, 1955, 1953, + 1885, 1956, 1956, 1958, 1959, 1960, 1961, 1962, + 1958, 1959, 1960, 1961, 1962, 1956, 1956, 1957, + 1904, 1956, 1956, 1956, 1956, 1963, 1904, 1965, + 1966, 1967, 1968, 1969, 1965, 1966, 1967, 1968, + 1969, 1964, 1904, 1970, 1970, 1970, 1970, 1971, + 1904, 1973, 1972, 71, 1939, 1939, 1940, 1941, + 1948, 1949, 1974, 1904, 1976, 1977, 1978, 1979, + 1980, 1976, 1977, 1978, 1979, 1980, 1975, 1904, + 1981, 1981, 1982, 1983, 1985, 1986, 1984, 1904, + 1988, 1987, 71, 1922, 1922, 1923, 1924, 1931, + 1932, 1989, 1904, 1991, 1992, 1993, 1994, 1995, + 1991, 1992, 1993, 1994, 1995, 1990, 1904, 1996, + 1996, 1997, 1998, 2000, 2001, 1999, 1904, 2003, + 2002, 71, 1905, 1905, 1906, 1907, 1914, 1915, + 2004, 1904, 2006, 2007, 2008, 2009, 2010, 2006, + 2007, 2008, 2009, 2010, 2005, 1904, 2011, 2011, + 2012, 2013, 2015, 2016, 2014, 1904, 2018, 2017, + 71, 2020, 2019, 71, 2022, 2021, 71, 71, + 71, 71, 71, 2023, 2024, 2024, 2025, 2026, + 2027, 2028, 71, 2030, 2030, 2031, 2032, 2033, + 2034, 2029, 2029, 2029, 71, 2035, 2035, 2035, + 2035, 71, 2037, 2036, 71, 2038, 2038, 2038, + 2038, 2038, 2038, 71, 2039, 2039, 2040, 2041, + 2042, 2043, 71, 2044, 2044, 2045, 2046, 2047, + 2047, 2047, 2048, 2049, 2047, 2047, 2047, 71, + 2050, 2050, 2050, 2050, 71, 2052, 2051, 71, + 2053, 1885, 2054, 2054, 2055, 2056, 2058, 2059, + 2057, 1885, 2060, 2060, 2061, 2062, 2063, 2063, + 2063, 2064, 2065, 2063, 2063, 2063, 71, 2066, + 2066, 2066, 2066, 71, 2068, 2067, 71, 2070, + 2070, 2070, 2070, 2069, 2071, 2071, 2072, 2073, + 2074, 2075, 2076, 2076, 2077, 2078, 2070, 2080, + 2080, 2081, 2082, 2083, 2084, 2085, 2085, 2086, + 2087, 2079, 2079, 2079, 2070, 2089, 2088, 2070, + 2090, 2090, 2090, 2090, 1862, 2091, 2092, 2091, + 2091, 2091, 2092, 2092, 2092, 1862, 2094, 2093, + 2095, 2095, 2096, 2097, 2099, 2100, 2098, 2093, + 2101, 2101, 2102, 2103, 2105, 2106, 2107, 2108, + 2104, 2093, 2109, 2109, 2110, 2111, 2113, 2114, + 2112, 2093, 2115, 2115, 2116, 2117, 2105, 2106, + 2119, 2120, 2118, 2093, 2121, 2121, 2122, 2123, + 2124, 2126, 2127, 2125, 2093, 2128, 2128, 2129, + 2130, 2105, 2106, 2131, 2132, 2093, 2133, 2133, + 2134, 2135, 2136, 2137, 2093, 2133, 2133, 2134, + 2135, 2136, 2137, 2138, 2093, 2139, 2139, 2140, + 2141, 2143, 2144, 2142, 2093, 2145, 2145, 2146, + 2147, 2149, 2150, 2151, 2152, 2148, 2093, 2153, + 2153, 2154, 2155, 2157, 2158, 2156, 2093, 2159, + 2159, 2160, 2161, 2149, 2150, 2163, 2164, 2162, + 2093, 2165, 2165, 2166, 2167, 2168, 2170, 2171, + 2169, 2093, 2172, 2172, 2173, 2174, 2149, 2150, + 2175, 2176, 2093, 2177, 2177, 2178, 2179, 2180, + 2181, 2093, 2177, 2177, 2178, 2179, 2182, 2180, + 2181, 2183, 2093, 2184, 2093, 2185, 2185, 2186, + 2187, 2188, 2190, 2191, 2192, 2193, 2193, 2194, + 2195, 2189, 2093, 2196, 2196, 2197, 2198, 2200, + 2201, 2202, 2202, 2203, 2204, 2199, 2093, 2205, + 2205, 2206, 2207, 2208, 2210, 2211, 2212, 2213, + 2213, 2214, 2215, 2209, 2093, 2216, 2216, 2217, + 2218, 2220, 2221, 2202, 2202, 2222, 2223, 2219, + 2093, 2224, 2224, 2225, 2226, 2227, 2229, 2230, + 2231, 2232, 2232, 2233, 2234, 2228, 2093, 2235, + 2235, 2236, 2237, 2239, 2240, 2202, 2202, 2241, + 2242, 2238, 2093, 2243, 2243, 2244, 2245, 2246, + 2243, 2248, 2249, 2250, 2250, 2251, 2252, 2247, + 2093, 2253, 2253, 2254, 2255, 2256, 2257, 2202, + 2202, 2258, 2259, 2093, 2261, 2260, 2093, 2262, + 2262, 2263, 2264, 2262, 2266, 2267, 2268, 2268, + 2269, 2270, 2265, 2093, 2243, 2243, 2244, 2245, + 2243, 2248, 2249, 2250, 2250, 2251, 2252, 2271, + 2093, 2273, 2272, 2093, 2274, 2274, 2275, 2276, + 2278, 2279, 2280, 2281, 2281, 2282, 2283, 2277, + 2093, 2224, 2224, 2225, 2226, 2229, 2230, 2231, + 2232, 2232, 2233, 2234, 2284, 2093, 2235, 2235, + 2236, 2237, 2239, 2240, 2202, 2202, 2241, 2242, + 2093, 2286, 2285, 2093, 2287, 2287, 2288, 2289, + 2291, 2292, 2293, 2294, 2294, 2295, 2296, 2290, + 2093, 2205, 2205, 2206, 2207, 2210, 2211, 2212, + 2213, 2213, 2214, 2215, 2297, 2093, 2216, 2216, + 2217, 2218, 2220, 2221, 2202, 2202, 2222, 2223, + 2093, 2299, 2298, 2093, 2300, 2300, 2301, 2302, + 2304, 2305, 2306, 2307, 2307, 2308, 2309, 2303, + 2093, 2185, 2185, 2186, 2187, 2190, 2191, 2192, + 2193, 2193, 2194, 2195, 2310, 2093, 2196, 2196, + 2197, 2198, 2200, 2201, 2202, 2202, 2203, 2204, + 2093, 2312, 2311, 2093, 2313, 2313, 2314, 2315, + 2316, 2317, 2093, 2319, 2318, 2093, 2320, 2320, + 2321, 2322, 2324, 2325, 2323, 2093, 2165, 2165, + 2166, 2167, 2170, 2171, 2326, 2093, 2328, 2327, + 2093, 2330, 2329, 2093, 2332, 2331, 2093, 2333, + 2333, 2334, 2335, 2336, 2337, 2093, 2339, 2338, + 2093, 2340, 2340, 2341, 2342, 2344, 2345, 2343, + 2093, 2121, 2121, 2122, 2123, 2126, 2127, 2346, + 2093, 2348, 2347, 2093, 2350, 2349, 2093, 2351, + 1885, 2352, 2352, 2353, 2354, 2356, 2357, 2355, + 1885, 2358, 2358, 2359, 2360, 2362, 2363, 2361, + 1885, 2364, 2364, 2365, 2366, 2368, 2369, 2367, + 1885, 2370, 2370, 2371, 2372, 2374, 2375, 2373, + 1885, 2376, 2376, 2377, 2378, 2380, 2381, 2379, + 1885, 2382, 2382, 2383, 2384, 2385, 2385, 2385, + 2386, 2387, 2385, 2385, 2385, 71, 2388, 2388, + 2388, 2388, 71, 2390, 2389, 71, 2392, 2391, + 71, 2394, 2393, 71, 2395, 1885, 2396, 2396, + 2397, 2398, 2400, 2401, 2399, 1885, 2402, 2402, + 2403, 2404, 2406, 2407, 2405, 1885, 2408, 2408, + 2409, 2410, 2412, 2413, 2411, 1885, 2415, 2415, + 2416, 2417, 2418, 2419, 2414, 2414, 2414, 71, + 2420, 2420, 2421, 2422, 2423, 2424, 71, 2426, + 2426, 2427, 2428, 2429, 2430, 2425, 2425, 2425, + 71, 2431, 2431, 2432, 2433, 2434, 2435, 71, + 2437, 2437, 2438, 2439, 2440, 2441, 2436, 2436, + 2436, 71, 2442, 2442, 2443, 2444, 2445, 2446, + 71, 2447, 2447, 2448, 2449, 2450, 2450, 2450, + 2451, 2452, 2450, 2450, 2450, 71, 2453, 2453, + 2453, 2453, 71, 2455, 2454, 71, 2457, 2456, + 71, 2459, 2458, 71, 2461, 2460, 71, 2463, + 2462, 71, 2464, 2464, 2464, 71, 2465, 2465, + 2466, 2467, 2468, 2469, 71, 2465, 2465, 2466, + 2467, 2468, 2469, 2470, 1885, 2471, 2471, 2472, + 2473, 2475, 2476, 2474, 1885, 2477, 2477, 2478, + 2479, 2481, 2482, 2480, 2480, 2480, 71, 2483, + 2483, 2484, 2485, 2486, 2487, 71, 2483, 2483, + 2484, 2485, 2488, 2486, 2487, 2488, 2488, 2488, + 71, 2489, 2489, 71, 2491, 2490, 71, 2493, + 2492, 71, 2495, 2494, 71, 2497, 2497, 2498, + 2499, 2500, 2501, 2502, 2503, 2504, 2505, 2505, + 2506, 2507, 2496, 2508, 2509, 2496, 2510, 2496, + 2512, 2512, 2511, 2514, 2513, 2511, 2515, 2496, + 2516, 2516, 2517, 2518, 2520, 2521, 2522, 2522, + 2523, 2524, 2519, 2496, 2526, 2525, 71, 2527, + 2496, 2528, 2528, 2528, 2528, 2511, 2530, 2529, + 2529, 2529, 2511, 2531, 1885, 2532, 2532, 2533, + 2534, 2536, 2537, 2535, 1885, 2538, 2538, 2539, + 2540, 2542, 2543, 2541, 2541, 2541, 71, 2544, + 2544, 2545, 2546, 2547, 2548, 71, 2544, 2544, + 2545, 2546, 2547, 2548, 2549, 1885, 2550, 2550, + 2551, 2552, 2554, 2555, 2553, 1885, 2557, 2557, + 2558, 2559, 2561, 2562, 2560, 2560, 2560, 2556, + 2563, 2563, 2563, 2556, 2564, 2564, 2565, 2566, + 2567, 2568, 2569, 2569, 2570, 2571, 2560, 2560, + 2560, 2556, 2573, 2572, 2556, 2575, 2574, 71, + 2577, 2576, 71, 2579, 2578, 71, 2580, 1885, + 2581, 2581, 2582, 2583, 2585, 2586, 2584, 1885, + 2587, 2587, 2588, 2589, 2591, 2592, 2590, 1885, + 2593, 2593, 2594, 2595, 2597, 2598, 2596, 1885, + 2599, 2599, 2600, 2601, 2603, 2604, 2602, 2602, + 2602, 2556, 2605, 2605, 2605, 2556, 2606, 2606, + 2607, 2608, 2609, 2610, 2611, 2611, 2612, 2613, + 2602, 2602, 2602, 2556, 2615, 2614, 2556, 2617, + 2616, 71, 2619, 2618, 71, 2620, 1885, 2621, + 2621, 2622, 2623, 2625, 2626, 2624, 1885, 2628, + 2628, 2629, 2630, 2631, 2632, 2633, 2634, 2635, + 2636, 2627, 2637, 2637, 2638, 2639, 2640, 2641, + 2627, 2637, 2637, 2638, 2639, 2640, 2641, 2643, + 2642, 2644, 2644, 2645, 2646, 2648, 2649, 2647, + 2642, 2650, 2650, 2651, 2652, 2653, 2654, 2655, + 2627, 2657, 2657, 2658, 2659, 2660, 2660, 2661, + 2662, 2663, 2663, 2664, 2665, 2666, 2666, 2666, + 2666, 2667, 2668, 2666, 2666, 2669, 2670, 2671, + 2672, 2666, 2666, 2673, 2674, 2675, 2676, 2666, + 2666, 2677, 2678, 2656, 2657, 2657, 2658, 2659, + 2680, 2667, 2668, 2680, 2680, 2680, 2679, 2666, + 2666, 2679, 2682, 2681, 2679, 2660, 2660, 2661, + 2662, 2666, 2666, 2666, 2666, 2671, 2672, 2666, + 2666, 2673, 2674, 2679, 2684, 2683, 2679, 2680, + 2657, 2657, 2658, 2659, 2660, 2660, 2661, 2662, + 2663, 2663, 2664, 2665, 2666, 2666, 2666, 2666, + 2667, 2668, 2666, 2666, 2669, 2670, 2671, 2672, + 2666, 2666, 2673, 2674, 2675, 2676, 2666, 2666, + 2677, 2678, 2680, 2680, 2680, 2679, 2682, 2684, + 2686, 2681, 2683, 2685, 2679, 2688, 2687, 2627, + 2690, 2689, 2627, 2691, 2691, 2692, 2693, 2694, + 2695, 2627, 2691, 2691, 2692, 2693, 2694, 2695, + 2696, 2642, 2697, 2697, 2698, 2699, 2701, 2702, + 2700, 2642, 2704, 2704, 2705, 2706, 2707, 2708, + 2709, 2707, 2703, 2711, 2712, 2712, 2713, 2714, + 2715, 2715, 2716, 2717, 2718, 2718, 2719, 2720, + 2721, 2721, 2721, 2721, 2722, 2723, 2721, 2721, + 2724, 2725, 2726, 2727, 2721, 2721, 2728, 2729, + 2730, 2731, 2721, 2721, 2732, 2733, 2711, 2710, + 2735, 2734, 2627, 2737, 2736, 2627, 2738, 2738, + 2739, 2740, 2741, 2742, 2627, 2738, 2738, 2739, + 2740, 2741, 2742, 2743, 2642, 2744, 2744, 2745, + 2746, 2748, 2749, 2747, 2642, 2750, 2750, 2751, + 2752, 2753, 2754, 2755, 2753, 2753, 2753, 2703, + 2756, 2757, 2757, 2758, 2759, 2760, 2760, 2761, + 2762, 2763, 2763, 2764, 2765, 2766, 2766, 2766, + 2766, 2767, 2768, 2766, 2766, 2769, 2770, 2771, + 2772, 2766, 2766, 2773, 2774, 2775, 2776, 2766, + 2766, 2777, 2778, 2756, 2756, 2756, 2710, 2780, + 2779, 2627, 2782, 2781, 2627, 2783, 2783, 2784, + 2785, 2786, 2787, 2627, 2783, 2783, 2784, 2785, + 2786, 2787, 2788, 2642, 2789, 2789, 2790, 2791, + 2793, 2794, 2792, 2642, 2795, 2795, 2796, 2797, + 2798, 2798, 2798, 2799, 2800, 2798, 2798, 2798, + 2627, 2801, 2801, 2802, 2803, 2804, 2804, 2805, + 2806, 2807, 2807, 2808, 2809, 2810, 2810, 2810, + 2810, 2811, 2812, 2810, 2810, 2813, 2814, 2815, + 2816, 2810, 2810, 2817, 2818, 2819, 2820, 2810, + 2810, 2821, 2822, 2656, 2824, 2823, 2627, 2826, + 2825, 2627, 2828, 2827, 71, 2830, 2831, 2832, + 2833, 2834, 2835, 2836, 2837, 2838, 2839, 2840, + 2841, 2842, 2843, 2844, 2830, 2831, 2832, 2833, + 2834, 2835, 2836, 2837, 2838, 2839, 2840, 2841, + 2842, 2843, 2844, 2829, 2845, 2845, 2846, 2847, + 2848, 2849, 2850, 2848, 2849, 2850, 2851, 2852, + 2829, 2853, 2853, 2854, 2855, 2857, 2858, 2856, + 2856, 2856, 71, 2859, 2859, 2860, 2861, 2862, + 2863, 71, 2859, 2859, 2860, 2861, 2862, 2863, + 2864, 1885, 2865, 2865, 2866, 2867, 2869, 2870, + 2868, 1885, 2871, 2871, 2872, 2873, 2875, 2876, + 2874, 1885, 2877, 2877, 2878, 2879, 2881, 2882, + 2880, 1885, 2884, 2884, 2885, 2886, 2888, 2889, + 2887, 2883, 2890, 2890, 2891, 2892, 2894, 2895, + 2893, 2883, 2896, 2896, 2897, 2898, 2900, 2901, + 2899, 2883, 2902, 2902, 2903, 2904, 2906, 2907, + 2905, 2883, 2908, 2908, 2909, 2910, 2912, 2913, + 2911, 1885, 2914, 2914, 2915, 2916, 2918, 2919, + 2917, 1885, 2920, 2920, 2921, 2922, 2923, 2923, + 2923, 2924, 2925, 2923, 2923, 2923, 71, 2926, + 2926, 2927, 2928, 2929, 2930, 71, 2931, 2931, + 2932, 2933, 2934, 2935, 2936, 2934, 2934, 2934, + 71, 2937, 2937, 71, 2939, 2938, 71, 2941, + 2940, 71, 2943, 2942, 71, 2945, 2944, 71, + 2947, 2946, 71, 2949, 2948, 71, 2951, 2950, + 71, 2953, 2952, 71, 2954, 2954, 2829, 2955, + 2955, 2829, 2956, 2956, 2957, 2958, 2959, 2960, + 2829, 2961, 2961, 2829, 2962, 2962, 2829, 2963, + 2963, 2829, 2964, 2964, 2965, 2966, 2967, 2968, + 2829, 2969, 2969, 2829, 2970, 2970, 2971, 2972, + 2973, 2974, 2829, 2975, 2976, 2977, 2978, 2975, + 2976, 2977, 2978, 2829, 2979, 2979, 2829, 2980, + 2980, 2981, 2982, 2983, 2984, 2829, 2985, 2986, + 2985, 2986, 2829, 2987, 2987, 2829, 2988, 2988, + 2829, 2989, 2989, 2829, 2990, 2990, 2829, 2991, + 2991, 2992, 2993, 2994, 2995, 2829, 2996, 2996, + 2997, 2998, 2999, 3000, 2829, 3001, 3001, 2829, + 3002, 3002, 2829, 3003, 3003, 3004, 3005, 3006, + 3007, 2829, 3008, 3008, 2829, 3009, 3009, 2829, + 3010, 3010, 2829, 3011, 3011, 3012, 3013, 3014, + 3015, 2829, 3016, 3017, 3018, 3016, 3017, 3018, + 2829, 3019, 3019, 2829, 3020, 3020, 2829, 3021, + 3021, 2829, 3022, 3022, 3023, 3024, 3025, 3026, + 2829, 3027, 3028, 3027, 3028, 2829, 3029, 3029, + 2829, 3030, 3030, 2829, 3031, 3031, 3032, 3033, + 3034, 3035, 2829, 3036, 3036, 2829, 3037, 3037, + 2829, 3038, 3038, 2829, 3039, 3039, 3040, 3041, + 3042, 3043, 2829, 3044, 3044, 3045, 3046, 3047, + 3048, 2829, 3049, 3049, 2829, 3050, 3050, 2829, + 3051, 3052, 2829, 3053, 2829, 3054, 3054, 3055, + 3056, 3057, 3058, 2829, 3059, 2829, 3060, 3060, + 3061, 3062, 3063, 3064, 2829, 3065, 3065, 2829, + 3066, 3066, 2829, 3067, 3067, 2829, 3068, 3068, + 2829, 3069, 3069, 3070, 3071, 3072, 3073, 2829, + 3074, 3074, 2829, 3075, 3075, 2829, 3076, 3076, + 2829, 3077, 3077, 2829, 3078, 3078, 2829, 3079, + 3079, 2829, 3080, 3080, 2829, 3081, 3081, 3082, + 3083, 3084, 3085, 2829, 3086, 3087, 3086, 3087, + 2829, 3088, 3088, 2829, 3089, 3089, 3090, 3091, + 3092, 3093, 2829, 3094, 3094, 3095, 3096, 3097, + 3098, 2829, 3099, 3100, 3101, 3102, 3101, 3102, + 2829, 3103, 2829, 3104, 3104, 3105, 3106, 3107, + 3108, 2829, 3109, 2829, 3110, 3110, 3111, 3112, + 3113, 3114, 2829, 3115, 3115, 2829, 3116, 3116, + 3117, 3118, 3119, 3120, 2829, 3121, 3121, 3122, + 3123, 3124, 3125, 2829, 3126, 3127, 3126, 3127, + 2829, 3128, 3128, 2829, 3129, 3129, 2829, 3130, + 3130, 2829, 3131, 3131, 3132, 3133, 3134, 3135, + 2829, 3136, 3136, 3137, 3138, 3139, 3140, 2829, + 3141, 3142, 3143, 3141, 3142, 3143, 2829, 3144, + 3144, 2829, 3145, 3145, 2829, 3146, 3146, 2829, + 3147, 3147, 3148, 3149, 3150, 3151, 2829, 3152, + 3152, 2829, 3153, 3153, 3154, 3155, 3156, 3157, + 2829, 3158, 3158, 3159, 3160, 3161, 3161, 3162, + 3163, 2829, 3164, 3164, 2829, 3165, 3165, 3166, + 3167, 3168, 3169, 3170, 2829, 3171, 3171, 3172, + 3173, 3174, 3174, 3175, 3176, 2829, 3177, 3177, + 2829, 3178, 3178, 2829, 3179, 3179, 2829, 3180, + 3180, 2829, 3181, 3181, 3182, 3183, 3184, 3185, + 2829, 3186, 3186, 2829, 3187, 3187, 2829, 3188, + 3188, 3189, 3190, 3191, 3192, 2829, 3193, 3194, + 3195, 3193, 3194, 3195, 2829, 3196, 3196, 3197, + 3198, 3199, 3200, 2829, 3201, 3201, 2829, 3202, + 3202, 2829, 3203, 3203, 2829, 3204, 3204, 3205, + 3206, 3207, 3208, 2829, 3209, 3209, 3210, 3211, + 3212, 3213, 2829, 3214, 3215, 3216, 3217, 3214, + 3215, 3216, 3217, 2829, 3218, 3218, 2829, 3219, + 3219, 3220, 3221, 3222, 3223, 2829, 3224, 3224, + 2829, 3225, 3225, 3226, 3227, 3228, 3229, 2829, + 3230, 3230, 2829, 3231, 3231, 3232, 3233, 3234, + 3235, 2829, 3236, 3236, 2829, 3237, 3237, 2829, + 3238, 3238, 2829, 3239, 3239, 3240, 3241, 3242, + 3243, 2829, 3244, 3245, 3246, 3244, 3245, 3246, + 2829, 3247, 3247, 2829, 3248, 3248, 2829, 3249, + 3249, 3250, 3251, 3252, 3253, 2829, 3254, 3254, + 2829, 3255, 3255, 3256, 3257, 3258, 3259, 2829, + 3260, 3260, 2829, 3261, 3261, 2829, 3263, 3262, + 3264, 3264, 3265, 3266, 3268, 3269, 3267, 3262, + 3270, 3270, 2829, 3271, 3271, 2829, 3272, 3272, + 3273, 3274, 3275, 3276, 2829, 3277, 3277, 3277, + 3277, 3277, 3277, 71, 3278, 3278, 3278, 3278, + 71, 3279, 3279, 3279, 3279, 71, 3280, 1885, + 3281, 3281, 3282, 3283, 3285, 3286, 3284, 1885, + 3287, 3287, 3288, 3289, 3291, 3292, 3290, 1885, + 3293, 3293, 3294, 3295, 3297, 3298, 3296, 1885, + 3299, 3299, 3300, 3301, 3303, 3304, 3302, 3302, + 3302, 71, 3305, 3305, 3306, 3307, 3308, 3309, + 71, 3305, 3305, 3306, 3307, 3310, 3308, 3309, + 3310, 3310, 3310, 71, 3311, 3311, 71, 3313, + 3312, 71, 3315, 3314, 71, 3317, 3316, 71, + 3318, 3318, 3318, 3318, 71, 3319, 3319, 71, + 3320, 1885, 3321, 3321, 3322, 3323, 3325, 3326, + 3324, 1885, 3327, 3327, 3328, 3329, 3331, 3332, + 3330, 1885, 3333, 3333, 3334, 3335, 3337, 3338, + 3336, 1885, 3339, 3339, 3340, 3341, 3343, 3344, + 3342, 1885, 3345, 3345, 3346, 3347, 3349, 3350, + 3348, 1885, 3351, 3351, 3352, 3353, 3354, 3356, + 3357, 3355, 3355, 3355, 2556, 3358, 3358, 3359, + 3360, 3361, 3362, 2556, 3364, 3364, 3365, 3366, + 3368, 3369, 3367, 3367, 3367, 3363, 3370, 3370, + 3370, 3363, 3372, 3371, 3371, 3371, 3363, 3373, + 3373, 3373, 3363, 3375, 3374, 3374, 3374, 3363, + 3377, 3376, 3376, 3376, 3363, 3378, 3378, 3378, + 3363, 3380, 3379, 3379, 3379, 3363, 3381, 3381, + 3381, 3381, 3382, 3382, 3382, 3363, 3383, 3383, + 3383, 3383, 71, 3384, 3363, 3380, 3363, 3377, + 3363, 3385, 3363, 3375, 3363, 3387, 3386, 71, + 3388, 3388, 3388, 2556, 3358, 3358, 3359, 3360, + 3361, 3362, 3389, 3389, 3389, 2556, 3391, 3390, + 71, 3393, 3392, 71, 3395, 3394, 71, 3396, + 1885, 3397, 3397, 3398, 3399, 3401, 3402, 3400, + 1885, 3403, 3403, 3404, 3405, 3407, 3408, 3406, + 1885, 3409, 3409, 3410, 3411, 3413, 3414, 3412, + 1885, 3415, 3415, 3416, 3417, 3419, 3420, 3418, + 1885, 3421, 3421, 3422, 3423, 3425, 3426, 3424, + 1885, 3427, 3427, 3428, 3429, 3430, 3432, 3433, + 3431, 3431, 3431, 2556, 3434, 3434, 3434, 3434, + 2556, 3435, 3435, 3435, 2556, 3434, 3434, 3434, + 3434, 3436, 3436, 3436, 2556, 3438, 3437, 71, + 3440, 3439, 71, 3442, 3441, 71, 3443, 1885, + 3444, 3444, 3445, 3446, 3448, 3449, 3447, 1885, + 3450, 3450, 3451, 3452, 3454, 3455, 3453, 1885, + 3456, 3456, 3457, 3458, 3460, 3461, 3459, 1885, + 3462, 3462, 3463, 3464, 3466, 3467, 3465, 1885, + 3468, 3468, 3469, 3470, 3472, 3473, 3471, 1885, + 3474, 3474, 3475, 3476, 3478, 3479, 3477, 3477, + 3477, 2556, 3480, 3480, 3480, 2556, 3481, 3481, + 3482, 3483, 3484, 3485, 3486, 3486, 3487, 3488, + 3477, 3477, 3477, 2556, 3490, 3489, 2556, 3492, + 3491, 71, 3494, 3493, 71, 3496, 3495, 71, + 3497, 1885, 3498, 3498, 3499, 3500, 3502, 3503, + 3501, 1885, 3504, 3504, 3505, 3506, 3507, 3508, + 3509, 3507, 1862, 3510, 3511, 3510, 3510, 3510, + 3511, 1862, 3513, 3512, 71, 3514, 1885, 3515, + 3515, 3516, 3517, 3519, 3520, 3518, 1885, 3521, + 3521, 3522, 3523, 3525, 3526, 3524, 3524, 3524, + 2556, 3527, 3527, 3527, 2556, 3528, 3528, 3528, + 2556, 3529, 3529, 3529, 2556, 3531, 3530, 3532, + 3532, 3532, 2556, 3533, 3533, 3533, 2556, 3534, + 3534, 3534, 2556, 3535, 3535, 3535, 2556, 3537, + 3531, 3537, 3537, 3537, 3536, 3539, 3538, 71, + 3540, 3540, 3540, 2556, 3541, 3541, 3541, 2556, + 3543, 3542, 3544, 3544, 3544, 2556, 3545, 3545, + 3545, 2556, 3546, 3543, 3546, 3546, 3546, 3542, + 3547, 3547, 3547, 2556, 3548, 3548, 3548, 2556, + 3549, 3542, 3550, 3550, 3550, 2556, 3551, 3551, + 3551, 2556, 3552, 3549, 3552, 3552, 3552, 3542, + 3553, 1885, 3554, 3554, 3555, 3556, 3558, 3559, + 3557, 1885, 3560, 3560, 3561, 3562, 3564, 3565, + 3563, 1885, 3566, 3566, 3567, 3568, 3570, 3571, + 3569, 1885, 3573, 3573, 3574, 3575, 3576, 3577, + 3572, 3572, 3572, 71, 3578, 3578, 3578, 3578, + 71, 3580, 3579, 71, 3582, 3581, 71, 3583, + 1885, 3584, 3584, 3585, 3586, 3588, 3589, 3587, + 1885, 3591, 3591, 3592, 3593, 3594, 3595, 3590, + 3590, 3590, 71, 3596, 3596, 3597, 3598, 3599, + 3600, 71, 3602, 3602, 3603, 3604, 3605, 3606, + 3601, 3601, 3601, 71, 3607, 3607, 3607, 3607, + 71, 3609, 3608, 71, 3611, 3610, 71, 3613, + 3613, 3614, 3615, 3616, 3617, 3617, 3617, 3618, + 3619, 3620, 3621, 3617, 3617, 3617, 3612, 3622, + 3622, 3623, 3624, 3625, 3626, 69, 3627, 3626, + 3618, 3619, 3628, 3629, 3630, 3631, 3632, 3633, + 69, 69, 3626, 69, 3626, 69, 3626, 69, + 783, 3635, 3635, 3614, 3636, 3637, 3617, 3638, + 3639, 3640, 3641, 3642, 3643, 3644, 3645, 3646, + 3647, 3648, 3649, 3650, 3651, 3652, 3617, 3617, + 3638, 3639, 3640, 3641, 3642, 3643, 3644, 3645, + 3646, 3647, 3648, 3649, 3650, 3651, 3652, 3618, + 3619, 3653, 3654, 3617, 3617, 3617, 3634, 3635, + 3635, 3614, 3636, 3637, 3617, 3657, 3658, 3659, + 3660, 3661, 3662, 3663, 3664, 3665, 3666, 3667, + 3668, 3669, 3670, 3671, 3617, 3617, 3657, 3658, + 3659, 3660, 3661, 3662, 3663, 3664, 3665, 3666, + 3667, 3668, 3669, 3670, 3671, 3618, 3619, 3653, + 3654, 3617, 3656, 3617, 3617, 3655, 3673, 3673, + 3674, 3675, 3676, 3677, 3677, 3677, 3678, 3679, + 3680, 3681, 3677, 3677, 3677, 3672, 3613, 3613, + 3614, 3682, 3683, 3617, 3657, 3658, 3659, 3660, + 3661, 3662, 3663, 3664, 3665, 3666, 3667, 3668, + 3669, 3670, 3671, 3617, 3617, 3657, 3658, 3659, + 3660, 3661, 3662, 3663, 3664, 3665, 3666, 3667, + 3668, 3669, 3670, 3671, 3618, 3619, 3684, 3685, + 3617, 3656, 3617, 3617, 3655, 3686, 3686, 3614, + 3687, 3688, 3617, 3617, 3617, 83, 84, 3689, + 3690, 3617, 3617, 3617, 783, 3691, 3691, 3623, + 3692, 3693, 3626, 69, 3627, 3626, 83, 84, + 3689, 3694, 101, 102, 3689, 3695, 69, 69, + 3626, 69, 3626, 69, 3626, 69, 783, 3622, + 3622, 3623, 3624, 3625, 3626, 69, 3698, 3699, + 3700, 3701, 3702, 3703, 3704, 3705, 3706, 3707, + 3708, 3709, 3710, 3711, 3712, 3627, 3626, 3698, + 3699, 3700, 3701, 3702, 3703, 3704, 3705, 3706, + 3707, 3708, 3709, 3710, 3711, 3712, 3618, 3619, + 3628, 3629, 3630, 3631, 3632, 3633, 69, 69, + 3626, 3697, 69, 3626, 69, 3626, 69, 3696, + 3691, 3691, 3623, 3692, 3693, 3626, 69, 3698, + 3699, 3700, 3701, 3702, 3703, 3704, 3705, 3706, + 3707, 3708, 3709, 3710, 3711, 3712, 3627, 3626, + 3698, 3699, 3700, 3701, 3702, 3703, 3704, 3705, + 3706, 3707, 3708, 3709, 3710, 3711, 3712, 83, + 84, 3713, 3694, 101, 102, 3713, 3695, 69, + 69, 3626, 3697, 69, 3626, 69, 3626, 69, + 3696, 690, 690, 690, 944, 941, 944, 943, + 944, 941, 945, 941, 940, 690, 690, 690, + 1093, 1093, 1094, 1095, 1062, 1063, 1064, 1062, + 1096, 1097, 1062, 1062, 1062, 1068, 690, 1108, + 1108, 1109, 1110, 1103, 1111, 1112, 1113, 1113, + 1114, 1115, 1103, 1103, 1103, 1102, 1117, 1116, + 1102, 690, 1127, 1127, 1128, 1129, 1130, 1131, + 1132, 1133, 1134, 1135, 1136, 1137, 1138, 1139, + 1140, 1141, 1142, 1143, 1144, 1130, 1131, 1132, + 1133, 1134, 1135, 1136, 1137, 1138, 1139, 1140, + 1141, 1142, 1143, 1144, 1145, 1146, 1147, 1147, + 1148, 1149, 1118, 1172, 1171, 1118, 690, 1633, + 1633, 1634, 1635, 1636, 1637, 1638, 1638, 1639, + 1640, 1629, 1629, 1629, 1625, 1642, 1641, 1625, + 1661, 1661, 1662, 1663, 1665, 1666, 1664, 1664, + 1664, 1625, 1611, 1668, 1668, 1669, 1670, 1671, + 1672, 1673, 1673, 1674, 1675, 1664, 1664, 1664, + 1625, 1677, 1676, 1625, 1679, 1678, 1611, 690, + 690, 690, 690, 690, 690, 690, 690, 690, + 2080, 2080, 2081, 2082, 2083, 2084, 2085, 2085, + 2086, 2087, 2079, 2079, 2079, 2070, 2089, 2088, + 2070, 690, 690, 2253, 2253, 2254, 2255, 2256, + 2257, 2202, 2202, 2258, 2259, 2093, 2261, 2260, + 2093, 2235, 2235, 2236, 2237, 2239, 2240, 2202, + 2202, 2241, 2242, 2238, 2093, 2273, 2272, 2093, + 2216, 2216, 2217, 2218, 2220, 2221, 2202, 2202, + 2222, 2223, 2219, 2093, 2286, 2285, 2093, 2196, + 2196, 2197, 2198, 2200, 2201, 2202, 2202, 2203, + 2204, 2199, 2093, 2299, 2298, 2093, 690, 690, + 690, 690, 2497, 2497, 2498, 2499, 2500, 2501, + 2502, 2503, 2504, 2505, 2505, 2506, 2507, 2496, + 2526, 2525, 71, 690, 2564, 2564, 2565, 2566, + 2567, 2568, 2569, 2569, 2570, 2571, 2560, 2560, + 2560, 2556, 2573, 2572, 2556, 690, 2606, 2606, + 2607, 2608, 2609, 2610, 2611, 2611, 2612, 2613, + 2602, 2602, 2602, 2556, 2615, 2614, 2556, 690, + 2660, 2660, 2661, 2662, 2666, 2666, 2666, 2666, + 2671, 2672, 2666, 2666, 2673, 2674, 2679, 2684, + 2683, 2679, 2657, 2657, 2658, 2659, 2680, 2667, + 2668, 2680, 2680, 2680, 2679, 2682, 2681, 2679, + 2680, 2657, 2657, 2658, 2659, 2660, 2660, 2661, + 2662, 2663, 2663, 2664, 2665, 2666, 2666, 2666, + 2666, 2667, 2668, 2666, 2666, 2669, 2670, 2671, + 2672, 2666, 2666, 2673, 2674, 2675, 2676, 2666, + 2666, 2677, 2678, 2680, 2680, 2680, 2679, 2682, + 2684, 2686, 2681, 2683, 2685, 2679, 690, 690, + 690, 690, 690, 690, 690, 3481, 3481, 3482, + 3483, 3484, 3485, 3486, 3486, 3487, 3488, 3477, + 3477, 3477, 2556, 3490, 3489, 2556, 690, 690, + 690, 690, 690, 690, 0 +}; + +static const short _zone_scanner_trans_targs[] = { + 0, 1, 1, 1, 2, 4, 17, 36, + 50, 57, 143, 73, 77, 85, 91, 107, + 110, 117, 128, 138, 1127, 151, 1132, 264, + 0, 3, 3, 3, 2, 166, 166, 166, + 166, 166, 3, 169, 0, 3, 3, 3, + 4, 17, 36, 50, 57, 62, 73, 77, + 85, 91, 107, 110, 117, 128, 138, 3, + 169, 0, 5, 5, 5, 230, 233, 237, + 5, 10, 6, 11, 20, 6, 5, 0, + 5, 5, 9, 5, 10, 11, 20, 0, + 7, 7, 7, 1127, 8, 0, 7, 7, + 7, 1127, 8, 8, 1127, 6, 6, 10, + 5, 12, 12, 12, 13, 1133, 176, 244, + 12, 12, 12, 13, 1133, 176, 244, 13, + 1128, 14, 0, 14, 14, 15, 172, 179, + 180, 181, 182, 183, 184, 185, 186, 187, + 188, 189, 190, 191, 192, 1135, 239, 1133, + 240, 1136, 242, 0, 16, 16, 16, 1129, + 267, 0, 16, 16, 16, 1129, 267, 18, + 22, 29, 32, 19, 5, 5, 5, 5, + 10, 6, 11, 20, 21, 21, 21, 13, + 21, 21, 21, 13, 23, 28, 24, 25, + 26, 27, 5, 5, 5, 5, 10, 6, + 11, 20, 5, 5, 5, 5, 10, 6, + 11, 20, 30, 31, 5, 5, 5, 5, + 10, 6, 11, 20, 33, 34, 35, 5, + 5, 5, 5, 10, 6, 11, 20, 37, + 41, 49, 38, 39, 40, 5, 5, 5, + 5, 10, 6, 11, 20, 42, 45, 43, + 44, 5, 5, 5, 5, 10, 6, 11, + 20, 46, 47, 48, 5, 5, 5, 5, + 10, 6, 11, 20, 5, 5, 5, 5, + 10, 6, 11, 20, 51, 52, 53, 55, + 54, 5, 5, 5, 5, 10, 6, 11, + 20, 56, 5, 5, 5, 5, 10, 6, + 11, 20, 58, 59, 60, 61, 5, 5, + 5, 5, 10, 6, 11, 20, 63, 66, + 64, 64, 64, 64, 141, 64, 64, 64, + 4, 17, 36, 50, 57, 65, 73, 77, + 85, 91, 107, 110, 117, 128, 138, 64, + 141, 67, 68, 69, 70, 71, 72, 5, + 5, 5, 5, 10, 6, 11, 20, 74, + 76, 75, 5, 5, 5, 5, 10, 6, + 11, 20, 5, 5, 5, 5, 10, 6, + 11, 20, 78, 80, 82, 84, 79, 5, + 5, 5, 5, 10, 6, 11, 20, 81, + 5, 5, 5, 5, 10, 6, 11, 20, + 83, 5, 5, 5, 5, 10, 6, 11, + 20, 5, 5, 5, 5, 10, 6, 11, + 20, 86, 90, 87, 88, 89, 5, 5, + 5, 5, 10, 6, 11, 20, 5, 5, + 5, 5, 10, 6, 11, 20, 92, 96, + 98, 93, 94, 95, 5, 5, 5, 5, + 10, 6, 11, 20, 97, 5, 5, 5, + 5, 10, 6, 11, 20, 5, 5, 5, + 99, 5, 10, 6, 11, 20, 100, 5, + 5, 5, 101, 5, 10, 6, 11, 20, + 5, 5, 5, 102, 5, 10, 6, 11, + 20, 103, 104, 105, 106, 5, 5, 5, + 5, 10, 6, 11, 20, 108, 109, 5, + 5, 5, 5, 10, 6, 11, 20, 111, + 112, 116, 5, 5, 5, 5, 10, 6, + 11, 20, 113, 114, 115, 5, 5, 5, + 5, 10, 6, 11, 20, 5, 5, 5, + 5, 10, 6, 11, 20, 118, 120, 122, + 124, 119, 5, 5, 5, 5, 10, 6, + 11, 20, 121, 5, 5, 5, 5, 10, + 6, 11, 20, 123, 5, 5, 5, 5, + 10, 6, 11, 20, 125, 126, 127, 5, + 5, 5, 5, 10, 6, 11, 20, 129, + 132, 134, 130, 131, 5, 5, 5, 5, + 10, 6, 11, 20, 133, 5, 5, 5, + 5, 10, 6, 11, 20, 135, 136, 0, + 137, 0, 5, 5, 5, 137, 5, 10, + 6, 11, 20, 139, 140, 5, 5, 5, + 5, 10, 6, 11, 20, 141, 64, 142, + 142, 142, 1130, 265, 144, 145, 145, 145, + 145, 150, 145, 145, 145, 146, 4, 17, + 36, 50, 57, 65, 73, 77, 85, 91, + 107, 110, 117, 128, 138, 145, 150, 64, + 64, 64, 146, 147, 147, 147, 147, 147, + 64, 141, 148, 148, 149, 149, 149, 149, + 149, 64, 64, 64, 148, 64, 141, 150, + 145, 151, 1127, 0, 153, 195, 201, 154, + 155, 156, 157, 158, 159, 1131, 0, 161, + 161, 161, 161, 162, 161, 161, 161, 161, + 162, 162, 0, 161, 0, 164, 164, 164, + 164, 165, 164, 164, 164, 164, 165, 165, + 164, 167, 167, 168, 168, 168, 168, 168, + 3, 3, 3, 167, 3, 169, 169, 3, + 0, 171, 171, 171, 171, 246, 247, 248, + 171, 171, 171, 171, 246, 247, 248, 0, + 173, 173, 173, 1128, 13, 1133, 176, 1134, + 244, 173, 173, 173, 1128, 1134, 174, 174, + 174, 1132, 175, 175, 1132, 177, 177, 177, + 1127, 8, 177, 177, 177, 8, 178, 178, + 178, 193, 228, 193, 1135, 0, 196, 197, + 198, 199, 200, 1131, 202, 203, 1131, 0, + 174, 174, 174, 1132, 175, 0, 206, 206, + 206, 1130, 207, 206, 206, 206, 1130, 207, + 207, 1130, 0, 209, 209, 209, 1135, 193, + 1136, 228, 209, 209, 209, 1135, 1136, 225, + 0, 225, 225, 225, 226, 174, 174, 174, + 1132, 175, 227, 1132, 227, 227, 175, 227, + 227, 227, 175, 229, 229, 229, 193, 229, + 229, 229, 193, 231, 232, 5, 5, 5, + 5, 10, 6, 11, 20, 234, 235, 236, + 5, 5, 5, 5, 10, 6, 11, 20, + 238, 5, 5, 5, 5, 10, 6, 11, + 20, 239, 1135, 241, 241, 241, 1127, 241, + 241, 241, 1127, 243, 243, 243, 243, 243, + 243, 245, 245, 245, 245, 245, 245, 246, + 171, 178, 178, 178, 193, 228, 249, 249, + 249, 249, 249, 249, 0, 264, 1132, 265, + 1130, 267, 1129, 269, 1137, 269, 1137, 0, + 271, 277, 278, 273, 1138, 271, 272, 273, + 1138, 271, 273, 274, 0, 275, 276, 1138, + 271, 272, 273, 1138, 0, 280, 285, 281, + 1139, 1140, 0, 282, 283, 284, 1139, 280, + 1140, 281, 285, 286, 287, 0, 288, 289, + 290, 285, 286, 287, 0, 292, 292, 292, + 292, 299, 0, 293, 0, 294, 294, 294, + 293, 296, 296, 296, 296, 296, 1141, 295, + 0, 294, 294, 294, 1141, 295, 295, 1141, + 297, 0, 297, 298, 298, 298, 298, 298, + 294, 294, 294, 297, 1141, 295, 299, 292, + 0, 301, 301, 301, 301, 311, 302, 310, + 306, 302, 303, 306, 0, 304, 304, 304, + 302, 306, 1142, 305, 304, 304, 304, 1142, + 305, 305, 1142, 307, 0, 308, 309, 302, + 303, 306, 311, 301, 313, 313, 313, 313, + 328, 314, 0, 0, 315, 315, 315, 1143, + 319, 325, 0, 315, 315, 315, 316, 324, + 320, 1143, 319, 325, 0, 316, 317, 320, + 318, 318, 318, 316, 320, 1143, 319, 318, + 318, 318, 319, 1143, 321, 0, 322, 323, + 316, 317, 320, 325, 1144, 326, 326, 326, + 326, 327, 327, 326, 328, 313, 0, 330, + 331, 332, 335, 333, 333, 333, 333, 333, + 334, 1145, 1146, 1147, 334, 333, 0, 337, + 337, 337, 337, 342, 1148, 1149, 1150, 337, + 337, 337, 338, 343, 360, 374, 381, 386, + 394, 398, 406, 412, 428, 431, 438, 449, + 459, 337, 342, 1148, 1149, 1150, 337, 337, + 337, 339, 462, 466, 337, 342, 1148, 1149, + 1150, 340, 341, 337, 337, 337, 337, 342, + 1148, 1149, 1150, 342, 337, 344, 346, 353, + 356, 345, 337, 337, 337, 337, 342, 1148, + 1149, 1150, 347, 352, 348, 349, 350, 351, + 337, 337, 337, 337, 342, 1148, 1149, 1150, + 337, 337, 337, 337, 342, 1148, 1149, 1150, + 354, 355, 337, 337, 337, 337, 342, 1148, + 1149, 1150, 357, 358, 359, 337, 337, 337, + 337, 342, 1148, 1149, 1150, 361, 365, 373, + 362, 363, 364, 337, 337, 337, 337, 342, + 1148, 1149, 1150, 366, 369, 367, 368, 337, + 337, 337, 337, 342, 1148, 1149, 1150, 370, + 371, 372, 337, 337, 337, 337, 342, 1148, + 1149, 1150, 337, 337, 337, 337, 342, 1148, + 1149, 1150, 375, 376, 377, 379, 378, 337, + 337, 337, 337, 342, 1148, 1149, 1150, 380, + 337, 337, 337, 337, 342, 1148, 1149, 1150, + 382, 383, 384, 385, 337, 337, 337, 337, + 342, 1148, 1149, 1150, 387, 388, 389, 390, + 391, 392, 393, 337, 337, 337, 337, 342, + 1148, 1149, 1150, 395, 397, 396, 337, 337, + 337, 337, 342, 1148, 1149, 1150, 337, 337, + 337, 337, 342, 1148, 1149, 1150, 399, 401, + 403, 405, 400, 337, 337, 337, 337, 342, + 1148, 1149, 1150, 402, 337, 337, 337, 337, + 342, 1148, 1149, 1150, 404, 337, 337, 337, + 337, 342, 1148, 1149, 1150, 337, 337, 337, + 337, 342, 1148, 1149, 1150, 407, 411, 408, + 409, 410, 337, 337, 337, 337, 342, 1148, + 1149, 1150, 337, 337, 337, 337, 342, 1148, + 1149, 1150, 413, 417, 419, 414, 415, 416, + 337, 337, 337, 337, 342, 1148, 1149, 1150, + 418, 337, 337, 337, 337, 342, 1148, 1149, + 1150, 337, 337, 337, 420, 337, 342, 1148, + 1149, 1150, 421, 337, 337, 337, 422, 337, + 342, 1148, 1149, 1150, 337, 337, 337, 423, + 337, 342, 1148, 1149, 1150, 424, 425, 426, + 427, 337, 337, 337, 337, 342, 1148, 1149, + 1150, 429, 430, 337, 337, 337, 337, 342, + 1148, 1149, 1150, 432, 433, 437, 337, 337, + 337, 337, 342, 1148, 1149, 1150, 434, 435, + 436, 337, 337, 337, 337, 342, 1148, 1149, + 1150, 337, 337, 337, 337, 342, 1148, 1149, + 1150, 439, 441, 443, 445, 440, 337, 337, + 337, 337, 342, 1148, 1149, 1150, 442, 337, + 337, 337, 337, 342, 1148, 1149, 1150, 444, + 337, 337, 337, 337, 342, 1148, 1149, 1150, + 446, 447, 448, 337, 337, 337, 337, 342, + 1148, 1149, 1150, 450, 453, 455, 451, 452, + 337, 337, 337, 337, 342, 1148, 1149, 1150, + 454, 337, 337, 337, 337, 342, 1148, 1149, + 1150, 456, 457, 458, 337, 337, 337, 458, + 337, 342, 1148, 1149, 1150, 460, 461, 337, + 337, 337, 337, 342, 1148, 1149, 1150, 463, + 464, 465, 337, 337, 337, 337, 342, 1148, + 1149, 1150, 467, 337, 337, 337, 337, 342, + 1148, 1149, 1150, 0, 469, 469, 469, 469, + 476, 0, 470, 471, 471, 471, 470, 471, + 475, 0, 471, 471, 471, 472, 471, 475, + 473, 473, 473, 473, 473, 474, 1151, 1152, + 1153, 474, 473, 475, 471, 476, 469, 478, + 478, 478, 478, 486, 479, 485, 1154, 1154, + 1154, 485, 1155, 1154, 1158, 480, 480, 480, + 481, 480, 484, 482, 482, 482, 482, 482, + 483, 1155, 1156, 1157, 483, 482, 484, 480, + 480, 480, 480, 480, 484, 486, 478, 0, + 488, 489, 504, 543, 551, 564, 1159, 488, + 490, 491, 1159, 492, 1159, 493, 494, 495, + 496, 497, 498, 499, 500, 501, 502, 503, + 1159, 505, 534, 506, 512, 507, 508, 509, + 510, 511, 1159, 513, 514, 515, 516, 525, + 517, 518, 519, 520, 521, 522, 523, 524, + 1159, 526, 527, 528, 529, 530, 531, 532, + 533, 1159, 535, 540, 536, 537, 538, 539, + 1159, 541, 542, 1159, 544, 545, 546, 547, + 548, 549, 550, 1159, 552, 553, 554, 555, + 556, 557, 558, 561, 559, 560, 1159, 562, + 563, 1159, 565, 566, 567, 570, 568, 569, + 1159, 571, 572, 573, 585, 588, 1159, 574, + 575, 576, 577, 578, 579, 580, 581, 582, + 583, 584, 1159, 586, 587, 1159, 589, 590, + 1159, 0, 592, 593, 599, 616, 619, 625, + 629, 1160, 592, 594, 595, 596, 597, 598, + 1160, 600, 606, 612, 601, 602, 603, 604, + 605, 1160, 607, 609, 608, 1160, 610, 611, + 1160, 613, 614, 615, 1160, 617, 618, 1160, + 620, 622, 621, 1160, 623, 624, 1160, 626, + 627, 628, 1160, 630, 631, 1160, 0, 633, + 1161, 633, 635, 1162, 637, 638, 638, 638, + 638, 667, 638, 638, 638, 639, 638, 667, + 640, 640, 640, 640, 666, 0, 640, 640, + 640, 641, 640, 666, 642, 642, 642, 641, + 642, 665, 642, 642, 642, 643, 642, 665, + 0, 644, 644, 644, 643, 662, 662, 662, + 662, 662, 644, 661, 644, 644, 644, 645, + 644, 661, 646, 646, 646, 645, 658, 658, + 658, 658, 658, 646, 657, 646, 646, 646, + 647, 646, 657, 648, 648, 648, 647, 654, + 654, 654, 654, 654, 648, 653, 648, 648, + 648, 649, 648, 653, 1163, 649, 650, 650, + 650, 650, 650, 651, 651, 652, 652, 652, + 652, 652, 1163, 651, 653, 648, 655, 655, + 656, 656, 656, 656, 656, 648, 648, 648, + 655, 648, 653, 657, 646, 659, 659, 660, + 660, 660, 660, 660, 646, 646, 646, 659, + 646, 657, 661, 644, 663, 663, 664, 664, + 664, 664, 664, 644, 644, 644, 663, 644, + 661, 665, 642, 666, 640, 667, 638, 669, + 670, 670, 670, 670, 672, 671, 670, 670, + 670, 670, 672, 1164, 672, 670, 674, 675, + 675, 675, 675, 677, 675, 675, 675, 676, + 675, 677, 1165, 677, 675, 679, 680, 680, + 680, 679, 680, 682, 680, 680, 680, 681, + 680, 682, 1166, 682, 680, 684, 0, 685, + 685, 685, 685, 686, 1167, 1168, 1169, 684, + 685, 685, 685, 685, 686, 1167, 1168, 1169, + 686, 685, 688, 1170, 688, 0, 690, 691, + 691, 691, 690, 691, 743, 691, 691, 691, + 692, 696, 738, 691, 743, 693, 693, 693, + 692, 693, 742, 693, 693, 693, 694, 693, + 742, 695, 695, 695, 740, 694, 695, 739, + 695, 695, 695, 695, 739, 697, 697, 697, + 697, 737, 698, 699, 699, 699, 698, 699, + 736, 699, 699, 699, 700, 704, 731, 699, + 736, 701, 701, 701, 700, 701, 735, 701, + 701, 701, 702, 701, 735, 703, 703, 703, + 733, 702, 703, 732, 703, 703, 703, 703, + 732, 705, 705, 705, 705, 730, 706, 707, + 707, 708, 708, 708, 727, 707, 729, 708, + 726, 1171, 1178, 1179, 708, 708, 708, 709, + 708, 726, 1171, 1178, 1179, 710, 710, 710, + 723, 709, 725, 710, 722, 1171, 1176, 1177, + 710, 710, 710, 711, 710, 722, 1176, 1177, + 712, 712, 712, 719, 711, 721, 712, 718, + 1171, 1174, 1175, 712, 712, 712, 713, 712, + 718, 1174, 1175, 714, 714, 714, 716, 713, + 714, 715, 1171, 1172, 1173, 714, 714, 714, + 714, 715, 1172, 1173, 715, 714, 714, 714, + 714, 717, 714, 715, 1171, 1172, 1173, 717, + 718, 712, 712, 712, 712, 720, 721, 712, + 718, 1171, 1174, 1175, 720, 722, 710, 710, + 710, 710, 724, 725, 710, 722, 1171, 1176, + 1177, 724, 726, 708, 708, 708, 708, 728, + 729, 708, 726, 1171, 1178, 1179, 728, 730, + 705, 705, 705, 705, 705, 730, 732, 703, + 703, 703, 703, 734, 703, 732, 734, 735, + 701, 736, 699, 737, 697, 697, 697, 697, + 697, 737, 739, 695, 695, 695, 695, 741, + 695, 739, 741, 742, 693, 743, 691, 745, + 746, 746, 746, 745, 746, 754, 746, 746, + 746, 747, 746, 754, 748, 748, 748, 747, + 748, 753, 748, 748, 748, 749, 748, 753, + 750, 750, 750, 749, 750, 752, 750, 750, + 750, 751, 750, 752, 1180, 752, 750, 753, + 748, 754, 746, 756, 757, 757, 757, 756, + 757, 771, 757, 757, 757, 758, 757, 771, + 759, 759, 759, 758, 759, 770, 760, 759, + 759, 759, 759, 770, 761, 761, 761, 761, + 769, 762, 761, 761, 761, 761, 769, 763, + 763, 763, 763, 768, 764, 763, 763, 763, + 763, 768, 765, 765, 765, 765, 767, 765, + 765, 765, 766, 765, 767, 1181, 767, 765, + 768, 763, 769, 761, 770, 759, 771, 757, + 773, 774, 774, 774, 774, 782, 775, 776, + 776, 776, 775, 776, 781, 776, 776, 776, + 777, 776, 781, 778, 778, 778, 778, 780, + 779, 1182, 780, 778, 781, 776, 782, 774, + 0, 783, 784, 783, 783, 785, 791, 783, + 790, 1183, 1184, 1185, 785, 791, 786, 0, + 787, 787, 788, 789, 783, 783, 783, 789, + 783, 790, 1183, 1184, 1185, 790, 783, 792, + 793, 793, 788, 795, 796, 796, 796, 795, + 796, 806, 796, 796, 796, 797, 796, 806, + 798, 798, 798, 798, 805, 799, 800, 800, + 800, 799, 800, 804, 0, 800, 800, 800, + 801, 800, 804, 802, 802, 802, 802, 802, + 803, 1186, 1187, 1188, 803, 802, 804, 800, + 805, 798, 806, 796, 808, 809, 809, 809, + 808, 809, 816, 809, 809, 809, 810, 809, + 816, 811, 811, 811, 810, 811, 815, 811, + 811, 811, 812, 811, 815, 813, 813, 813, + 813, 813, 814, 1189, 1190, 1191, 814, 813, + 815, 811, 816, 809, 818, 819, 819, 819, + 818, 819, 855, 0, 819, 819, 819, 820, + 834, 841, 848, 819, 855, 821, 821, 821, + 821, 833, 0, 822, 823, 823, 823, 822, + 823, 832, 823, 823, 823, 824, 823, 832, + 0, 825, 825, 825, 828, 828, 828, 830, + 830, 830, 1192, 825, 827, 1195, 1196, 828, + 829, 1193, 1194, 830, 831, 1197, 1198, 0, + 826, 827, 825, 829, 828, 831, 830, 832, + 823, 833, 821, 835, 835, 835, 835, 840, + 836, 837, 837, 837, 836, 837, 839, 0, + 837, 837, 837, 838, 837, 839, 0, 838, + 825, 825, 825, 828, 828, 828, 830, 830, + 830, 1192, 825, 827, 1195, 1196, 828, 829, + 1193, 1194, 830, 831, 1197, 1198, 839, 837, + 840, 835, 842, 842, 842, 842, 847, 843, + 844, 844, 844, 843, 844, 846, 844, 844, + 844, 845, 844, 846, 845, 825, 825, 825, + 828, 828, 828, 830, 830, 830, 1192, 825, + 827, 1195, 1196, 828, 829, 1193, 1194, 830, + 831, 1197, 1198, 846, 844, 847, 842, 849, + 849, 849, 849, 854, 850, 851, 851, 851, + 850, 851, 853, 851, 851, 851, 852, 851, + 853, 825, 825, 825, 828, 828, 828, 830, + 830, 830, 1192, 825, 827, 1195, 1196, 828, + 829, 1193, 1194, 830, 831, 1197, 1198, 853, + 851, 854, 849, 855, 819, 0, 857, 891, + 908, 922, 929, 934, 942, 946, 954, 960, + 976, 979, 986, 997, 1007, 858, 858, 858, + 882, 885, 889, 858, 881, 858, 858, 858, + 859, 858, 881, 860, 860, 860, 860, 880, + 861, 862, 862, 862, 861, 862, 879, 862, + 862, 862, 863, 862, 879, 864, 864, 864, + 863, 864, 878, 0, 864, 864, 864, 865, + 864, 878, 866, 866, 866, 865, 866, 877, + 866, 866, 866, 867, 866, 877, 868, 868, + 868, 867, 868, 876, 868, 868, 868, 869, + 868, 876, 870, 870, 870, 869, 870, 875, + 870, 870, 870, 871, 870, 875, 872, 872, + 872, 872, 874, 872, 872, 872, 873, 872, + 874, 1199, 874, 872, 875, 870, 876, 868, + 877, 866, 878, 864, 879, 862, 880, 860, + 881, 858, 883, 884, 858, 858, 858, 858, + 881, 886, 887, 888, 858, 858, 858, 858, + 881, 890, 858, 858, 858, 858, 881, 892, + 894, 901, 904, 893, 858, 858, 858, 858, + 881, 895, 900, 896, 897, 898, 899, 858, + 858, 858, 858, 881, 858, 858, 858, 858, + 881, 902, 903, 858, 858, 858, 858, 881, + 905, 906, 907, 858, 858, 858, 858, 881, + 909, 913, 921, 910, 911, 912, 858, 858, + 858, 858, 881, 914, 917, 915, 916, 858, + 858, 858, 858, 881, 918, 919, 920, 858, + 858, 858, 858, 881, 858, 858, 858, 858, + 881, 923, 924, 925, 927, 926, 858, 858, + 858, 858, 881, 928, 858, 858, 858, 858, + 881, 930, 931, 932, 933, 858, 858, 858, + 858, 881, 935, 936, 937, 938, 939, 940, + 941, 858, 858, 858, 858, 881, 943, 945, + 944, 858, 858, 858, 858, 881, 858, 858, + 858, 858, 881, 947, 949, 951, 953, 948, + 858, 858, 858, 858, 881, 950, 858, 858, + 858, 858, 881, 952, 858, 858, 858, 858, + 881, 858, 858, 858, 858, 881, 955, 959, + 956, 957, 958, 858, 858, 858, 858, 881, + 858, 858, 858, 858, 881, 961, 965, 967, + 962, 963, 964, 858, 858, 858, 858, 881, + 966, 858, 858, 858, 858, 881, 858, 858, + 858, 968, 858, 881, 969, 858, 858, 858, + 970, 858, 881, 858, 858, 858, 971, 858, + 881, 972, 973, 974, 975, 858, 858, 858, + 858, 881, 977, 978, 858, 858, 858, 858, + 881, 980, 981, 985, 858, 858, 858, 858, + 881, 982, 983, 984, 858, 858, 858, 858, + 881, 858, 858, 858, 858, 881, 987, 989, + 991, 993, 988, 858, 858, 858, 858, 881, + 990, 858, 858, 858, 858, 881, 992, 858, + 858, 858, 858, 881, 994, 995, 996, 858, + 858, 858, 858, 881, 998, 1001, 1003, 999, + 1000, 858, 858, 858, 858, 881, 1002, 858, + 858, 858, 858, 881, 1004, 1005, 0, 1006, + 858, 858, 858, 1006, 858, 881, 1008, 1009, + 858, 858, 858, 858, 881, 1011, 1012, 1200, + 1014, 1015, 1015, 1015, 1014, 1015, 1023, 1015, + 1015, 1015, 1016, 1015, 1023, 1017, 1017, 1017, + 1016, 1017, 1022, 1017, 1017, 1017, 1018, 1017, + 1022, 1019, 1019, 1019, 1019, 1021, 1020, 1201, + 1021, 1019, 1022, 1017, 1023, 1015, 1025, 1202, + 1027, 1028, 1028, 1028, 1027, 1028, 1054, 1028, + 1028, 1028, 1029, 1028, 1054, 1030, 1030, 1030, + 1029, 1030, 1053, 1030, 1030, 1030, 1031, 1030, + 1053, 1032, 1032, 1032, 1031, 1032, 1052, 1032, + 1032, 1032, 1033, 1050, 1032, 1052, 1034, 1034, + 1034, 1034, 1049, 0, 1034, 1034, 1034, 1035, + 1034, 1049, 1036, 1037, 1047, 1038, 1039, 1046, + 1040, 1044, 1041, 1042, 1042, 1043, 1035, 1203, + 1045, 1048, 1049, 1034, 1051, 1050, 1052, 1032, + 1053, 1030, 1054, 1028, 1056, 1057, 1057, 1057, + 1056, 1057, 1067, 1057, 1057, 1057, 1058, 1057, + 1067, 1059, 1059, 1059, 1058, 1059, 1066, 1059, + 1059, 1059, 1060, 1059, 1066, 1061, 1061, 1061, + 1060, 1061, 1065, 1061, 1061, 1061, 1062, 1063, + 1061, 1065, 1204, 1064, 1063, 1065, 1061, 1066, + 1059, 1067, 1057, 1069, 1070, 1070, 1070, 1069, + 1070, 1080, 1070, 1070, 1070, 1071, 1070, 1080, + 1072, 1072, 1072, 1071, 1072, 1079, 1072, 1072, + 1072, 1073, 1072, 1079, 1074, 1074, 1074, 1073, + 1074, 1078, 1074, 1074, 1074, 1075, 1074, 1078, + 1076, 1076, 1076, 1076, 1076, 1077, 1205, 1206, + 1207, 1077, 1076, 1078, 1074, 1079, 1072, 1080, + 1070, 1082, 1083, 1083, 1083, 1082, 1083, 1085, + 1083, 1083, 1083, 1084, 1083, 1085, 1208, 1084, + 1085, 1083, 1087, 1088, 1088, 1088, 1087, 1088, + 1098, 1088, 1088, 1088, 1089, 1088, 1098, 1090, + 1091, 1092, 0, 1093, 1094, 1095, 1096, 1097, + 0, 1209, 1098, 1088, 1100, 1101, 0, 1102, + 1103, 1104, 1210, 1106, 1107, 1108, 1109, 1110, + 1211, 1112, 1113, 1113, 1113, 1112, 1113, 1118, + 1113, 1113, 1113, 1114, 1113, 1118, 1115, 1115, + 1115, 1114, 1115, 1117, 1116, 1115, 1115, 1115, + 1115, 1117, 1212, 1117, 1115, 1118, 1113, 1120, + 1121, 1121, 1121, 1120, 1121, 1126, 1122, 1121, + 1121, 1121, 1121, 1126, 1123, 1123, 1123, 1123, + 1125, 1124, 1123, 1123, 1123, 1123, 1125, 1213, + 1125, 1123, 1126, 1121, 0, 1, 152, 1, + 1, 160, 1127, 151, 1132, 264, 14, 194, + 14, 14, 204, 224, 1135, 239, 1133, 240, + 1136, 242, 0, 142, 142, 142, 170, 250, + 251, 252, 253, 266, 255, 256, 257, 258, + 259, 260, 261, 262, 263, 1130, 265, 0, + 163, 170, 250, 251, 252, 253, 254, 255, + 256, 257, 258, 259, 260, 261, 262, 263, + 0, 1, 152, 1, 1, 160, 1127, 151, + 1132, 264, 1, 1, 1132, 264, 174, 174, + 174, 1132, 175, 178, 178, 178, 193, 228, + 0, 205, 208, 210, 211, 212, 213, 214, + 215, 216, 217, 218, 219, 220, 221, 222, + 223, 1132 +}; + +static const short _zone_scanner_trans_actions[] = { + 2399, 0, 5, 7, 561, 720, 720, 720, + 720, 720, 720, 720, 720, 720, 720, 720, + 720, 720, 720, 720, 3, 9, 3, 9, + 2555, 101, 738, 741, 43, 57, 55, 53, + 0, 59, 735, 744, 361, 0, 5, 7, + 95, 95, 95, 95, 95, 95, 95, 95, + 95, 95, 95, 95, 95, 95, 95, 3, + 9, 2007, 2010, 3007, 3011, 0, 0, 0, + 3003, 3015, 3023, 4731, 4751, 355, 0, 353, + 5, 7, 359, 3, 9, 369, 396, 1992, + 13, 417, 420, 2271, 423, 15, 0, 5, + 7, 2136, 9, 11, 2243, 2001, 357, 11, + 411, 13, 378, 384, 393, 4726, 2983, 2223, + 0, 5, 7, 9, 2991, 1998, 396, 11, + 2239, 0, 4031, 5, 7, 2387, 2679, 2679, + 2679, 2679, 2679, 2679, 2679, 2679, 2679, 2679, + 2679, 2679, 2679, 2679, 2679, 3, 9, 1995, + 1998, 369, 396, 5292, 747, 2683, 2687, 4246, + 2691, 2004, 0, 5, 7, 372, 9, 0, + 0, 0, 0, 0, 2133, 3831, 3835, 3827, + 3839, 3843, 5156, 5161, 408, 2227, 2231, 2235, + 11, 399, 402, 405, 0, 0, 0, 0, + 0, 0, 2106, 3651, 3655, 3647, 3659, 3663, + 5066, 5071, 2103, 3631, 3635, 3627, 3639, 3643, + 5056, 5061, 0, 0, 2064, 3371, 3375, 3367, + 3379, 3383, 4926, 4931, 0, 0, 0, 2016, + 3051, 3055, 3047, 3059, 3063, 4766, 4771, 0, + 0, 0, 0, 0, 0, 2091, 3551, 3555, + 3547, 3559, 3563, 5016, 5021, 0, 0, 0, + 0, 2067, 3391, 3395, 3387, 3399, 3403, 4936, + 4941, 0, 0, 0, 2088, 3531, 3535, 3527, + 3539, 3543, 5006, 5011, 2073, 3431, 3435, 3427, + 3439, 3443, 4956, 4961, 0, 0, 0, 0, + 0, 2124, 3771, 3775, 3767, 3779, 3783, 5126, + 5131, 0, 2127, 3791, 3795, 3787, 3799, 3803, + 5136, 5141, 0, 0, 0, 0, 2025, 3111, + 3115, 3107, 3119, 3123, 4796, 4801, 0, 0, + 99, 726, 729, 723, 732, 0, 5, 7, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 3, + 9, 0, 0, 0, 0, 0, 0, 2079, + 3471, 3475, 3467, 3479, 3483, 4976, 4981, 0, + 0, 0, 2046, 3251, 3255, 3247, 3259, 3263, + 4866, 4871, 2061, 3351, 3355, 3347, 3359, 3363, + 4916, 4921, 0, 0, 0, 0, 0, 2115, + 3711, 3715, 3707, 3719, 3723, 5096, 5101, 0, + 2118, 3731, 3735, 3727, 3739, 3743, 5106, 5111, + 0, 2052, 3291, 3295, 3287, 3299, 3303, 4886, + 4891, 2121, 3751, 3755, 3747, 3759, 3763, 5116, + 5121, 0, 0, 0, 0, 0, 2028, 3131, + 3135, 3127, 3139, 3143, 4806, 4811, 2031, 3151, + 3155, 3147, 3159, 3163, 4816, 4821, 0, 0, + 0, 0, 0, 0, 2058, 3331, 3335, 3327, + 3339, 3343, 4906, 4911, 0, 2112, 3691, 3695, + 3687, 3699, 3703, 5086, 5091, 2013, 3031, 3035, + 0, 3027, 3039, 3043, 4756, 4761, 0, 2085, + 3511, 3515, 0, 3507, 3519, 3523, 4996, 5001, + 2094, 3571, 3575, 0, 3567, 3579, 3583, 5026, + 5031, 0, 0, 0, 0, 2097, 3591, 3595, + 3587, 3599, 3603, 5036, 5041, 0, 0, 2022, + 3091, 3095, 3087, 3099, 3103, 4786, 4791, 0, + 0, 0, 2037, 3191, 3195, 3187, 3199, 3203, + 4836, 4841, 0, 0, 0, 2082, 3491, 3495, + 3487, 3499, 3503, 4986, 4991, 2043, 3231, 3235, + 3227, 3239, 3243, 4856, 4861, 0, 0, 0, + 0, 0, 2019, 3071, 3075, 3067, 3079, 3083, + 4776, 4781, 0, 2109, 3671, 3675, 3667, 3679, + 3683, 5076, 5081, 0, 2055, 3311, 3315, 3307, + 3319, 3323, 4896, 4901, 0, 0, 0, 2076, + 3451, 3455, 3447, 3459, 3463, 4966, 4971, 0, + 0, 0, 0, 0, 2100, 3611, 3615, 3607, + 3619, 3623, 5046, 5051, 0, 2034, 3171, 3175, + 3167, 3179, 3183, 4826, 4831, 0, 0, 573, + 561, 2403, 639, 2531, 2535, 43, 2527, 2539, + 2543, 4191, 4196, 0, 0, 2130, 3811, 3815, + 3807, 3819, 3823, 5146, 5151, 11, 411, 0, + 5, 7, 3, 9, 0, 99, 726, 729, + 723, 732, 0, 5, 7, 561, 97, 97, + 97, 97, 97, 97, 97, 97, 97, 97, + 97, 97, 97, 97, 97, 3, 9, 101, + 738, 741, 43, 57, 55, 53, 0, 59, + 735, 744, 2559, 43, 57, 55, 53, 0, + 59, 660, 2599, 2603, 4206, 2595, 2607, 11, + 411, 11, 411, 93, 89, 89, 89, 0, + 0, 0, 0, 0, 0, 87, 39, 37, + 510, 513, 507, 516, 0, 5, 7, 3, + 9, 11, 0, 411, 4016, 522, 2315, 2323, + 2307, 2331, 0, 5, 7, 3, 9, 11, + 411, 2559, 43, 57, 55, 53, 0, 59, + 660, 2599, 2603, 4206, 2595, 2607, 11, 411, + 2375, 2343, 3971, 3986, 3956, 4001, 5178, 5202, + 0, 5, 7, 3, 9, 369, 396, 2999, + 3019, 4736, 4741, 5322, 4746, 5431, 5334, 5424, + 5328, 0, 5, 7, 372, 2179, 0, 5, + 7, 372, 9, 11, 2239, 426, 2247, 2255, + 3896, 2263, 11, 375, 381, 390, 0, 5, + 7, 9, 396, 11, 2239, 2975, 0, 0, + 0, 0, 0, 81, 0, 0, 77, 2367, + 519, 2311, 2319, 3946, 2327, 5361, 2335, 3961, + 3976, 5172, 3991, 0, 5, 7, 372, 9, + 11, 2239, 4021, 4006, 5184, 5190, 5347, 5196, + 5438, 5354, 0, 5, 7, 372, 2179, 2339, + 531, 3966, 3981, 3951, 3996, 13, 378, 384, + 2155, 393, 408, 3871, 2227, 2231, 2235, 11, + 399, 402, 405, 408, 2227, 2231, 2235, 11, + 399, 402, 405, 0, 0, 2049, 3271, 3275, + 3267, 3279, 3283, 4876, 4881, 0, 0, 0, + 2040, 3211, 3215, 3207, 3219, 3223, 4846, 4851, + 0, 2070, 3411, 3415, 3407, 3419, 3423, 4946, + 4951, 11, 411, 426, 2247, 2255, 3906, 11, + 375, 381, 3847, 408, 2227, 2231, 11, 399, + 402, 408, 2227, 2231, 11, 399, 402, 11, + 411, 13, 378, 384, 393, 2223, 408, 2227, + 2231, 11, 399, 402, 534, 11, 411, 11, + 411, 11, 2239, 438, 2275, 17, 441, 31, + 2299, 29, 0, 474, 2279, 21, 23, 0, + 465, 444, 19, 447, 456, 25, 25, 3916, + 450, 453, 27, 471, 684, 65, 0, 0, + 1, 681, 2615, 687, 69, 69, 690, 693, + 2611, 71, 65, 0, 0, 67, 687, 69, + 69, 693, 71, 71, 75, 0, 5, 7, + 3, 9, 564, 561, 4201, 13, 417, 420, + 43, 57, 55, 53, 0, 59, 3901, 423, + 432, 0, 5, 7, 2623, 9, 11, 3881, + 2559, 2547, 43, 57, 55, 53, 0, 59, + 654, 2563, 2567, 4206, 5298, 2571, 11, 411, + 79, 0, 5, 7, 3, 9, 4211, 702, + 2627, 21, 23, 0, 435, 468, 2283, 2287, + 444, 19, 5166, 2291, 0, 5, 7, 2631, + 9, 11, 3886, 447, 459, 25, 25, 450, + 453, 27, 11, 411, 0, 5, 7, 3, + 9, 705, 83, 711, 708, 2635, 2639, 5304, + 2647, 2643, 717, 0, 5, 7, 4216, 714, + 2651, 2655, 9, 9, 85, 21, 23, 0, + 2295, 3921, 3926, 444, 19, 5340, 3931, 0, + 5, 7, 11, 3891, 447, 462, 25, 25, + 450, 453, 27, 11, 3876, 0, 5, 7, + 3, 9, 11, 411, 11, 411, 125, 117, + 119, 121, 0, 123, 0, 5, 7, 3, + 9, 1, 366, 387, 11, 411, 325, 323, + 1896, 1899, 1893, 1902, 2947, 4716, 4721, 0, + 5, 7, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 3, 9, 1905, 2171, 2215, 239, 1392, + 1395, 0, 0, 0, 1389, 1398, 2779, 4296, + 4301, 0, 0, 265, 1548, 1551, 1545, 1554, + 2831, 4426, 4431, 11, 411, 0, 0, 0, + 0, 0, 321, 1884, 1887, 1881, 1890, 2943, + 4706, 4711, 0, 0, 0, 0, 0, 0, + 303, 1776, 1779, 1773, 1782, 2907, 4616, 4621, + 301, 1764, 1767, 1761, 1770, 2903, 4606, 4611, + 0, 0, 275, 1608, 1611, 1605, 1614, 2851, + 4476, 4481, 0, 0, 0, 243, 1416, 1419, + 1413, 1422, 2787, 4316, 4321, 0, 0, 0, + 0, 0, 0, 293, 1716, 1719, 1713, 1722, + 2887, 4566, 4571, 0, 0, 0, 0, 277, + 1620, 1623, 1617, 1626, 2855, 4486, 4491, 0, + 0, 0, 291, 1704, 1707, 1701, 1710, 2883, + 4556, 4561, 281, 1644, 1647, 1641, 1650, 2863, + 4506, 4511, 0, 0, 0, 0, 0, 315, + 1848, 1851, 1845, 1854, 2931, 4676, 4681, 0, + 317, 1860, 1863, 1857, 1866, 2935, 4686, 4691, + 0, 0, 0, 0, 249, 1452, 1455, 1449, + 1458, 2799, 4346, 4351, 0, 0, 0, 0, + 0, 0, 0, 285, 1668, 1671, 1665, 1674, + 2871, 4526, 4531, 0, 0, 0, 263, 1536, + 1539, 1533, 1542, 2827, 4416, 4421, 273, 1596, + 1599, 1593, 1602, 2847, 4466, 4471, 0, 0, + 0, 0, 0, 309, 1812, 1815, 1809, 1818, + 2919, 4646, 4651, 0, 311, 1824, 1827, 1821, + 1830, 2923, 4656, 4661, 0, 267, 1560, 1563, + 1557, 1566, 2835, 4436, 4441, 313, 1836, 1839, + 1833, 1842, 2927, 4666, 4671, 0, 0, 0, + 0, 0, 251, 1464, 1467, 1461, 1470, 2803, + 4356, 4361, 253, 1476, 1479, 1473, 1482, 2807, + 4366, 4371, 0, 0, 0, 0, 0, 0, + 271, 1584, 1587, 1581, 1590, 2843, 4456, 4461, + 0, 307, 1800, 1803, 1797, 1806, 2915, 4636, + 4641, 241, 1404, 1407, 0, 1401, 1410, 2783, + 4306, 4311, 0, 289, 1692, 1695, 0, 1689, + 1698, 2879, 4546, 4551, 295, 1728, 1731, 0, + 1725, 1734, 2891, 4576, 4581, 0, 0, 0, + 0, 297, 1740, 1743, 1737, 1746, 2895, 4586, + 4591, 0, 0, 247, 1440, 1443, 1437, 1446, + 2795, 4336, 4341, 0, 0, 0, 257, 1500, + 1503, 1497, 1506, 2815, 4386, 4391, 0, 0, + 0, 287, 1680, 1683, 1677, 1686, 2875, 4536, + 4541, 261, 1524, 1527, 1521, 1530, 2823, 4406, + 4411, 0, 0, 0, 0, 0, 245, 1428, + 1431, 1425, 1434, 2791, 4326, 4331, 0, 305, + 1788, 1791, 1785, 1794, 2911, 4626, 4631, 0, + 269, 1572, 1575, 1569, 1578, 2839, 4446, 4451, + 0, 0, 0, 283, 1656, 1659, 1653, 1662, + 2867, 4516, 4521, 0, 0, 0, 0, 0, + 299, 1752, 1755, 1749, 1758, 2899, 4596, 4601, + 0, 255, 1488, 1491, 1485, 1494, 2811, 4376, + 4381, 0, 0, 561, 237, 1380, 1383, 43, + 1377, 1386, 2775, 4286, 4291, 0, 0, 319, + 1872, 1875, 1869, 1878, 2939, 4696, 4701, 0, + 0, 0, 259, 1512, 1515, 1509, 1518, 2819, + 4396, 4401, 0, 279, 1632, 1635, 1629, 1638, + 2859, 4496, 4501, 339, 0, 5, 7, 3, + 9, 567, 561, 51, 645, 648, 43, 642, + 651, 2763, 0, 5, 7, 113, 3, 9, + 115, 0, 5, 7, 3, 9, 780, 2167, + 2211, 11, 411, 11, 411, 11, 411, 0, + 5, 7, 3, 9, 561, 561, 363, 2143, + 2147, 43, 1, 2139, 2151, 0, 5, 7, + 113, 3, 9, 115, 0, 5, 7, 3, + 9, 780, 2167, 2211, 11, 411, 11, 411, + 51, 645, 648, 642, 651, 11, 411, 345, + 561, 0, 0, 0, 0, 0, 594, 43, + 0, 0, 789, 0, 792, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 798, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 810, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 813, 0, 0, 0, 0, 0, 0, 0, + 0, 816, 0, 0, 0, 0, 0, 0, + 819, 0, 0, 822, 0, 0, 0, 0, + 0, 0, 0, 825, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 828, 0, + 0, 831, 0, 0, 0, 0, 0, 0, + 786, 0, 0, 0, 0, 0, 795, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 801, 0, 0, 804, 0, 0, + 807, 347, 561, 0, 0, 0, 0, 0, + 0, 609, 43, 0, 0, 0, 0, 0, + 852, 0, 0, 0, 0, 0, 0, 0, + 0, 855, 0, 0, 0, 849, 0, 0, + 843, 0, 0, 0, 846, 0, 0, 861, + 0, 0, 0, 840, 0, 0, 834, 0, + 0, 0, 837, 0, 0, 858, 753, 750, + 2703, 103, 537, 540, 537, 41, 546, 549, + 543, 552, 0, 5, 7, 537, 3, 9, + 41, 546, 549, 543, 552, 570, 0, 5, + 7, 561, 3, 9, 49, 630, 633, 43, + 627, 636, 0, 5, 7, 561, 3, 9, + 2551, 49, 630, 633, 43, 57, 55, 53, + 0, 59, 627, 636, 0, 5, 7, 561, + 3, 9, 49, 630, 633, 43, 57, 55, + 53, 0, 59, 627, 636, 0, 5, 7, + 561, 3, 9, 49, 630, 633, 43, 57, + 55, 53, 0, 59, 627, 636, 0, 5, + 7, 561, 3, 9, 624, 43, 57, 55, + 53, 0, 59, 2559, 43, 57, 55, 53, + 0, 59, 2575, 4206, 11, 411, 2559, 43, + 57, 55, 53, 0, 59, 657, 2583, 2587, + 4206, 2579, 2591, 11, 411, 2559, 43, 57, + 55, 53, 0, 59, 657, 2583, 2587, 4206, + 2579, 2591, 11, 411, 2559, 43, 57, 55, + 53, 0, 59, 657, 2583, 2587, 4206, 2579, + 2591, 11, 411, 11, 411, 11, 411, 477, + 35, 492, 495, 489, 498, 477, 0, 5, + 7, 3, 9, 486, 11, 411, 537, 41, + 546, 549, 543, 552, 0, 5, 7, 537, + 3, 9, 540, 11, 411, 561, 47, 615, + 618, 43, 612, 621, 0, 5, 7, 537, + 3, 9, 540, 11, 411, 2619, 699, 35, + 492, 495, 489, 498, 2303, 3936, 3941, 477, + 0, 5, 7, 3, 9, 696, 2163, 2207, + 11, 411, 750, 2723, 103, 1983, 2951, 327, + 1911, 1914, 43, 1908, 1917, 0, 5, 7, + 561, 0, 0, 3, 9, 331, 1935, 1938, + 43, 1932, 1941, 0, 5, 7, 4186, 3, + 9, 576, 2435, 2439, 0, 43, 2431, 2443, + 0, 5, 7, 3, 9, 0, 5, 7, + 3, 9, 561, 329, 1923, 1926, 43, 1920, + 1929, 0, 5, 7, 561, 0, 0, 3, + 9, 333, 1947, 1950, 43, 1944, 1953, 0, + 5, 7, 4186, 3, 9, 579, 2451, 2455, + 0, 43, 2447, 2459, 0, 5, 7, 3, + 9, 0, 5, 7, 3, 9, 0, 4181, + 5316, 582, 2467, 2471, 0, 43, 582, 2463, + 2475, 4161, 5244, 5250, 0, 5, 7, 4181, + 3, 9, 1980, 2175, 2219, 585, 2483, 2487, + 0, 43, 585, 2479, 2491, 4166, 5256, 5262, + 0, 5, 7, 4181, 3, 9, 2175, 2219, + 588, 2499, 2503, 0, 43, 588, 2495, 2507, + 4171, 5268, 5274, 0, 5, 7, 4181, 3, + 9, 2175, 2219, 591, 2515, 2519, 0, 43, + 2511, 2523, 4176, 5280, 5286, 0, 5, 7, + 3, 9, 2175, 2219, 11, 411, 2427, 4146, + 4151, 4036, 4141, 4156, 5238, 5410, 5417, 558, + 11, 411, 2423, 4126, 4131, 4036, 2423, 4121, + 4136, 5232, 5396, 5403, 558, 11, 411, 2419, + 4106, 4111, 4036, 2419, 4101, 4116, 5226, 5382, + 5389, 558, 11, 411, 2415, 4086, 4091, 4036, + 2415, 4081, 4096, 5220, 5368, 5375, 558, 11, + 411, 337, 1971, 1974, 1968, 1977, 11, 411, + 2411, 4066, 4071, 4036, 4061, 4076, 558, 11, + 411, 11, 411, 11, 411, 335, 1959, 1962, + 1956, 1965, 11, 411, 2407, 4046, 4051, 4036, + 4041, 4056, 558, 11, 411, 11, 411, 561, + 47, 615, 618, 43, 612, 621, 0, 5, + 7, 561, 3, 9, 47, 615, 618, 43, + 612, 621, 0, 5, 7, 561, 3, 9, + 47, 615, 618, 43, 612, 621, 0, 5, + 7, 537, 3, 9, 540, 11, 411, 11, + 411, 11, 411, 561, 47, 615, 618, 43, + 612, 621, 0, 5, 7, 561, 3, 9, + 47, 615, 618, 43, 612, 621, 477, 0, + 5, 7, 3, 9, 35, 492, 495, 489, + 498, 477, 0, 5, 7, 3, 9, 35, + 492, 495, 489, 498, 477, 0, 5, 7, + 3, 9, 35, 492, 495, 489, 498, 0, + 5, 7, 537, 3, 9, 540, 11, 411, + 11, 411, 11, 411, 11, 411, 11, 411, + 351, 0, 5, 7, 3, 9, 561, 47, + 615, 618, 43, 612, 621, 0, 5, 7, + 349, 3, 9, 0, 5, 7, 3, 9, + 127, 1, 11, 411, 11, 411, 11, 411, + 774, 0, 762, 5, 7, 765, 768, 3, + 9, 1, 366, 387, 109, 111, 0, 2695, + 750, 103, 105, 561, 771, 2751, 2755, 43, + 2747, 2759, 2743, 4276, 4281, 11, 411, 0, + 750, 103, 107, 561, 47, 615, 618, 43, + 612, 621, 0, 5, 7, 349, 3, 9, + 0, 5, 7, 3, 9, 561, 45, 600, + 603, 43, 597, 606, 777, 0, 5, 7, + 113, 3, 9, 115, 0, 5, 7, 3, + 9, 1, 366, 387, 11, 411, 11, 411, + 11, 411, 11, 411, 561, 45, 600, 603, + 43, 597, 606, 0, 5, 7, 561, 3, + 9, 45, 600, 603, 43, 597, 606, 0, + 5, 7, 113, 3, 9, 115, 0, 5, + 7, 3, 9, 1, 366, 387, 11, 411, + 11, 411, 11, 411, 561, 45, 600, 603, + 43, 597, 606, 864, 0, 5, 7, 145, + 147, 149, 151, 3, 9, 0, 5, 7, + 3, 9, 2391, 561, 45, 600, 603, 43, + 597, 606, 0, 5, 7, 0, 3, 9, + 2771, 0, 5, 7, 0, 5, 7, 0, + 5, 7, 1, 3, 9, 366, 387, 3, + 9, 366, 387, 3, 9, 366, 387, 867, + 127, 11, 411, 11, 411, 11, 411, 11, + 411, 11, 411, 0, 5, 7, 3, 9, + 561, 45, 600, 603, 43, 597, 606, 2699, + 0, 5, 7, 750, 3, 9, 4251, 103, + 756, 2711, 2715, 756, 2711, 2715, 756, 2711, + 2715, 2703, 2707, 2719, 4256, 4261, 2707, 2719, + 4256, 4261, 2707, 2719, 4256, 4261, 11, 411, + 11, 411, 0, 5, 7, 3, 9, 561, + 45, 600, 603, 43, 597, 606, 0, 5, + 7, 750, 3, 9, 103, 759, 2731, 2735, + 759, 2731, 2735, 759, 2731, 2735, 2723, 2727, + 2739, 4266, 4271, 2727, 2739, 4266, 4271, 2727, + 2739, 4266, 4271, 11, 411, 11, 411, 0, + 5, 7, 3, 9, 561, 45, 600, 603, + 43, 597, 606, 0, 5, 7, 537, 3, + 9, 41, 546, 549, 41, 546, 549, 41, + 546, 549, 540, 543, 552, 2379, 2383, 543, + 552, 2379, 2383, 543, 552, 2379, 2383, 11, + 411, 11, 411, 11, 411, 870, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 153, 876, 879, + 0, 0, 0, 873, 882, 0, 5, 7, + 349, 3, 9, 0, 5, 7, 3, 9, + 561, 45, 600, 603, 43, 597, 606, 0, + 5, 7, 561, 3, 9, 49, 630, 633, + 43, 627, 636, 678, 0, 5, 7, 663, + 3, 9, 63, 669, 672, 61, 666, 675, + 0, 5, 7, 663, 3, 9, 63, 669, + 672, 61, 666, 675, 0, 5, 7, 561, + 3, 9, 47, 615, 618, 43, 612, 621, + 0, 5, 7, 537, 3, 9, 41, 546, + 549, 543, 552, 0, 5, 7, 127, 3, + 9, 1, 11, 411, 11, 411, 11, 411, + 11, 411, 11, 411, 11, 411, 11, 411, + 11, 411, 0, 0, 179, 1032, 1035, 1029, + 1038, 0, 0, 0, 173, 996, 999, 993, + 1002, 0, 193, 1116, 1119, 1113, 1122, 0, + 0, 0, 0, 0, 235, 1368, 1371, 1365, + 1374, 0, 0, 0, 0, 0, 0, 217, + 1260, 1263, 1257, 1266, 215, 1248, 1251, 1245, + 1254, 0, 0, 189, 1092, 1095, 1089, 1098, + 0, 0, 0, 157, 900, 903, 897, 906, + 0, 0, 0, 0, 0, 0, 207, 1200, + 1203, 1197, 1206, 0, 0, 0, 0, 191, + 1104, 1107, 1101, 1110, 0, 0, 0, 205, + 1188, 1191, 1185, 1194, 195, 1128, 1131, 1125, + 1134, 0, 0, 0, 0, 0, 229, 1332, + 1335, 1329, 1338, 0, 231, 1344, 1347, 1341, + 1350, 0, 0, 0, 0, 163, 936, 939, + 933, 942, 0, 0, 0, 0, 0, 0, + 0, 199, 1152, 1155, 1149, 1158, 0, 0, + 0, 177, 1020, 1023, 1017, 1026, 187, 1080, + 1083, 1077, 1086, 0, 0, 0, 0, 0, + 223, 1296, 1299, 1293, 1302, 0, 225, 1308, + 1311, 1305, 1314, 0, 181, 1044, 1047, 1041, + 1050, 227, 1320, 1323, 1317, 1326, 0, 0, + 0, 0, 0, 165, 948, 951, 945, 954, + 167, 960, 963, 957, 966, 0, 0, 0, + 0, 0, 0, 185, 1068, 1071, 1065, 1074, + 0, 221, 1284, 1287, 1281, 1290, 155, 888, + 891, 0, 885, 894, 0, 203, 1176, 1179, + 0, 1173, 1182, 209, 1212, 1215, 0, 1209, + 1218, 0, 0, 0, 0, 211, 1224, 1227, + 1221, 1230, 0, 0, 161, 924, 927, 921, + 930, 0, 0, 0, 171, 984, 987, 981, + 990, 0, 0, 0, 201, 1164, 1167, 1161, + 1170, 175, 1008, 1011, 1005, 1014, 0, 0, + 0, 0, 0, 159, 912, 915, 909, 918, + 0, 219, 1272, 1275, 1269, 1278, 0, 183, + 1056, 1059, 1053, 1062, 0, 0, 0, 197, + 1140, 1143, 1137, 1146, 0, 0, 0, 0, + 0, 213, 1236, 1239, 1233, 1242, 0, 169, + 972, 975, 969, 978, 0, 0, 2395, 561, + 47, 615, 618, 43, 612, 621, 0, 0, + 233, 1356, 1359, 1353, 1362, 537, 555, 1, + 561, 47, 615, 618, 43, 612, 621, 0, + 5, 7, 561, 3, 9, 45, 600, 603, + 43, 597, 606, 0, 5, 7, 349, 3, + 9, 0, 5, 7, 3, 9, 127, 1, + 11, 411, 11, 411, 11, 411, 127, 1, + 561, 45, 600, 603, 43, 597, 606, 0, + 5, 7, 561, 3, 9, 45, 600, 603, + 43, 597, 606, 0, 5, 7, 561, 3, + 9, 47, 615, 618, 43, 612, 621, 0, + 5, 7, 33, 480, 3, 9, 35, 492, + 495, 489, 498, 783, 0, 5, 7, 483, + 3, 9, 131, 133, 0, 135, 137, 0, + 139, 0, 141, 143, 0, 501, 129, 1, + 0, 0, 11, 411, 115, 113, 11, 411, + 11, 411, 11, 411, 561, 45, 600, 603, + 43, 597, 606, 0, 5, 7, 561, 3, + 9, 45, 600, 603, 43, 597, 606, 0, + 5, 7, 561, 3, 9, 47, 615, 618, + 43, 612, 621, 0, 5, 7, 33, 480, + 3, 9, 486, 115, 113, 11, 411, 11, + 411, 11, 411, 561, 45, 600, 603, 43, + 597, 606, 0, 5, 7, 561, 3, 9, + 45, 600, 603, 43, 597, 606, 0, 5, + 7, 561, 3, 9, 45, 600, 603, 43, + 597, 606, 0, 5, 7, 113, 3, 9, + 115, 0, 5, 7, 3, 9, 1, 366, + 387, 11, 411, 11, 411, 11, 411, 11, + 411, 561, 47, 615, 618, 43, 612, 621, + 0, 5, 7, 750, 3, 9, 2703, 103, + 11, 411, 561, 47, 615, 618, 43, 612, + 621, 0, 5, 7, 1989, 3, 9, 115, + 113, 115, 2767, 343, 113, 115, 113, 115, + 2971, 2967, 11, 411, 1986, 115, 2963, 341, + 113, 115, 2955, 1986, 115, 341, 113, 115, + 2959, 561, 47, 615, 618, 43, 612, 621, + 0, 5, 7, 561, 3, 9, 47, 615, + 618, 43, 612, 621, 73, 0, 5, 7, + 3, 9, 1, 11, 411, 11, 411, 561, + 45, 600, 603, 43, 597, 606, 477, 0, + 5, 7, 3, 9, 35, 492, 495, 489, + 498, 73, 0, 5, 7, 3, 9, 1, + 11, 411, 11, 411, 528, 525, 0, 2351, + 2355, 504, 414, 423, 2347, 2359, 525, 355, + 2187, 2195, 2987, 2995, 2159, 2203, 2979, 2983, + 3856, 3866, 2371, 525, 2187, 2195, 2675, 2675, + 2675, 2675, 2675, 2675, 2675, 2675, 2675, 2675, + 2675, 2675, 2675, 2675, 2675, 2159, 2203, 4011, + 4026, 4241, 4241, 4241, 4241, 4241, 4241, 4241, + 4241, 4241, 4241, 4241, 4241, 4241, 4241, 4241, + 2363, 2671, 91, 4226, 4231, 2667, 2659, 2663, + 4221, 4236, 2187, 2195, 2159, 2203, 429, 2251, + 2259, 3911, 2267, 429, 2183, 2191, 2199, 3861, + 5208, 5214, 5310, 5310, 5310, 5310, 5310, 5310, + 5310, 5310, 5310, 5310, 5310, 5310, 5310, 5310, + 5310, 3851 +}; + +static const short _zone_scanner_eof_actions[] = { + 0, 2399, 2555, 361, 2007, 353, 1992, 15, + 15, 353, 353, 1992, 1992, 1992, 4031, 5292, + 2004, 361, 361, 2007, 1992, 1992, 361, 361, + 361, 361, 361, 2007, 2007, 361, 361, 2007, + 361, 361, 361, 2007, 361, 361, 361, 361, + 2007, 361, 361, 361, 2007, 361, 361, 361, + 2007, 2007, 361, 361, 361, 361, 2007, 361, + 2007, 361, 361, 361, 361, 2007, 361, 361, + 361, 361, 361, 361, 361, 361, 361, 361, + 2007, 361, 361, 2007, 2007, 361, 361, 2007, + 361, 2007, 361, 2007, 2007, 361, 361, 361, + 361, 2007, 2007, 361, 361, 361, 361, 2007, + 361, 2007, 2007, 361, 2007, 2007, 361, 361, + 361, 361, 2007, 361, 361, 2007, 361, 2007, + 361, 361, 361, 2007, 2007, 361, 361, 2007, + 361, 2007, 361, 2007, 361, 361, 361, 2007, + 361, 361, 361, 2007, 361, 2007, 361, 361, + 573, 2403, 361, 361, 2007, 361, 2399, 361, + 361, 573, 2555, 2555, 2555, 2555, 361, 15, + 93, 93, 93, 93, 93, 93, 93, 93, + 39, 573, 0, 4016, 573, 361, 2555, 2555, + 2555, 361, 2375, 2403, 2999, 1992, 2399, 15, + 1992, 15, 4031, 2999, 2999, 2999, 2999, 2999, + 2999, 2999, 2999, 2999, 2999, 2999, 2999, 2999, + 2999, 1992, 2975, 93, 93, 93, 93, 93, + 93, 93, 93, 93, 2367, 5361, 2399, 2004, + 4021, 4031, 4021, 4021, 4021, 4021, 4021, 4021, + 4021, 4021, 4021, 4021, 4021, 4021, 4021, 4021, + 531, 4031, 1992, 15, 1992, 1992, 361, 361, + 2007, 361, 361, 361, 2007, 361, 2007, 1992, + 1992, 15, 1992, 1992, 1992, 1992, 353, 4031, + 1992, 1992, 534, 534, 534, 534, 534, 534, + 534, 534, 534, 534, 534, 534, 534, 534, + 15, 2004, 534, 2004, 0, 0, 31, 31, + 31, 456, 456, 456, 456, 31, 31, 684, + 684, 2615, 2615, 2615, 2615, 684, 67, 2615, + 2615, 2615, 2615, 75, 564, 4201, 432, 432, + 4201, 2547, 4201, 75, 79, 79, 79, 435, + 435, 435, 459, 459, 459, 459, 435, 79, + 0, 83, 711, 717, 85, 717, 15, 15, + 462, 462, 462, 462, 717, 15, 85, 0, + 0, 125, 125, 125, 125, 125, 125, 125, + 325, 325, 325, 325, 325, 325, 325, 325, + 325, 325, 325, 325, 325, 325, 325, 325, + 325, 325, 325, 325, 325, 325, 325, 325, + 325, 325, 325, 325, 325, 325, 325, 325, + 325, 325, 325, 325, 325, 325, 325, 325, + 325, 325, 325, 325, 325, 325, 325, 325, + 325, 325, 325, 325, 325, 325, 325, 325, + 325, 325, 325, 325, 325, 325, 325, 325, + 325, 325, 325, 325, 325, 325, 325, 325, + 325, 325, 325, 325, 325, 325, 325, 325, + 325, 325, 325, 325, 325, 325, 325, 325, + 325, 325, 325, 325, 325, 325, 325, 325, + 325, 325, 325, 325, 325, 325, 325, 325, + 325, 325, 325, 325, 325, 325, 325, 325, + 325, 325, 325, 325, 325, 325, 325, 325, + 325, 325, 325, 325, 325, 325, 325, 325, + 325, 325, 325, 325, 339, 567, 567, 2763, + 2763, 2763, 2763, 339, 339, 339, 567, 567, + 2763, 2763, 2763, 2763, 339, 567, 339, 345, + 345, 345, 345, 345, 345, 345, 345, 345, + 345, 345, 345, 345, 345, 345, 345, 345, + 345, 345, 345, 345, 345, 345, 345, 345, + 345, 345, 345, 345, 345, 345, 345, 345, + 345, 345, 345, 345, 345, 345, 345, 345, + 345, 345, 345, 345, 345, 345, 345, 345, + 345, 345, 345, 345, 345, 345, 345, 345, + 345, 345, 345, 345, 345, 345, 345, 345, + 345, 345, 345, 345, 345, 345, 345, 345, + 345, 345, 345, 345, 345, 345, 345, 345, + 345, 345, 345, 345, 345, 345, 345, 345, + 345, 345, 345, 345, 345, 345, 345, 345, + 345, 345, 345, 345, 345, 345, 345, 347, + 347, 347, 347, 347, 347, 347, 347, 347, + 347, 347, 347, 347, 347, 347, 347, 347, + 347, 347, 347, 347, 347, 347, 347, 347, + 347, 347, 347, 347, 347, 347, 347, 347, + 347, 347, 347, 347, 347, 347, 347, 347, + 753, 753, 353, 353, 353, 353, 353, 353, + 570, 570, 570, 2551, 570, 2551, 570, 2551, + 570, 2551, 2551, 2551, 2551, 353, 2551, 2551, + 2551, 353, 2551, 2551, 2551, 353, 2551, 2551, + 2551, 353, 353, 353, 353, 353, 353, 353, + 353, 353, 353, 353, 353, 353, 570, 570, + 353, 353, 353, 699, 699, 699, 699, 753, + 753, 1983, 1983, 1983, 1983, 1983, 1983, 1983, + 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, + 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, + 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, + 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, + 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, + 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, + 570, 570, 570, 570, 570, 570, 353, 353, + 353, 353, 353, 570, 570, 570, 570, 353, + 353, 353, 353, 353, 353, 353, 353, 353, + 353, 353, 353, 353, 353, 353, 570, 570, + 353, 353, 353, 353, 353, 353, 353, 774, + 774, 774, 2695, 2695, 774, 774, 353, 774, + 2695, 2695, 570, 570, 353, 353, 570, 570, + 777, 777, 777, 777, 353, 353, 353, 570, + 570, 570, 570, 777, 777, 777, 777, 353, + 353, 570, 570, 864, 864, 2391, 2391, 864, + 2771, 867, 867, 867, 867, 867, 867, 867, + 864, 864, 864, 2391, 2391, 2699, 4251, 864, + 864, 864, 2391, 2391, 2699, 4251, 864, 864, + 864, 2391, 2391, 864, 2771, 864, 864, 353, + 870, 870, 353, 353, 570, 570, 570, 570, + 678, 678, 678, 678, 570, 570, 353, 353, + 353, 353, 353, 353, 353, 353, 353, 353, + 353, 353, 870, 870, 870, 870, 870, 870, + 870, 870, 870, 870, 870, 870, 870, 870, + 870, 870, 870, 870, 870, 870, 870, 870, + 870, 870, 870, 870, 870, 870, 870, 870, + 870, 870, 870, 870, 870, 870, 870, 870, + 870, 870, 870, 870, 870, 870, 870, 870, + 870, 870, 870, 870, 870, 870, 870, 870, + 870, 870, 870, 870, 870, 870, 870, 870, + 870, 870, 870, 870, 870, 870, 870, 870, + 870, 870, 870, 870, 870, 870, 870, 870, + 870, 870, 870, 870, 870, 870, 870, 870, + 870, 870, 870, 870, 870, 870, 870, 870, + 870, 870, 870, 870, 870, 870, 870, 870, + 870, 870, 870, 870, 870, 870, 870, 870, + 870, 870, 870, 870, 870, 870, 870, 870, + 870, 870, 870, 870, 870, 2395, 2395, 870, + 870, 870, 353, 353, 353, 570, 570, 570, + 570, 353, 353, 353, 353, 353, 353, 353, + 353, 353, 570, 570, 570, 570, 570, 570, + 777, 777, 783, 783, 783, 783, 783, 783, + 783, 783, 783, 353, 783, 783, 783, 783, + 783, 353, 777, 777, 353, 353, 353, 570, + 570, 570, 570, 570, 570, 777, 777, 777, + 777, 353, 353, 353, 570, 570, 570, 570, + 570, 570, 777, 777, 777, 777, 353, 353, + 353, 570, 570, 753, 753, 353, 570, 570, + 777, 777, 777, 777, 2767, 777, 777, 777, + 777, 2971, 353, 777, 777, 2963, 777, 777, + 2963, 777, 777, 2963, 777, 777, 2963, 570, + 570, 570, 570, 353, 353, 353, 353, 570, + 570, 353, 353, 353, 353, 353, 353, 0, + 0, 0, 0, 91, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0 +}; + + + + + + +__attribute__((visibility("default"))) +int zs_init( + zs_scanner_t *s, + const char *origin, + const uint16_t rclass, + const uint32_t ttl) +{ + if (s == NULL) { + return -1; + } + + memset(s, 0, sizeof(*s)); + + // Nonzero initial scanner state. + s->cs = 1127; + + // Reset the file descriptor. + s->file.descriptor = -1; + + // Use the root zone as origin if not specified. + if (origin == NULL || strlen(origin) == 0) { + origin = "."; + } + size_t origin_len = strlen(origin); + + // Prepare a zone settings header. + const char *format; + if (origin[origin_len - 1] != '.') { + format = "$ORIGIN %s.\n"; + } else { + format = "$ORIGIN %s\n"; + } + + char settings[1024]; + int ret = snprintf(settings, sizeof(settings), format, origin); + if (ret <= 0 || ret >= sizeof(settings)) { + ERR(ZS_ENOMEM); + return -1; + } + + // Parse the settings to set up the scanner origin. + if (zs_set_input_string(s, settings, ret) != 0 || + zs_parse_all(s) != 0) { + return -1; + } + + // Set scanner defaults. + s->path = strdup("."); + if (s->path == NULL) { + ERR(ZS_ENOMEM); + return -1; + } + s->default_class = rclass; + s->default_ttl = ttl; + s->line_counter = 1; + + s->state = ZS_STATE_NONE; + s->process.automatic = false; + + return 0; +} + +static void input_deinit( + zs_scanner_t *s, + bool keep_filename) +{ + // Deinit the file input. + if (s->file.descriptor != -1) { + // Unmap the file content. + if (s->input.start != NULL) { + if (s->input.mmaped) { + munmap((void *)s->input.start, + s->input.end - s->input.start); + } else { + free((void *)s->input.start); + } + } + + // Close the opened file. + close(s->file.descriptor); + s->file.descriptor = -1; + } + + // Keep file name for possible trailing error report. + if (!keep_filename) { + free(s->file.name); + s->file.name = NULL; + } + + // Unset the input limits. + s->input.start = NULL; + s->input.current = NULL; + s->input.end = NULL; + s->input.eof = false; +} + +__attribute__((visibility("default"))) +void zs_deinit( + zs_scanner_t *s) +{ + if (s == NULL) { + return; + } + + input_deinit(s, false); + free(s->path); +} + +static int set_input_string( + zs_scanner_t *s, + const char *input, + size_t size, + bool final_block) +{ + if (s == NULL) { + return -1; + } + + if (input == NULL) { + ERR(ZS_EINVAL); + return -1; + } + + // Deinit possibly opened file. + input_deinit(s, final_block); + + // Set the scanner input limits. + s->input.start = input; + s->input.current = input; + s->input.end = input + size; + s->input.eof = final_block; + + return 0; +} + +static char *read_file_to_buf( + int fd, + size_t *bufsize) +{ + size_t bufs = 0, newbufs = 8192; + char *buf = malloc(bufs + newbufs); + int ret = 0; + + while (buf != NULL && (ret = read(fd, buf + bufs, newbufs)) == newbufs) { + bufs += newbufs; + newbufs = bufs; + char *newbuf = realloc(buf, bufs + newbufs); + if (newbuf == NULL) { + free(buf); + } + buf = newbuf; + } + if (ret < 0) { + free(buf); + return NULL; + } + + *bufsize = bufs + ret; + return buf; +} + +__attribute__((visibility("default"))) +int zs_set_input_string( + zs_scanner_t *s, + const char *input, + size_t size) +{ + s->state = ZS_STATE_NONE; + + return set_input_string(s, input, size, false); +} + +__attribute__((visibility("default"))) +int zs_set_input_file( + zs_scanner_t *s, + const char *file_name) +{ + if (s == NULL) { + return -1; + } + + if (file_name == NULL) { + ERR(ZS_EINVAL); + return -1; + } + + // Deinit possibly opened file. + input_deinit(s, false); + + // Try to open the file. + s->file.descriptor = open(file_name, O_RDONLY); + if (s->file.descriptor == -1) { + ERR(ZS_FILE_OPEN); + return -1; + } + + char *start = NULL; + size_t size = 0; + + // Check the input. + struct stat file_stat; + if (fstat(s->file.descriptor, &file_stat) == -1) { + ERR(ZS_FILE_INVALID); + input_deinit(s, false); + return -1; + } else if (S_ISCHR(file_stat.st_mode) || + S_ISBLK(file_stat.st_mode) || + S_ISFIFO(file_stat.st_mode)) { + // Workaround if cannot mmap, read to memory. + start = read_file_to_buf(s->file.descriptor, &size); + if (start == NULL) { + ERR(ZS_FILE_INVALID); + input_deinit(s, false); + return -1; + } + } else if (!S_ISREG(file_stat.st_mode)) { // Require regular file. + ERR(ZS_FILE_INVALID); + input_deinit(s, false); + return -1; + } else if (file_stat.st_size > 0) { // Mmap non-emtpy file. + start = mmap(0, file_stat.st_size, PROT_READ, MAP_SHARED, + s->file.descriptor, 0); + if (start == MAP_FAILED) { + ERR(ZS_FILE_INVALID); + input_deinit(s, false); + return -1; + } + + size = file_stat.st_size; + s->input.mmaped = true; + + // Try to set the mapped memory advise to sequential. + (void)madvise(start, size, MADV_SEQUENTIAL); + } + + // Set the scanner input limits. + s->input.start = start; + s->input.current = start; + s->input.end = start + size; + + // Get absolute path of the zone file if possible. + char *full_name = realpath(file_name, NULL); + if (full_name != NULL) { + free(s->path); + s->path = strdup(dirname(full_name)); + free(full_name); + if (s->path == NULL) { + ERR(ZS_ENOMEM); + input_deinit(s, false); + return -1; + } + } + + s->file.name = strdup(file_name); + if (s->file.name == NULL) { + ERR(ZS_ENOMEM); + input_deinit(s, false); + return -1; + } + + s->state = ZS_STATE_NONE; + + return 0; +} + +__attribute__((visibility("default"))) +int zs_set_processing( + zs_scanner_t *s, + void (*process_record)(zs_scanner_t *), + void (*process_error)(zs_scanner_t *), + void *data) +{ + if (s == NULL) { + return -1; + } + + s->process.record = process_record; + s->process.error = process_error; + s->process.data = data; + + return 0; +} + +typedef enum { + WRAP_NONE, // Initial state. + WRAP_DETECTED, // Input block end is a first '\' in rdata. + WRAP_PROCESS // Parsing of auxiliary block = "\". +} wrap_t; + +static void parse( + zs_scanner_t *s, + wrap_t *wrap) +{ + // Restore scanner input limits (Ragel internals). + const char *p = s->input.current; + const char *pe = s->input.end; + const char *eof = s->input.eof ? pe : NULL; + + // Restore state variables (Ragel internals). + int cs = s->cs; + int top = s->top; + int stack[ZS_RAGEL_STACK_SIZE]; + memcpy(stack, s->stack, sizeof(stack)); + + // Next 2 variables are for better performance. + // Restoring r_data pointer to next free space. + uint8_t *rdata_tail = s->r_data + s->r_data_tail; + // Initialization of the last r_data byte. + uint8_t *rdata_stop = s->r_data + ZS_MAX_RDATA_LENGTH - 1; + + // Write scanner body (in C). + + { + int _klen; + unsigned int _trans; + short _widec; + const short *_acts; + unsigned int _nacts; + const short *_keys; + + if ( p == pe ) + goto _test_eof; + if ( cs == 0 ) + goto _out; +_resume: + _widec = (*p); + _klen = _zone_scanner_cond_lengths[cs]; + _keys = _zone_scanner_cond_keys + (_zone_scanner_cond_offsets[cs]*2); + if ( _klen > 0 ) { + const short *_lower = _keys; + const short *_mid; + const short *_upper = _keys + (_klen<<1) - 2; + while (1) { + if ( _upper < _lower ) + break; + + _mid = _lower + (((_upper-_lower) >> 1) & ~1); + if ( _widec < _mid[0] ) + _upper = _mid - 2; + else if ( _widec > _mid[1] ) + _lower = _mid + 2; + else { + switch ( _zone_scanner_cond_spaces[_zone_scanner_cond_offsets[cs] + ((_mid - _keys)>>1)] ) { + case 0: { + _widec = (short)(640 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + break; + } + case 1: { + _widec = (short)(1152 + ((*p) - -128)); + if ( + !s->multiline ) _widec += 256; + break; + } + case 2: { + _widec = (short)(128 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + break; + } + case 3: { + _widec = (short)(2688 + ((*p) - -128)); + if ( + s->number64 != 0 ) _widec += 256; + break; + } + case 4: { + _widec = (short)(4224 + ((*p) - -128)); + if ( + s->number64 == 0 ) _widec += 256; + break; + } + case 5: { + _widec = (short)(1664 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + break; + } + case 6: { + _widec = (short)(3200 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + s->number64 != 0 ) _widec += 512; + break; + } + case 7: { + _widec = (short)(4736 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + s->number64 == 0 ) _widec += 512; + break; + } + case 8: { + _widec = (short)(5760 + ((*p) - -128)); + if ( + s->number64 != 0 ) _widec += 256; + if ( + s->number64 == 0 ) _widec += 512; + break; + } + case 9: { + _widec = (short)(12928 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + if ( + s->number64 == 0 ) _widec += 1024; + break; + } + case 10: { + _widec = (short)(6784 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + s->number64 != 0 ) _widec += 512; + if ( + s->number64 == 0 ) _widec += 1024; + break; + } + case 11: { + _widec = (short)(8832 + ((*p) - -128)); + if ( + s->multiline ) _widec += 256; + if ( + !s->multiline ) _widec += 512; + if ( + s->number64 != 0 ) _widec += 1024; + if ( + s->number64 == 0 ) _widec += 2048; + break; + } + } + break; + } + } + } + + _keys = _zone_scanner_trans_keys + _zone_scanner_key_offsets[cs]; + _trans = _zone_scanner_index_offsets[cs]; + + _klen = _zone_scanner_single_lengths[cs]; + if ( _klen > 0 ) { + const short *_lower = _keys; + const short *_mid; + const short *_upper = _keys + _klen - 1; + while (1) { + if ( _upper < _lower ) + break; + + _mid = _lower + ((_upper-_lower) >> 1); + if ( _widec < *_mid ) + _upper = _mid - 1; + else if ( _widec > *_mid ) + _lower = _mid + 1; + else { + _trans += (unsigned int)(_mid - _keys); + goto _match; + } + } + _keys += _klen; + _trans += _klen; + } + + _klen = _zone_scanner_range_lengths[cs]; + if ( _klen > 0 ) { + const short *_lower = _keys; + const short *_mid; + const short *_upper = _keys + (_klen<<1) - 2; + while (1) { + if ( _upper < _lower ) + break; + + _mid = _lower + (((_upper-_lower) >> 1) & ~1); + if ( _widec < _mid[0] ) + _upper = _mid - 2; + else if ( _widec > _mid[1] ) + _lower = _mid + 2; + else { + _trans += (unsigned int)((_mid - _keys)>>1); + goto _match; + } + } + _trans += _klen; + } + +_match: + _trans = _zone_scanner_indicies[_trans]; + cs = _zone_scanner_trans_targs[_trans]; + + if ( _zone_scanner_trans_actions[_trans] == 0 ) + goto _again; + + _acts = _zone_scanner_actions + _zone_scanner_trans_actions[_trans]; + _nacts = (unsigned int) *_acts++; + while ( _nacts-- > 0 ) + { + switch ( *_acts++ ) + { + case 0: + { + p--; {cs = stack[--top]; goto _again;} + } + break; + case 1: + { + s->line_counter++; + } + break; + case 2: + { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + p--; {cs = 268;goto _again;} + } + s->multiline = true; + } + break; + case 3: + { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + p--; {cs = 268;goto _again;} + } + s->multiline = false; + } + break; + case 4: + { + s->buffer_length = 0; + } + break; + case 5: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + break; + case 6: + { + s->buffer[s->buffer_length++] = 0; + } + break; + case 7: + { + s->buffer[0] = 0; + s->buffer_length = 0; + } + break; + case 8: + { + WARN(ZS_BAD_REST); + p--; {cs = 268;goto _again;} + } + break; + case 9: + { + s->buffer_length = 0; + } + break; + case 10: + { + if ((*p) == '\r') { + ERR(ZS_DOS_NEWLINE); + } + + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } + } + break; + case 11: + { + // Terminate the error context string. + s->buffer[s->buffer_length++] = 0; + + // Error counter incrementation. + s->error.counter++; + + // Initialize the fcall stack. + top = 0; + + // Reset the multiline context. + s->multiline = false; + + s->state = ZS_STATE_ERROR; + + // Execute the error callback. + if (s->process.automatic) { + if (s->process.error != NULL) { + s->process.error(s); + + // Stop if required from the callback. + if (s->state == ZS_STATE_STOP) { + {p++; goto _out; } + } + } + + // Stop the scanner if fatal error. + if (s->error.fatal) { + {p++; goto _out; } + } + {cs = 1127;goto _again;} + } else { + // Return if external processing. + p--; cs = 1127; {p++; goto _out; } + } + } + break; + case 12: + { + s->item_length = 0; + s->item_length_position = s->dname_tmp_length++; + } + break; + case 13: + { + // Check for maximum dname label length. + if (s->item_length < ZS_MAX_LABEL_LENGTH) { + (s->dname)[s->dname_tmp_length++] = (*p); + s->item_length++; + } else { + WARN(ZS_LABEL_OVERFLOW); + p--; {cs = 268;goto _again;} + } + } + break; + case 14: + { + // Check for maximum dname length overflow after each label. + // (at least the next label length must follow). + if (s->dname_tmp_length < ZS_MAX_DNAME_LENGTH) { + (s->dname)[s->item_length_position] = + (uint8_t)(s->item_length); + } else { + WARN(ZS_DNAME_OVERFLOW); + p--; {cs = 268;goto _again;} + } + } + break; + case 15: + { + if (s->item_length < ZS_MAX_LABEL_LENGTH) { + (s->dname)[s->dname_tmp_length] = 0; + s->item_length++; + } else { + WARN(ZS_LABEL_OVERFLOW); + p--; {cs = 268;goto _again;} + } + } + break; + case 16: + { + (s->dname)[s->dname_tmp_length] *= 10; + (s->dname)[s->dname_tmp_length] += digit_to_num[(uint8_t)(*p)]; + } + break; + case 17: + { + s->dname_tmp_length++; + } + break; + case 18: + { + WARN(ZS_BAD_NUMBER); + p--; {cs = 268;goto _again;} + } + break; + case 19: + { + // Enough room for the terminal label is guaranteed (_label_exit). + (s->dname)[s->dname_tmp_length++] = 0; + } + break; + case 20: + { + // Check for (relative + origin) dname length overflow. + if (s->dname_tmp_length + s->zone_origin_length <= ZS_MAX_DNAME_LENGTH) { + memcpy(s->dname + s->dname_tmp_length, + s->zone_origin, + s->zone_origin_length); + + s->dname_tmp_length += s->zone_origin_length; + } else { + WARN(ZS_DNAME_OVERFLOW); + p--; {cs = 268;goto _again;} + } + } + break; + case 21: + { + // Copy already verified zone origin. + memcpy(s->dname, + s->zone_origin, + s->zone_origin_length); + + s->dname_tmp_length = s->zone_origin_length; + } + break; + case 22: + { + s->item_length_position = 0; + s->dname_tmp_length = 0; + } + break; + case 23: + { + WARN(ZS_BAD_DNAME_CHAR); + p--; {cs = 268;goto _again;} + } + break; + case 24: + { p--; {stack[top++] = cs; cs = 270;goto _again;} } + break; + case 25: + { + if (rdata_tail <= rdata_stop) { + s->item_length_location = rdata_tail++; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {cs = 268;goto _again;} + } + } + break; + case 26: + { + s->item_length = rdata_tail - s->item_length_location - 1; + + if (s->item_length <= MAX_ITEM_LENGTH) { + *(s->item_length_location) = (uint8_t)(s->item_length); + } else { + WARN(ZS_ITEM_OVERFLOW); + p--; {cs = 268;goto _again;} + } + } + break; + case 27: + { + s->dname = s->r_owner; + s->r_owner_length = 0; + } + break; + case 28: + { + s->r_owner_length = s->dname_tmp_length; + } + break; + case 29: + { + if (s->r_owner_length == 0) { + WARN(ZS_BAD_PREVIOUS_OWNER); + p--; {cs = 268;goto _again;} + } + } + break; + case 30: + { + s->r_owner_length = 0; + WARN(ZS_BAD_OWNER); + p--; {cs = 268;goto _again;} + } + break; + case 31: + { + s->dname = rdata_tail; + } + break; + case 32: + { + rdata_tail += s->dname_tmp_length; + } + break; + case 33: + { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)(*p) <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + p--; {cs = 268;goto _again;} + } + } + break; + case 34: + { + s->number64 = 0; + } + break; + case 35: + { + WARN(ZS_BAD_NUMBER); + p--; {cs = 268;goto _again;} + } + break; + case 36: + { + s->decimal_counter = 0; + } + break; + case 37: + { + s->number64_tmp = s->number64; + } + break; + case 38: + { + s->decimal_counter++; + } + break; + case 39: + { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + p--; {cs = 268;goto _again;} + } + } + break; + case 40: + { + s->decimals = 2; + } + break; + case 41: + { + s->decimals = 3; + } + break; + case 42: + { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {cs = 268;goto _again;} + } + } + break; + case 43: + { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {cs = 268;goto _again;} + } + } + break; + case 44: + { + if (s->number64 <= UINT32_MAX) { + uint32_t num32 = htonl((uint32_t)s->number64); + memcpy(rdata_tail, &num32, 4); + rdata_tail += 4; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {cs = 268;goto _again;} + } + } + break; + case 45: + { + if (s->number64 <= UINT16_MAX) { + s->r_type = (uint16_t)(s->number64); + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {cs = 268;goto _again;} + } + } + break; + case 46: + { + if (s->number64 <= UINT16_MAX) { + s->r_data_length = (uint16_t)(s->number64); + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {cs = 268;goto _again;} + } + } + break; + case 47: + { + WARN(ZS_BAD_TIME_UNIT); + p--; {cs = 268;goto _again;} + } + break; + case 48: + { if (s->number64 <= (UINT32_MAX / 60)) { + s->number64 *= 60; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {cs = 268;goto _again;} + } + } + break; + case 49: + { if (s->number64 <= (UINT32_MAX / 3600)) { + s->number64 *= 3600; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {cs = 268;goto _again;} + } + } + break; + case 50: + { if (s->number64 <= (UINT32_MAX / 86400)) { + s->number64 *= 86400; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {cs = 268;goto _again;} + } + } + break; + case 51: + { if (s->number64 <= (UINT32_MAX / 604800)) { + s->number64 *= 604800; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {cs = 268;goto _again;} + } + } + break; + case 52: + { + s->number64_tmp = s->number64; + } + break; + case 53: + { + if (s->number64 + s->number64_tmp < UINT32_MAX) { + s->number64 += s->number64_tmp; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {cs = 268;goto _again;} + } + } + break; + case 54: + { + s->buffer_length = 0; + } + break; + case 55: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {cs = 268;goto _again;} + } + } + break; + case 56: + { + s->buffer[s->buffer_length] = 0; + + if (s->buffer_length == 14) { // Date; 14 = len("YYYYMMDDHHmmSS"). + uint32_t timestamp; + int ret = date_to_timestamp(s->buffer, ×tamp); + + if (ret == ZS_OK) { + *((uint32_t *)rdata_tail) = htonl(timestamp); + rdata_tail += 4; + } else { + WARN(ret); + p--; {cs = 268;goto _again;} + } + } else if (s->buffer_length <= 10) { // Timestamp format. + char *end; + + s->number64 = strtoull((char *)(s->buffer), &end, 10); + + if (end == (char *)(s->buffer) || *end != '\0') { + WARN(ZS_BAD_TIMESTAMP); + p--; {cs = 268;goto _again;} + } + + if (s->number64 <= UINT32_MAX) { + *((uint32_t *)rdata_tail) = htonl((uint32_t)s->number64); + rdata_tail += 4; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {cs = 268;goto _again;} + } + } else { + WARN(ZS_BAD_TIMESTAMP_LENGTH); + p--; {cs = 268;goto _again;} + } + } + break; + case 57: + { + WARN(ZS_BAD_TIMESTAMP_CHAR); + p--; {cs = 268;goto _again;} + } + break; + case 58: + { + if (rdata_tail <= rdata_stop) { + // Split long string. + if (s->long_string && + rdata_tail - s->item_length_location == 1 + MAX_ITEM_LENGTH) { + // _item_length_exit equivalent. + *(s->item_length_location) = MAX_ITEM_LENGTH; + // _item_length_init equivalent. + s->item_length_location = rdata_tail++; + + if (rdata_tail > rdata_stop) { + WARN(ZS_TEXT_OVERFLOW); + p--; {cs = 268;goto _again;} + } + } + + *(rdata_tail++) = (*p); + } else { + WARN(ZS_TEXT_OVERFLOW); + p--; {cs = 268;goto _again;} + } + } + break; + case 59: + { + WARN(ZS_BAD_TEXT_CHAR); + p--; {cs = 268;goto _again;} + } + break; + case 60: + { + WARN(ZS_BAD_TEXT); + p--; {cs = 268;goto _again;} + } + break; + case 61: + { + if (rdata_tail <= rdata_stop) { + // Split long string. + if (s->long_string && + rdata_tail - s->item_length_location == 1 + MAX_ITEM_LENGTH) { + // _item_length_exit equivalent. + *(s->item_length_location) = MAX_ITEM_LENGTH; + // _item_length_init equivalent. + s->item_length_location = rdata_tail++; + + if (rdata_tail > rdata_stop) { + WARN(ZS_TEXT_OVERFLOW); + p--; {cs = 268;goto _again;} + } + } + + *rdata_tail = 0; + s->item_length++; + } else { + WARN(ZS_TEXT_OVERFLOW); + p--; {cs = 268;goto _again;} + } + } + break; + case 62: + { + if ((*rdata_tail < (UINT8_MAX / 10)) || // Dominant fast check. + ((*rdata_tail == (UINT8_MAX / 10)) && // Marginal case. + ((*p) <= (UINT8_MAX % 10) + '0') + ) + ) { + *rdata_tail *= 10; + *rdata_tail += digit_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + p--; {cs = 268;goto _again;} + } + } + break; + case 63: + { + rdata_tail++; + } + break; + case 64: + { + WARN(ZS_BAD_NUMBER); + p--; {cs = 268;goto _again;} + } + break; + case 65: + { p--; {stack[top++] = cs; cs = 279;goto _again;} } + break; + case 66: + { + s->long_string = true; + } + break; + case 67: + { + s->long_string = false; + } + break; + case 68: + { + if (s->number64 <= UINT32_MAX) { + s->default_ttl = (uint32_t)(s->number64); + } else { + ERR(ZS_NUMBER32_OVERFLOW); + p--; {cs = 268;goto _again;} + } + } + break; + case 69: + { + ERR(ZS_BAD_TTL); + p--; {cs = 268;goto _again;} + } + break; + case 70: + { p--; {stack[top++] = cs; cs = 291;goto _again;} } + break; + case 71: + { + s->dname = s->zone_origin; + } + break; + case 72: + { + s->zone_origin_length = s->dname_tmp_length; + } + break; + case 73: + { + ERR(ZS_BAD_ORIGIN); + p--; {cs = 268;goto _again;} + } + break; + case 74: + { p--; {stack[top++] = cs; cs = 300;goto _again;} } + break; + case 75: + { + rdata_tail = s->r_data; + } + break; + case 76: + { + size_t len = rdata_tail - s->r_data; + if (len >= sizeof(s->include_filename)) { + ERR(ZS_BAD_INCLUDE_FILENAME); + p--; {cs = 268;goto _again;} + } + + // Store zero terminated include filename. + memcpy(s->include_filename, s->r_data, len); + s->include_filename[len] = '\0'; + + // For detection whether origin is not present. + s->dname = NULL; + } + break; + case 77: + { + ERR(ZS_BAD_INCLUDE_FILENAME); + p--; {cs = 268;goto _again;} + } + break; + case 78: + { + s->dname = s->r_data; + } + break; + case 79: + { + s->r_data_length = s->dname_tmp_length; + } + break; + case 80: + { + ERR(ZS_BAD_INCLUDE_ORIGIN); + p--; {cs = 268;goto _again;} + } + break; + case 81: + { + // Extend relative file path. + if (s->include_filename[0] != '/') { + int ret = snprintf((char *)(s->buffer), sizeof(s->buffer), + "%s/%s", s->path, s->include_filename); + if (ret <= 0 || ret > sizeof(s->buffer)) { + ERR(ZS_BAD_INCLUDE_FILENAME); + p--; {cs = 268;goto _again;} + } + memcpy(s->include_filename, s->buffer, ret); + } + + // Origin conversion from wire to text form in \DDD notation. + if (s->dname == NULL) { // Use current origin. + wire_dname_to_str(s->zone_origin, + s->zone_origin_length, + (char *)s->buffer); + } else { // Use specified origin. + wire_dname_to_str(s->r_data, + s->r_data_length, + (char *)s->buffer); + } + + // Let the caller to solve the include. + if (s->process.automatic) { + // Create new scanner for included zone file. + zs_scanner_t *ss = malloc(sizeof(zs_scanner_t)); + if (ss == NULL) { + ERR(ZS_UNPROCESSED_INCLUDE); + p--; {cs = 268;goto _again;} + } + + // Parse included zone file. + if (zs_init(ss, (char *)s->buffer, s->default_class, + s->default_ttl) != 0 || + zs_set_input_file(ss, (char *)(s->include_filename)) != 0 || + zs_set_processing(ss, s->process.record, s->process.error, + s->process.data) != 0 || + zs_parse_all(ss) != 0) { + // File internal errors are handled by error callback. + if (ss->error.counter > 0) { + s->error.counter += ss->error.counter; + ERR(ZS_UNPROCESSED_INCLUDE); + // General include file error. + } else { + ERR(ss->error.code); + } + zs_deinit(ss); + free(ss); + p--; {cs = 268;goto _again;} + } + zs_deinit(ss); + free(ss); + } else { + s->state = ZS_STATE_INCLUDE; + p--; cs = 1127; {p++; goto _out; } + } + } + break; + case 82: + { p--; {stack[top++] = cs; cs = 312;goto _again;} } + break; + case 83: + { + ERR(ZS_OK); + } + break; + case 84: + { + NOERR; + } + break; + case 85: + { + ERR(ZS_BAD_DIRECTIVE); + p--; {cs = 268;goto _again;} + } + break; + case 86: + { + s->r_class = s->default_class; + } + break; + case 87: + { + s->r_ttl = s->default_ttl; + } + break; + case 88: + { + s->r_class = KNOT_CLASS_IN; + } + break; + case 89: + { + if (s->number64 <= UINT32_MAX) { + s->r_ttl = (uint32_t)(s->number64); + } else { + WARN(ZS_NUMBER32_OVERFLOW); + p--; {cs = 268;goto _again;} + } + } + break; + case 90: + { + s->buffer_length = 0; + } + break; + case 91: + { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = (*p); + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {cs = 268;goto _again;} + } + } + break; + case 92: + { + WARN(ZS_BAD_ADDRESS_CHAR); + p--; {cs = 268;goto _again;} + } + break; + case 93: + { + s->buffer[s->buffer_length] = 0; + + if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) { + WARN(ZS_BAD_IPV4); + p--; {cs = 268;goto _again;} + } + } + break; + case 94: + { + memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH); + rdata_tail += ZS_INET4_ADDR_LENGTH; + } + break; + case 95: + { + s->buffer[s->buffer_length] = 0; + + if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) { + WARN(ZS_BAD_IPV6); + p--; {cs = 268;goto _again;} + } + } + break; + case 96: + { + memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH); + rdata_tail += ZS_INET6_ADDR_LENGTH; + } + break; + case 97: + { + memset(&(s->apl), 0, sizeof(s->apl)); + } + break; + case 98: + { + s->apl.excl_flag = 128; // dec 128 = bin 10000000. + } + break; + case 99: + { + s->apl.addr_family = 1; + } + break; + case 100: + { + s->apl.addr_family = 2; + } + break; + case 101: + { + if ((s->apl.addr_family == 1 && s->number64 <= 32) || + (s->apl.addr_family == 2 && s->number64 <= 128)) { + s->apl.prefix_length = (uint8_t)(s->number64); + } else { + WARN(ZS_BAD_APL); + p--; {cs = 268;goto _again;} + } + } + break; + case 102: + { + // Copy address to buffer. + uint8_t len; + switch (s->apl.addr_family) { + case 1: + len = ZS_INET4_ADDR_LENGTH; + memcpy(s->buffer, s->addr, len); + break; + case 2: + len = ZS_INET6_ADDR_LENGTH; + memcpy(s->buffer, s->addr, len); + break; + default: + WARN(ZS_BAD_APL); + p--; {cs = 268;goto _again;} + } + // Find prefix without trailing zeroes. + while (len > 0) { + if ((s->buffer[len - 1] & 255) != 0) { + break; + } + len--; + } + // Check for rdata overflow. + if (rdata_tail + 4 + len > rdata_stop) { + WARN(ZS_RDATA_OVERFLOW); + p--; {cs = 268;goto _again;} + } + // Write address family. + *((uint16_t *)rdata_tail) = htons(s->apl.addr_family); + rdata_tail += 2; + // Write prefix length in bits. + *(rdata_tail) = s->apl.prefix_length; + rdata_tail += 1; + // Write negation flag + prefix length in bytes. + *(rdata_tail) = len + s->apl.excl_flag; + rdata_tail += 1; + // Write address prefix non-null data. + memcpy(rdata_tail, s->buffer, len); + rdata_tail += len; + } + break; + case 103: + { + WARN(ZS_BAD_APL); + p--; {cs = 268;goto _again;} + } + break; + case 104: + { + if (rdata_tail <= rdata_stop) { + *rdata_tail = first_hex_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {cs = 268;goto _again;} + } + } + break; + case 105: + { + *rdata_tail += second_hex_to_num[(uint8_t)(*p)]; + rdata_tail++; + } + break; + case 106: + { + WARN(ZS_BAD_HEX_CHAR); + p--; {cs = 268;goto _again;} + } + break; + case 107: + { + if ((rdata_tail - s->r_data) != s->r_data_length) { + WARN(ZS_BAD_RDATA_LENGTH); + p--; {cs = 268;goto _again;} + } + } + break; + case 108: + { + WARN(ZS_BAD_HEX_RDATA); + p--; {cs = 268;goto _again;} + } + break; + case 109: + { + if (rdata_tail <= rdata_stop) { + *rdata_tail = first_base64_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {cs = 268;goto _again;} + } + } + break; + case 110: + { + *(rdata_tail++) += second_left_base64_to_num[(uint8_t)(*p)]; + + if (rdata_tail <= rdata_stop) { + *rdata_tail = second_right_base64_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {cs = 268;goto _again;} + } + } + break; + case 111: + { + *(rdata_tail++) += third_left_base64_to_num[(uint8_t)(*p)]; + + if (rdata_tail <= rdata_stop) { + *rdata_tail = third_right_base64_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {cs = 268;goto _again;} + } + } + break; + case 112: + { + *(rdata_tail++) += fourth_base64_to_num[(uint8_t)(*p)]; + } + break; + case 113: + { + WARN(ZS_BAD_BASE64_CHAR); + p--; {cs = 268;goto _again;} + } + break; + case 114: + { p--; {stack[top++] = cs; cs = 329;goto _again;} } + break; + case 115: + { + if (rdata_tail <= rdata_stop) { + *rdata_tail = first_base32hex_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {cs = 268;goto _again;} + } + } + break; + case 116: + { + *(rdata_tail++) += second_left_base32hex_to_num[(uint8_t)(*p)]; + + if (rdata_tail <= rdata_stop) { + *rdata_tail = second_right_base32hex_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {cs = 268;goto _again;} + } + } + break; + case 117: + { + *rdata_tail += third_base32hex_to_num[(uint8_t)(*p)]; + } + break; + case 118: + { + *(rdata_tail++) += fourth_left_base32hex_to_num[(uint8_t)(*p)]; + + if (rdata_tail <= rdata_stop) { + *rdata_tail = fourth_right_base32hex_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {cs = 268;goto _again;} + } + } + break; + case 119: + { + *(rdata_tail++) += fifth_left_base32hex_to_num[(uint8_t)(*p)]; + + if (rdata_tail <= rdata_stop) { + *rdata_tail = fifth_right_base32hex_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {cs = 268;goto _again;} + } + } + break; + case 120: + { + *rdata_tail += sixth_base32hex_to_num[(uint8_t)(*p)]; + } + break; + case 121: + { + *(rdata_tail++) += seventh_left_base32hex_to_num[(uint8_t)(*p)]; + + if (rdata_tail <= rdata_stop) { + *rdata_tail = seventh_right_base32hex_to_num[(uint8_t)(*p)]; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {cs = 268;goto _again;} + } + } + break; + case 122: + { + *(rdata_tail++) += eighth_base32hex_to_num[(uint8_t)(*p)]; + } + break; + case 123: + { + WARN(ZS_BAD_BASE32HEX_CHAR); + p--; {cs = 268;goto _again;} + } + break; + case 124: + { + *(rdata_tail++) = 0; + } + break; + case 125: + { + *(rdata_tail++) = 1; + } + break; + case 126: + { + *(rdata_tail++) = 2; + } + break; + case 127: + { + *(rdata_tail++) = 3; + } + break; + case 128: + { + *(rdata_tail++) = 5; + } + break; + case 129: + { + *(rdata_tail++) = 6; + } + break; + case 130: + { + *(rdata_tail++) = 7; + } + break; + case 131: + { + *(rdata_tail++) = 8; + } + break; + case 132: + { + *(rdata_tail++) = 10; + } + break; + case 133: + { + *(rdata_tail++) = 12; + } + break; + case 134: + { + *(rdata_tail++) = 13; + } + break; + case 135: + { + *(rdata_tail++) = 14; + } + break; + case 136: + { + *(rdata_tail++) = 15; + } + break; + case 137: + { + *(rdata_tail++) = 16; + } + break; + case 138: + { + *(rdata_tail++) = 252; + } + break; + case 139: + { + *(rdata_tail++) = 253; + } + break; + case 140: + { + *(rdata_tail++) = 254; + } + break; + case 141: + { + *((uint16_t *)rdata_tail) = htons(1); + rdata_tail += 2; + } + break; + case 142: + { + *((uint16_t *)rdata_tail) = htons(2); + rdata_tail += 2; + } + break; + case 143: + { + *((uint16_t *)rdata_tail) = htons(3); + rdata_tail += 2; + } + break; + case 144: + { + *((uint16_t *)rdata_tail) = htons(4); + rdata_tail += 2; + } + break; + case 145: + { + *((uint16_t *)rdata_tail) = htons(5); + rdata_tail += 2; + } + break; + case 146: + { + *((uint16_t *)rdata_tail) = htons(6); + rdata_tail += 2; + } + break; + case 147: + { + *((uint16_t *)rdata_tail) = htons(7); + rdata_tail += 2; + } + break; + case 148: + { + *((uint16_t *)rdata_tail) = htons(8); + rdata_tail += 2; + } + break; + case 149: + { + *((uint16_t *)rdata_tail) = htons(253); + rdata_tail += 2; + } + break; + case 150: + { + *((uint16_t *)rdata_tail) = htons(254); + rdata_tail += 2; + } + break; + case 151: + { + WARN(ZS_BAD_GATEWAY); + p--; {cs = 268;goto _again;} + } + break; + case 152: + { + WARN(ZS_BAD_GATEWAY_KEY); + p--; {cs = 268;goto _again;} + } + break; + case 153: + { + WARN(ZS_UNSUPPORTED_TYPE); + p--; {cs = 268;goto _again;} + } + break; + case 154: + { type_num(KNOT_RRTYPE_A, &rdata_tail); } + break; + case 155: + { type_num(KNOT_RRTYPE_NS, &rdata_tail); } + break; + case 156: + { type_num(KNOT_RRTYPE_CNAME, &rdata_tail); } + break; + case 157: + { type_num(KNOT_RRTYPE_SOA, &rdata_tail); } + break; + case 158: + { type_num(KNOT_RRTYPE_PTR, &rdata_tail); } + break; + case 159: + { type_num(KNOT_RRTYPE_HINFO, &rdata_tail); } + break; + case 160: + { type_num(KNOT_RRTYPE_MINFO, &rdata_tail); } + break; + case 161: + { type_num(KNOT_RRTYPE_MX, &rdata_tail); } + break; + case 162: + { type_num(KNOT_RRTYPE_TXT, &rdata_tail); } + break; + case 163: + { type_num(KNOT_RRTYPE_RP, &rdata_tail); } + break; + case 164: + { type_num(KNOT_RRTYPE_AFSDB, &rdata_tail); } + break; + case 165: + { type_num(KNOT_RRTYPE_RT, &rdata_tail); } + break; + case 166: + { type_num(KNOT_RRTYPE_KEY, &rdata_tail); } + break; + case 167: + { type_num(KNOT_RRTYPE_AAAA, &rdata_tail); } + break; + case 168: + { type_num(KNOT_RRTYPE_LOC, &rdata_tail); } + break; + case 169: + { type_num(KNOT_RRTYPE_SRV, &rdata_tail); } + break; + case 170: + { type_num(KNOT_RRTYPE_NAPTR, &rdata_tail); } + break; + case 171: + { type_num(KNOT_RRTYPE_KX, &rdata_tail); } + break; + case 172: + { type_num(KNOT_RRTYPE_CERT, &rdata_tail); } + break; + case 173: + { type_num(KNOT_RRTYPE_DNAME, &rdata_tail); } + break; + case 174: + { type_num(KNOT_RRTYPE_APL, &rdata_tail); } + break; + case 175: + { type_num(KNOT_RRTYPE_DS, &rdata_tail); } + break; + case 176: + { type_num(KNOT_RRTYPE_SSHFP, &rdata_tail); } + break; + case 177: + { type_num(KNOT_RRTYPE_IPSECKEY, &rdata_tail); } + break; + case 178: + { type_num(KNOT_RRTYPE_RRSIG, &rdata_tail); } + break; + case 179: + { type_num(KNOT_RRTYPE_NSEC, &rdata_tail); } + break; + case 180: + { type_num(KNOT_RRTYPE_DNSKEY, &rdata_tail); } + break; + case 181: + { type_num(KNOT_RRTYPE_DHCID, &rdata_tail); } + break; + case 182: + { type_num(KNOT_RRTYPE_NSEC3, &rdata_tail); } + break; + case 183: + { type_num(KNOT_RRTYPE_NSEC3PARAM, &rdata_tail); } + break; + case 184: + { type_num(KNOT_RRTYPE_TLSA, &rdata_tail); } + break; + case 185: + { type_num(KNOT_RRTYPE_CDS, &rdata_tail); } + break; + case 186: + { type_num(KNOT_RRTYPE_CDNSKEY, &rdata_tail); } + break; + case 187: + { type_num(KNOT_RRTYPE_SPF, &rdata_tail); } + break; + case 188: + { type_num(KNOT_RRTYPE_NID, &rdata_tail); } + break; + case 189: + { type_num(KNOT_RRTYPE_L32, &rdata_tail); } + break; + case 190: + { type_num(KNOT_RRTYPE_L64, &rdata_tail); } + break; + case 191: + { type_num(KNOT_RRTYPE_LP, &rdata_tail); } + break; + case 192: + { type_num(KNOT_RRTYPE_EUI48, &rdata_tail); } + break; + case 193: + { type_num(KNOT_RRTYPE_EUI64, &rdata_tail); } + break; + case 194: + { type_num(KNOT_RRTYPE_URI, &rdata_tail); } + break; + case 195: + { type_num(KNOT_RRTYPE_CAA, &rdata_tail); } + break; + case 196: + { + if (s->number64 <= UINT16_MAX) { + window_add_bit(s->number64, s); + } else { + WARN(ZS_NUMBER16_OVERFLOW); + p--; {cs = 268;goto _again;} + } + } + break; + case 197: + { window_add_bit(KNOT_RRTYPE_A, s); } + break; + case 198: + { window_add_bit(KNOT_RRTYPE_NS, s); } + break; + case 199: + { window_add_bit(KNOT_RRTYPE_CNAME, s); } + break; + case 200: + { window_add_bit(KNOT_RRTYPE_SOA, s); } + break; + case 201: + { window_add_bit(KNOT_RRTYPE_PTR, s); } + break; + case 202: + { window_add_bit(KNOT_RRTYPE_HINFO, s); } + break; + case 203: + { window_add_bit(KNOT_RRTYPE_MINFO, s); } + break; + case 204: + { window_add_bit(KNOT_RRTYPE_MX, s); } + break; + case 205: + { window_add_bit(KNOT_RRTYPE_TXT, s); } + break; + case 206: + { window_add_bit(KNOT_RRTYPE_RP, s); } + break; + case 207: + { window_add_bit(KNOT_RRTYPE_AFSDB, s); } + break; + case 208: + { window_add_bit(KNOT_RRTYPE_RT, s); } + break; + case 209: + { window_add_bit(KNOT_RRTYPE_KEY, s); } + break; + case 210: + { window_add_bit(KNOT_RRTYPE_AAAA, s); } + break; + case 211: + { window_add_bit(KNOT_RRTYPE_LOC, s); } + break; + case 212: + { window_add_bit(KNOT_RRTYPE_SRV, s); } + break; + case 213: + { window_add_bit(KNOT_RRTYPE_NAPTR, s); } + break; + case 214: + { window_add_bit(KNOT_RRTYPE_KX, s); } + break; + case 215: + { window_add_bit(KNOT_RRTYPE_CERT, s); } + break; + case 216: + { window_add_bit(KNOT_RRTYPE_DNAME, s); } + break; + case 217: + { window_add_bit(KNOT_RRTYPE_APL, s); } + break; + case 218: + { window_add_bit(KNOT_RRTYPE_DS, s); } + break; + case 219: + { window_add_bit(KNOT_RRTYPE_SSHFP, s); } + break; + case 220: + { window_add_bit(KNOT_RRTYPE_IPSECKEY, s); } + break; + case 221: + { window_add_bit(KNOT_RRTYPE_RRSIG, s); } + break; + case 222: + { window_add_bit(KNOT_RRTYPE_NSEC, s); } + break; + case 223: + { window_add_bit(KNOT_RRTYPE_DNSKEY, s); } + break; + case 224: + { window_add_bit(KNOT_RRTYPE_DHCID, s); } + break; + case 225: + { window_add_bit(KNOT_RRTYPE_NSEC3, s); } + break; + case 226: + { window_add_bit(KNOT_RRTYPE_NSEC3PARAM, s); } + break; + case 227: + { window_add_bit(KNOT_RRTYPE_TLSA, s); } + break; + case 228: + { window_add_bit(KNOT_RRTYPE_CDS, s); } + break; + case 229: + { window_add_bit(KNOT_RRTYPE_CDNSKEY, s); } + break; + case 230: + { window_add_bit(KNOT_RRTYPE_SPF, s); } + break; + case 231: + { window_add_bit(KNOT_RRTYPE_NID, s); } + break; + case 232: + { window_add_bit(KNOT_RRTYPE_L32, s); } + break; + case 233: + { window_add_bit(KNOT_RRTYPE_L64, s); } + break; + case 234: + { window_add_bit(KNOT_RRTYPE_LP, s); } + break; + case 235: + { window_add_bit(KNOT_RRTYPE_EUI48, s); } + break; + case 236: + { window_add_bit(KNOT_RRTYPE_EUI64, s); } + break; + case 237: + { window_add_bit(KNOT_RRTYPE_URI, s); } + break; + case 238: + { window_add_bit(KNOT_RRTYPE_CAA, s); } + break; + case 239: + { + memset(s->windows, 0, sizeof(s->windows)); + s->last_window = -1; + } + break; + case 240: + { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + p--; {cs = 268;goto _again;} + } + } + } + } + break; + case 241: + { + WARN(ZS_BAD_BITMAP); + p--; {cs = 268;goto _again;} + } + break; + case 242: + { p--; {stack[top++] = cs; cs = 336;goto _again;} } + break; + case 243: + { + if (s->number64 <= 90) { + s->loc.d1 = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {cs = 268;goto _again;} + } + } + break; + case 244: + { + if (s->number64 <= 180) { + s->loc.d2 = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {cs = 268;goto _again;} + } + } + break; + case 245: + { + if (s->number64 <= 59) { + s->loc.m1 = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {cs = 268;goto _again;} + } + } + break; + case 246: + { + if (s->number64 <= 59) { + s->loc.m2 = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {cs = 268;goto _again;} + } + } + break; + case 247: + { + if (s->number64 <= 59999) { + s->loc.s1 = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {cs = 268;goto _again;} + } + } + break; + case 248: + { + if (s->number64 <= 59999) { + s->loc.s2 = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {cs = 268;goto _again;} + } + } + break; + case 249: + { + if ((s->loc.alt_sign == 1 && s->number64 <= 4284967295) || + (s->loc.alt_sign == -1 && s->number64 <= 10000000)) + { + s->loc.alt = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + p--; {cs = 268;goto _again;} + } + } + break; + case 250: + { + if (s->number64 <= 9000000000ULL) { + s->loc.siz = s->number64; + } else { + WARN(ZS_BAD_NUMBER); + p--; {cs = 268;goto _again;} + } + } + break; + case 251: + { + if (s->number64 <= 9000000000ULL) { + s->loc.hp = s->number64; + } else { + WARN(ZS_BAD_NUMBER); + p--; {cs = 268;goto _again;} + } + } + break; + case 252: + { + if (s->number64 <= 9000000000ULL) { + s->loc.vp = s->number64; + } else { + WARN(ZS_BAD_NUMBER); + p--; {cs = 268;goto _again;} + } + } + break; + case 253: + { + s->loc.lat_sign = -1; + } + break; + case 254: + { + s->loc.long_sign = -1; + } + break; + case 255: + { + s->loc.alt_sign = -1; + } + break; + case 256: + { + memset(&(s->loc), 0, sizeof(s->loc)); + // Defaults. + s->loc.siz = 100; + s->loc.vp = 1000; + s->loc.hp = 1000000; + s->loc.lat_sign = 1; + s->loc.long_sign = 1; + s->loc.alt_sign = 1; + } + break; + case 257: + { + // Write version. + *(rdata_tail) = 0; + rdata_tail += 1; + // Write size. + *(rdata_tail) = loc64to8(s->loc.siz); + rdata_tail += 1; + // Write horizontal precision. + *(rdata_tail) = loc64to8(s->loc.hp); + rdata_tail += 1; + // Write vertical precision. + *(rdata_tail) = loc64to8(s->loc.vp); + rdata_tail += 1; + // Write latitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign * + (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1)); + rdata_tail += 4; + // Write longitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign * + (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2)); + rdata_tail += 4; + // Write altitude. + *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign * + (s->loc.alt)); + rdata_tail += 4; + } + break; + case 258: + { + WARN(ZS_BAD_LOC_DATA); + p--; {cs = 268;goto _again;} + } + break; + case 259: + { + WARN(ZS_BAD_HEX_RDATA); + p--; {cs = 268;goto _again;} + } + break; + case 260: + { + s->item_length = 0; + } + break; + case 261: + { + s->item_length++; + } + break; + case 262: + { + if (s->item_length != 6) { + WARN(ZS_BAD_EUI_LENGTH); + p--; {cs = 268;goto _again;} + } + } + break; + case 263: + { + if (s->item_length != 8) { + WARN(ZS_BAD_EUI_LENGTH); + p--; {cs = 268;goto _again;} + } + } + break; + case 264: + { + WARN(ZS_BAD_CHAR_DASH); + p--; {cs = 268;goto _again;} + } + break; + case 265: + { + s->item_length = 0; + } + break; + case 266: + { + s->item_length++; + } + break; + case 267: + { + if (s->item_length != 4) { + WARN(ZS_BAD_L64_LENGTH); + p--; {cs = 268;goto _again;} + } + } + break; + case 268: + { + WARN(ZS_BAD_CHAR_COLON); + p--; {cs = 268;goto _again;} + } + break; + case 269: + { + WARN(ZS_BAD_ALGORITHM); + p--; {cs = 268;goto _again;} + } + break; + case 270: + { + WARN(ZS_BAD_CERT_TYPE); + p--; {cs = 268;goto _again;} + } + break; + case 271: + { p--; {stack[top++] = cs; cs = 487;goto _again;} } + break; + case 272: + { p--; {stack[top++] = cs; cs = 591;goto _again;} } + break; + case 273: + { + rdata_tail = s->r_data; + } + break; + case 274: + { + WARN(ZS_BAD_RDATA); + p--; {cs = 268;goto _again;} + } + break; + case 275: + { + p--; + switch (s->r_type) { + case KNOT_RRTYPE_A: + {stack[top++] = cs; cs = 632;goto _again;} + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + {stack[top++] = cs; cs = 634;goto _again;} + case KNOT_RRTYPE_SOA: + {stack[top++] = cs; cs = 636;goto _again;} + case KNOT_RRTYPE_HINFO: + {stack[top++] = cs; cs = 668;goto _again;} + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + {stack[top++] = cs; cs = 673;goto _again;} + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + {stack[top++] = cs; cs = 678;goto _again;} + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + {stack[top++] = cs; cs = 683;goto _again;} + case KNOT_RRTYPE_AAAA: + {stack[top++] = cs; cs = 687;goto _again;} + case KNOT_RRTYPE_LOC: + {stack[top++] = cs; cs = 689;goto _again;} + case KNOT_RRTYPE_SRV: + {stack[top++] = cs; cs = 744;goto _again;} + case KNOT_RRTYPE_NAPTR: + {stack[top++] = cs; cs = 755;goto _again;} + case KNOT_RRTYPE_CERT: + {stack[top++] = cs; cs = 772;goto _again;} + case KNOT_RRTYPE_APL: + {stack[top++] = cs; cs = 783;goto _again;} + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + {stack[top++] = cs; cs = 794;goto _again;} + case KNOT_RRTYPE_SSHFP: + {stack[top++] = cs; cs = 807;goto _again;} + case KNOT_RRTYPE_IPSECKEY: + {stack[top++] = cs; cs = 817;goto _again;} + case KNOT_RRTYPE_RRSIG: + {stack[top++] = cs; cs = 856;goto _again;} + case KNOT_RRTYPE_NSEC: + {stack[top++] = cs; cs = 1010;goto _again;} + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + {stack[top++] = cs; cs = 1013;goto _again;} + case KNOT_RRTYPE_DHCID: + {stack[top++] = cs; cs = 1024;goto _again;} + case KNOT_RRTYPE_NSEC3: + {stack[top++] = cs; cs = 1026;goto _again;} + case KNOT_RRTYPE_NSEC3PARAM: + {stack[top++] = cs; cs = 1055;goto _again;} + case KNOT_RRTYPE_TLSA: + {stack[top++] = cs; cs = 1068;goto _again;} + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + {stack[top++] = cs; cs = 1086;goto _again;} + case KNOT_RRTYPE_L32: + {stack[top++] = cs; cs = 1081;goto _again;} + case KNOT_RRTYPE_EUI48: + {stack[top++] = cs; cs = 1099;goto _again;} + case KNOT_RRTYPE_EUI64: + {stack[top++] = cs; cs = 1105;goto _again;} + case KNOT_RRTYPE_URI: + {stack[top++] = cs; cs = 1111;goto _again;} + case KNOT_RRTYPE_CAA: + {stack[top++] = cs; cs = 1119;goto _again;} + default: + WARN(ZS_CANNOT_TEXT_DATA); + {cs = 268;goto _again;} + } + } + break; + case 276: + { + switch (s->r_type) { + // Next types must not have empty rdata. + case KNOT_RRTYPE_A: + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + case KNOT_RRTYPE_SOA: + case KNOT_RRTYPE_HINFO: + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + case KNOT_RRTYPE_RP: + case KNOT_RRTYPE_AAAA: + case KNOT_RRTYPE_LOC: + case KNOT_RRTYPE_SRV: + case KNOT_RRTYPE_NAPTR: + case KNOT_RRTYPE_CERT: + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_SSHFP: + case KNOT_RRTYPE_IPSECKEY: + case KNOT_RRTYPE_RRSIG: + case KNOT_RRTYPE_NSEC: + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_DHCID: + case KNOT_RRTYPE_NSEC3: + case KNOT_RRTYPE_NSEC3PARAM: + case KNOT_RRTYPE_TLSA: + case KNOT_RRTYPE_CDS: + case KNOT_RRTYPE_CDNSKEY: + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L32: + case KNOT_RRTYPE_L64: + case KNOT_RRTYPE_LP: + case KNOT_RRTYPE_EUI48: + case KNOT_RRTYPE_EUI64: + case KNOT_RRTYPE_URI: + case KNOT_RRTYPE_CAA: + {stack[top++] = cs; cs = 468;goto _again;} + // Next types can have empty rdata. + case KNOT_RRTYPE_APL: + default: + {stack[top++] = cs; cs = 477;goto _again;} + } + } + break; + case 277: + { + if (pe - p == 1) { + *wrap = WRAP_DETECTED; + } + } + break; + case 278: + { + if (*wrap == WRAP_NONE) { + p--; + } + } + break; + case 279: + { + WARN(ZS_UNSUPPORTED_TYPE); + p--; {cs = 268;goto _again;} + } + break; + case 280: + { s->r_type = KNOT_RRTYPE_A; } + break; + case 281: + { s->r_type = KNOT_RRTYPE_NS; } + break; + case 282: + { s->r_type = KNOT_RRTYPE_CNAME; } + break; + case 283: + { s->r_type = KNOT_RRTYPE_SOA; } + break; + case 284: + { s->r_type = KNOT_RRTYPE_PTR; } + break; + case 285: + { s->r_type = KNOT_RRTYPE_HINFO; } + break; + case 286: + { s->r_type = KNOT_RRTYPE_MINFO; } + break; + case 287: + { s->r_type = KNOT_RRTYPE_MX; } + break; + case 288: + { s->r_type = KNOT_RRTYPE_TXT; } + break; + case 289: + { s->r_type = KNOT_RRTYPE_RP; } + break; + case 290: + { s->r_type = KNOT_RRTYPE_AFSDB; } + break; + case 291: + { s->r_type = KNOT_RRTYPE_RT; } + break; + case 292: + { s->r_type = KNOT_RRTYPE_KEY; } + break; + case 293: + { s->r_type = KNOT_RRTYPE_AAAA; } + break; + case 294: + { s->r_type = KNOT_RRTYPE_LOC; } + break; + case 295: + { s->r_type = KNOT_RRTYPE_SRV; } + break; + case 296: + { s->r_type = KNOT_RRTYPE_NAPTR; } + break; + case 297: + { s->r_type = KNOT_RRTYPE_KX; } + break; + case 298: + { s->r_type = KNOT_RRTYPE_CERT; } + break; + case 299: + { s->r_type = KNOT_RRTYPE_DNAME; } + break; + case 300: + { s->r_type = KNOT_RRTYPE_APL; } + break; + case 301: + { s->r_type = KNOT_RRTYPE_DS; } + break; + case 302: + { s->r_type = KNOT_RRTYPE_SSHFP; } + break; + case 303: + { s->r_type = KNOT_RRTYPE_IPSECKEY; } + break; + case 304: + { s->r_type = KNOT_RRTYPE_RRSIG; } + break; + case 305: + { s->r_type = KNOT_RRTYPE_NSEC; } + break; + case 306: + { s->r_type = KNOT_RRTYPE_DNSKEY; } + break; + case 307: + { s->r_type = KNOT_RRTYPE_DHCID; } + break; + case 308: + { s->r_type = KNOT_RRTYPE_NSEC3; } + break; + case 309: + { s->r_type = KNOT_RRTYPE_NSEC3PARAM; } + break; + case 310: + { s->r_type = KNOT_RRTYPE_TLSA; } + break; + case 311: + { s->r_type = KNOT_RRTYPE_CDS; } + break; + case 312: + { s->r_type = KNOT_RRTYPE_CDNSKEY; } + break; + case 313: + { s->r_type = KNOT_RRTYPE_SPF; } + break; + case 314: + { s->r_type = KNOT_RRTYPE_NID; } + break; + case 315: + { s->r_type = KNOT_RRTYPE_L32; } + break; + case 316: + { s->r_type = KNOT_RRTYPE_L64; } + break; + case 317: + { s->r_type = KNOT_RRTYPE_LP; } + break; + case 318: + { s->r_type = KNOT_RRTYPE_EUI48; } + break; + case 319: + { s->r_type = KNOT_RRTYPE_EUI64; } + break; + case 320: + { s->r_type = KNOT_RRTYPE_URI; } + break; + case 321: + { s->r_type = KNOT_RRTYPE_CAA; } + break; + case 322: + { + if (rdata_tail - s->r_data > UINT16_MAX) { + WARN(ZS_RDATA_OVERFLOW); + p--; {cs = 268;goto _again;} + } + s->r_data_length = rdata_tail - s->r_data; + + s->state = ZS_STATE_DATA; + + // Execute the record callback. + if (s->process.automatic) { + if (s->process.record != NULL) { + s->process.record(s); + + // Stop if required from the callback. + if (s->state == ZS_STATE_STOP) { + {p++; goto _out; } + } + } + } else { + // Return if external processing. + p--; {p++; goto _out; } + } + } + break; + } + } + +_again: + if ( cs == 0 ) + goto _out; + if ( ++p != pe ) + goto _resume; + _test_eof: {} + if ( p == eof ) + { + const short *__acts = _zone_scanner_actions + _zone_scanner_eof_actions[cs]; + unsigned int __nacts = (unsigned int) *__acts++; + while ( __nacts-- > 0 ) { + switch ( *__acts++ ) { + case 8: + { + WARN(ZS_BAD_REST); + p--; {cs = 268; if ( p == pe ) + goto _test_eof; +goto _again;} + } + break; + case 18: + { + WARN(ZS_BAD_NUMBER); + p--; {cs = 268; if ( p == pe ) + goto _test_eof; +goto _again;} + } + break; + case 23: + { + WARN(ZS_BAD_DNAME_CHAR); + p--; {cs = 268; if ( p == pe ) + goto _test_eof; +goto _again;} + } + break; + case 30: + { + s->r_owner_length = 0; + WARN(ZS_BAD_OWNER); + p--; {cs = 268; if ( p == pe ) + goto _test_eof; +goto _again;} + } + break; + case 35: + { + WARN(ZS_BAD_NUMBER); + p--; {cs = 268; if ( p == pe ) + goto _test_eof; +goto _again;} + } + break; + case 47: + { + WARN(ZS_BAD_TIME_UNIT); + p--; {cs = 268; if ( p == pe ) + goto _test_eof; +goto _again;} + } + break; + case 57: + { + WARN(ZS_BAD_TIMESTAMP_CHAR); + p--; {cs = 268; if ( p == pe ) + goto _test_eof; +goto _again;} + } + break; + case 59: + { + WARN(ZS_BAD_TEXT_CHAR); + p--; {cs = 268; if ( p == pe ) + goto _test_eof; +goto _again;} + } + break; + case 60: + { + WARN(ZS_BAD_TEXT); + p--; {cs = 268; if ( p == pe ) + goto _test_eof; +goto _again;} + } + break; + case 64: + { + WARN(ZS_BAD_NUMBER); + p--; {cs = 268; if ( p == pe ) + goto _test_eof; +goto _again;} + } + break; + case 67: + { + s->long_string = false; + } + break; + case 69: + { + ERR(ZS_BAD_TTL); + p--; {cs = 268; if ( p == pe ) + goto _test_eof; +goto _again;} + } + break; + case 73: + { + ERR(ZS_BAD_ORIGIN); + p--; {cs = 268; if ( p == pe ) + goto _test_eof; +goto _again;} + } + break; + case 77: + { + ERR(ZS_BAD_INCLUDE_FILENAME); + p--; {cs = 268; if ( p == pe ) + goto _test_eof; +goto _again;} + } + break; + case 80: + { + ERR(ZS_BAD_INCLUDE_ORIGIN); + p--; {cs = 268; if ( p == pe ) + goto _test_eof; +goto _again;} + } + break; + case 84: + { + NOERR; + } + break; + case 85: + { + ERR(ZS_BAD_DIRECTIVE); + p--; {cs = 268; if ( p == pe ) + goto _test_eof; +goto _again;} + } + break; + case 92: + { + WARN(ZS_BAD_ADDRESS_CHAR); + p--; {cs = 268; if ( p == pe ) + goto _test_eof; +goto _again;} + } + break; + case 103: + { + WARN(ZS_BAD_APL); + p--; {cs = 268; if ( p == pe ) + goto _test_eof; +goto _again;} + } + break; + case 106: + { + WARN(ZS_BAD_HEX_CHAR); + p--; {cs = 268; if ( p == pe ) + goto _test_eof; +goto _again;} + } + break; + case 108: + { + WARN(ZS_BAD_HEX_RDATA); + p--; {cs = 268; if ( p == pe ) + goto _test_eof; +goto _again;} + } + break; + case 113: + { + WARN(ZS_BAD_BASE64_CHAR); + p--; {cs = 268; if ( p == pe ) + goto _test_eof; +goto _again;} + } + break; + case 123: + { + WARN(ZS_BAD_BASE32HEX_CHAR); + p--; {cs = 268; if ( p == pe ) + goto _test_eof; +goto _again;} + } + break; + case 151: + { + WARN(ZS_BAD_GATEWAY); + p--; {cs = 268; if ( p == pe ) + goto _test_eof; +goto _again;} + } + break; + case 152: + { + WARN(ZS_BAD_GATEWAY_KEY); + p--; {cs = 268; if ( p == pe ) + goto _test_eof; +goto _again;} + } + break; + case 153: + { + WARN(ZS_UNSUPPORTED_TYPE); + p--; {cs = 268; if ( p == pe ) + goto _test_eof; +goto _again;} + } + break; + case 241: + { + WARN(ZS_BAD_BITMAP); + p--; {cs = 268; if ( p == pe ) + goto _test_eof; +goto _again;} + } + break; + case 258: + { + WARN(ZS_BAD_LOC_DATA); + p--; {cs = 268; if ( p == pe ) + goto _test_eof; +goto _again;} + } + break; + case 259: + { + WARN(ZS_BAD_HEX_RDATA); + p--; {cs = 268; if ( p == pe ) + goto _test_eof; +goto _again;} + } + break; + case 264: + { + WARN(ZS_BAD_CHAR_DASH); + p--; {cs = 268; if ( p == pe ) + goto _test_eof; +goto _again;} + } + break; + case 268: + { + WARN(ZS_BAD_CHAR_COLON); + p--; {cs = 268; if ( p == pe ) + goto _test_eof; +goto _again;} + } + break; + case 269: + { + WARN(ZS_BAD_ALGORITHM); + p--; {cs = 268; if ( p == pe ) + goto _test_eof; +goto _again;} + } + break; + case 270: + { + WARN(ZS_BAD_CERT_TYPE); + p--; {cs = 268; if ( p == pe ) + goto _test_eof; +goto _again;} + } + break; + case 274: + { + WARN(ZS_BAD_RDATA); + p--; {cs = 268; if ( p == pe ) + goto _test_eof; +goto _again;} + } + break; + case 279: + { + WARN(ZS_UNSUPPORTED_TYPE); + p--; {cs = 268; if ( p == pe ) + goto _test_eof; +goto _again;} + } + break; + } + } + } + + _out: {} + } + + + // Check if the scanner state machine is in an uncovered state. + bool extra_error = false; + if (cs == 0) { + ERR(ZS_UNCOVERED_STATE); + extra_error = true; + // Check for an unclosed multiline record. + } else if (s->input.eof && s->multiline) { + ERR(ZS_UNCLOSED_MULTILINE); + extra_error = true; + } + + // Treat the extra error. + if (extra_error) { + s->error.counter++; + s->state = ZS_STATE_ERROR; + + // Copy the error context just for the part of the current line. + s->buffer_length = 0; + while (p < pe && *p != '\n' && s->buffer_length < 50) { + s->buffer[s->buffer_length++] = *p++; + } + s->buffer[s->buffer_length++] = 0; + + // Execute the error callback. + if (s->process.automatic && s->process.error != NULL) { + s->process.error(s); + } + + return; + } + + // Storing scanner states. + s->cs = cs; + s->top = top; + memcpy(s->stack, stack, sizeof(stack)); + + // Store the current parser position. + s->input.current = p; + + // Storing r_data pointer. + s->r_data_tail = rdata_tail - s->r_data; + + if (*wrap == WRAP_DETECTED) { + if (set_input_string(s, "\\", 1, true) != 0) { + return; + } + + *wrap = WRAP_PROCESS; + parse(s, wrap); + } else { + *wrap = WRAP_NONE; + } +} + +__attribute__((visibility("default"))) +int zs_parse_record( + zs_scanner_t *s) +{ + if (s == NULL) { + return -1; + } + + // Check if parsing is possible. + switch (s->state) { + case ZS_STATE_NONE: + case ZS_STATE_DATA: + case ZS_STATE_INCLUDE: + break; + case ZS_STATE_ERROR: + if (s->error.fatal) { + return -1; + } + break; + default: + // Return if stop or end of file. + return 0; + } + + // Check for the end of the input. + if (s->input.current != s->input.end) { + // Try to parse another item. + s->state = ZS_STATE_NONE; + wrap_t wrap = WRAP_NONE; + parse(s, &wrap); + + // Finish if nothing was parsed. + if (s->state == ZS_STATE_NONE) { + // Parse the final block. + if (set_input_string(s, "\n", 1, true) != 0) { + return -1; + } + parse(s, &wrap); + if (s->state == ZS_STATE_NONE) { + s->state = ZS_STATE_EOF; + } + } + } else { + s->state = ZS_STATE_EOF; + } + + return 0; +} + +__attribute__((visibility("default"))) +int zs_parse_all( + zs_scanner_t *s) +{ + if (s == NULL) { + return -1; + } + + s->process.automatic = true; + + // Parse input block. + wrap_t wrap = WRAP_NONE; + parse(s, &wrap); + + // Parse trailing newline-char block if it makes sense. + if (s->state != ZS_STATE_STOP && !s->error.fatal) { + if (set_input_string(s, "\n", 1, true) != 0) { + return -1; + } + parse(s, &wrap); + } + + // Check if any errors have occurred. + if (s->error.counter > 0) { + return -1; + } + + return 0; +} diff --git a/src/libzscanner/scanner.h b/src/libzscanner/scanner.h new file mode 100644 index 0000000..283dce5 --- /dev/null +++ b/src/libzscanner/scanner.h @@ -0,0 +1,370 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \file + * + * \brief Zone scanner core interface. + * + * \addtogroup zscanner + * @{ + */ + +#pragma once + +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> + +#include "libzscanner/error.h" + +/*! \brief Maximal length of rdata. */ +#define ZS_MAX_RDATA_LENGTH 65535 +/*! \brief Maximal length of domain name. */ +#define ZS_MAX_DNAME_LENGTH 255 +/*! \brief Maximal length of domain name label. */ +#define ZS_MAX_LABEL_LENGTH 63 + +/*! \brief Length of ipv4 address in the wire format. */ +#define ZS_INET4_ADDR_LENGTH 4 +/*! \brief Length of ipv6 address in the wire format. */ +#define ZS_INET6_ADDR_LENGTH 16 + +/*! \brief Number of bitmap windows. */ +#define ZS_BITMAP_WINDOWS 256 + +/*! \brief Ragel call stack size (see Ragel internals). */ +#define ZS_RAGEL_STACK_SIZE 16 + +/*! \brief Auxiliary structure for storing bitmap window items (see RFC4034). */ +typedef struct { + uint8_t bitmap[32]; + uint8_t length; +} zs_win_t; + +/*! \brief Auxiliary structure for storing one APL record (see RFC3123). */ +typedef struct { + uint8_t excl_flag; + uint16_t addr_family; + uint8_t prefix_length; +} zs_apl_t; + +/*! \brief Auxiliary structure for storing LOC information (see RFC1876). */ +typedef struct { + uint32_t d1, d2; + uint32_t m1, m2; + uint32_t s1, s2; + uint32_t alt; + uint64_t siz, hp, vp; + int8_t lat_sign, long_sign, alt_sign; +} zs_loc_t; + +/*! \brief Scanner states describing the result. */ +typedef enum { + ZS_STATE_NONE, /*!< Initial state (no data). */ + ZS_STATE_DATA, /*!< A record parsed. */ + ZS_STATE_ERROR, /*!< An error occurred. */ + ZS_STATE_INCLUDE, /*!< An include directive (see include_filename, buffer). */ + ZS_STATE_EOF, /*!< The end of the current input reached. */ + ZS_STATE_STOP /*!< Early stop (possibly set from a callback). */ +} zs_state_t; + +/*! + * \brief Context structure for zone scanner. + * + * This structure contains following items: + * - Copies of Ragel internal variables. The scanner can be called many times + * on smaller parts of zone file/memory. So it is necessary to preserve + * internal values between subsequent scanner callings. + * - Auxiliary variables which are used during processing zone data. + * - Pointers to callback functions and pointer to any arbitrary data which + * can be used in callback functions. + * - Zone file and error information. + * - Output variables (r_ prefix) containing all parts of zone record. These + * data are useful during processing via callback function. + */ +typedef struct zs_scanner zs_scanner_t; // Forward declaration due to arguments. +struct zs_scanner { + /*! Current state (Ragel internals). */ + int cs; + /*! Stack top (Ragel internals). */ + int top; + /*! Call stack (Ragel internals). */ + int stack[ZS_RAGEL_STACK_SIZE]; + + /*! Indicates whether current record is multiline. */ + bool multiline; + /*! Auxiliary number for all numeric operations. */ + uint64_t number64; + /*! Auxiliary variable for time and other numeric operations. */ + uint64_t number64_tmp; + /*! Auxiliary variable for float numeric operations. */ + uint32_t decimals; + /*! Auxiliary variable for float numeric operations. */ + uint32_t decimal_counter; + + /*! Auxiliary variable for item length (label, base64, ...). */ + uint32_t item_length; + /*! Auxiliary index for item length position in array. */ + uint32_t item_length_position; + /*! Auxiliary pointer to item length. */ + uint8_t *item_length_location; + /*! Auxiliary buffer length. Is zero if no comment after a valid record. */ + uint32_t buffer_length; + /*! Auxiliary buffer. Contains a comment after a valid record. */ + uint8_t buffer[ZS_MAX_RDATA_LENGTH]; + /*! Auxiliary buffer for current included file name. */ + char include_filename[ZS_MAX_RDATA_LENGTH]; + /*! Absolute path for relative includes. */ + char *path; + + /*! Auxiliary array of bitmap window blocks. */ + zs_win_t windows[ZS_BITMAP_WINDOWS]; + /*! Last window block which is used (-1 means no window). */ + int16_t last_window; + /*! Auxiliary apl structure. */ + zs_apl_t apl; + /*! Auxiliary loc structure. */ + zs_loc_t loc; + /*! Auxiliary IP address storage. */ + uint8_t addr[ZS_INET6_ADDR_LENGTH]; + /*! Allow text strings longer than 255 characters. */ + bool long_string; + + /*! Pointer to the actual dname storage (origin/owner/rdata). */ + uint8_t *dname; + /*! Pointer to the actual dname length storage. */ + uint32_t *dname_length; + /*! + * Temporary dname length which is copied to dname_length after + * dname processing. + */ + uint32_t dname_tmp_length; + /*! Position of the last free r_data byte. */ + uint32_t r_data_tail; + + /*! Length of the current origin. */ + uint32_t zone_origin_length; + /*! + * Wire format of the current origin (ORIGIN directive sets this). + * + * \note Maximal dname length check is after each valid label. + */ + uint8_t zone_origin[ZS_MAX_DNAME_LENGTH + ZS_MAX_LABEL_LENGTH]; + /*! Value of the default class. */ + uint16_t default_class; + /*! Value of the current default ttl (TTL directive sets this). */ + uint32_t default_ttl; + + /*! The current processing state. */ + zs_state_t state; + + /*! Processing callbacks and auxiliary data. */ + struct { + /*! Automatic zone processing using record/error callbacks. */ + bool automatic; + /*! Callback function for correct zone record. */ + void (*record)(zs_scanner_t *); + /*! Callback function for wrong situations. */ + void (*error)(zs_scanner_t *); + /*! Arbitrary data useful inside callback functions. */ + void *data; + } process; + + /*! Input parameters. */ + struct { + /*! Start of the block. */ + const char *start; + /*! Current parser position. */ + const char *current; + /*! End of the block. */ + const char *end; + /*! Indication for the final block parsing. */ + bool eof; + /*! Indication of being mmap()-ed (malloc()-ed otherwise). */ + bool mmaped; + } input; + + /*! File input parameters. */ + struct { + /*! Zone file name. */ + char *name; + /*!< File descriptor. */ + int descriptor; + } file; + + struct { + /*! Last occurred error/warning code. */ + int code; + /*! Error/warning counter. */ + uint64_t counter; + /*! Indicates serious error - parsing cannot continue. */ + bool fatal; + } error; + + /*! Zone data line counter. */ + uint64_t line_counter; + + /*! Length of the current record owner. */ + uint32_t r_owner_length; + /*! + * Owner of the current record. + * + * \note Maximal dname length check is after each valid label. + */ + uint8_t r_owner[ZS_MAX_DNAME_LENGTH + ZS_MAX_LABEL_LENGTH]; + /*! Class of the current record. */ + uint16_t r_class; + /*! TTL of the current record. */ + uint32_t r_ttl; + /*! Type of the current record data. */ + uint16_t r_type; + /*! Length of the current rdata. */ + uint32_t r_data_length; + /*! Current rdata. */ + uint8_t r_data[ZS_MAX_RDATA_LENGTH]; + + /* + * Example: a. IN 60 MX 1 b. ; A comment + * + * r_owner_length = 3 + * r_owner = 016100 + * r_class = 1 + * r_ttl = 60 + * r_type = 15 + * r_data_length = 5 + * r_data = 0001016200 + * buffer_length = 11 + * buffer = " A comment" + */ +}; + +/*! + * \brief Initializes the scanner context. + * + * \note Error code is stored in the scanner context. + * + * \param scanner Scanner context. + * \param origin Initial zone origin. + * \param rclass Zone class value. + * \param ttl Initial ttl value. + * + * \retval 0 if success. + * \retval -1 if error. + */ +int zs_init( + zs_scanner_t *scanner, + const char *origin, + const uint16_t rclass, + const uint32_t ttl +); + +/*! + * \brief Deinitializes the scanner context. + * + * \param scanner Scanner context. + */ +void zs_deinit( + zs_scanner_t *scanner +); + +/*! + * \brief Sets the scanner to parse a zone data string. + * + * \note Error code is stored in the scanner context. + * + * \param scanner Scanner context. + * \param input Input zone data string to parse. + * \param size Size of the input string. + * + * \retval 0 if success. + * \retval -1 if error. + */ +int zs_set_input_string( + zs_scanner_t *scanner, + const char *input, + size_t size +); + +/*! + * \brief Sets the scanner to parse a zone file.. + * + * \note Error code is stored in the scanner context. + * + * \param scanner Scanner context. + * \param file_name Name of the file to parse. + * + * \retval 0 if success. + * \retval -1 if error. + */ +int zs_set_input_file( + zs_scanner_t *scanner, + const char *file_name +); + +/*! + * \brief Sets the scanner processing callbacks for automatic processing. + * + * \note Error code is stored in the scanner context. + * + * \param scanner Scanner context. + * \param process_record Processing callback function (may be NULL). + * \param process_error Error callback function (may be NULL). + * \param data Arbitrary data useful in callback functions. + * + * \retval 0 if success. + * \retval -1 if error. + */ +int zs_set_processing( + zs_scanner_t *scanner, + void (*process_record)(zs_scanner_t *), + void (*process_error)(zs_scanner_t *), + void *data +); + +/*! + * \brief Parses one record from the input. + * + * The following processing should be based on the scanner->state. + * + * \note Error code and other information are stored in the scanner context. + * + * \param scanner Scanner context. + * + * \retval 0 if success. + * \retval -1 if error. + */ +int zs_parse_record( + zs_scanner_t *scanner +); + +/*! + * \brief Launches automatic parsing of the whole input. + * + * For each correctly recognized record, the record callback is executed. + * If any syntax error occurs, the error callback is executed. + * + * \note Error code and other information are stored in the scanner context. + * + * \param scanner Scanner context. + * + * \retval 0 if success. + * \retval -1 if error. + */ +int zs_parse_all( + zs_scanner_t *scanner +); + +/*! @} */ diff --git a/src/libzscanner/scanner.rl b/src/libzscanner/scanner.rl new file mode 100644 index 0000000..4050b56 --- /dev/null +++ b/src/libzscanner/scanner.rl @@ -0,0 +1,541 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <arpa/inet.h> +#include <fcntl.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <libgen.h> +#include <math.h> +#include <netinet/in.h> +#include <sys/socket.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include "libzscanner/scanner.h" +#include "libzscanner/functions.h" +#include "libknot/descriptor.h" + +/*! \brief Maximal length of rdata item. */ +#define MAX_ITEM_LENGTH 255 + +/*! \brief Latitude value for equator (2^31). */ +#define LOC_LAT_ZERO (uint32_t)2147483648 +/*! \brief Longitude value for meridian (2^31). */ +#define LOC_LONG_ZERO (uint32_t)2147483648 +/*! \brief Zero level altitude value. */ +#define LOC_ALT_ZERO (uint32_t)10000000 + +/*! \brief Shorthand for setting warning data. */ +#define WARN(err_code) { s->error.code = err_code; } +/*! \brief Shorthand for setting error data. */ +#define ERR(err_code) { WARN(err_code); s->error.fatal = true; } +/*! \brief Shorthand for error reset. */ +#define NOERR { WARN(ZS_OK); s->error.fatal = false; } + +/*! + * \brief Writes record type number to r_data. + * + * \param type Type number. + * \param rdata_tail Position where to write type number to. + */ +static inline void type_num(const uint16_t type, uint8_t **rdata_tail) +{ + *((uint16_t *)*rdata_tail) = htons(type); + *rdata_tail += 2; +} + +/*! + * \brief Sets bit to bitmap window. + * + * \param type Type number. + * \param s Scanner context. + */ +static inline void window_add_bit(const uint16_t type, zs_scanner_t *s) { + uint8_t win = type / 256; + uint8_t bit_pos = type % 256; + uint8_t byte_pos = bit_pos / 8; + + ((s->windows[win]).bitmap)[byte_pos] |= 128 >> (bit_pos % 8); + + if ((s->windows[win]).length < byte_pos + 1) { + (s->windows[win]).length = byte_pos + 1; + } + + if (s->last_window < win) { + s->last_window = win; + } +} + +// Include scanner file (in Ragel). +%%{ + machine zone_scanner; + + include "scanner_body.rl"; + + write data; +}%% + +__attribute__((visibility("default"))) +int zs_init( + zs_scanner_t *s, + const char *origin, + const uint16_t rclass, + const uint32_t ttl) +{ + if (s == NULL) { + return -1; + } + + memset(s, 0, sizeof(*s)); + + // Nonzero initial scanner state. + s->cs = %%{ write start; }%%; + + // Reset the file descriptor. + s->file.descriptor = -1; + + // Use the root zone as origin if not specified. + if (origin == NULL || strlen(origin) == 0) { + origin = "."; + } + size_t origin_len = strlen(origin); + + // Prepare a zone settings header. + const char *format; + if (origin[origin_len - 1] != '.') { + format = "$ORIGIN %s.\n"; + } else { + format = "$ORIGIN %s\n"; + } + + char settings[1024]; + int ret = snprintf(settings, sizeof(settings), format, origin); + if (ret <= 0 || ret >= sizeof(settings)) { + ERR(ZS_ENOMEM); + return -1; + } + + // Parse the settings to set up the scanner origin. + if (zs_set_input_string(s, settings, ret) != 0 || + zs_parse_all(s) != 0) { + return -1; + } + + // Set scanner defaults. + s->path = strdup("."); + if (s->path == NULL) { + ERR(ZS_ENOMEM); + return -1; + } + s->default_class = rclass; + s->default_ttl = ttl; + s->line_counter = 1; + + s->state = ZS_STATE_NONE; + s->process.automatic = false; + + return 0; +} + +static void input_deinit( + zs_scanner_t *s, + bool keep_filename) +{ + // Deinit the file input. + if (s->file.descriptor != -1) { + // Unmap the file content. + if (s->input.start != NULL) { + if (s->input.mmaped) { + munmap((void *)s->input.start, + s->input.end - s->input.start); + } else { + free((void *)s->input.start); + } + } + + // Close the opened file. + close(s->file.descriptor); + s->file.descriptor = -1; + } + + // Keep file name for possible trailing error report. + if (!keep_filename) { + free(s->file.name); + s->file.name = NULL; + } + + // Unset the input limits. + s->input.start = NULL; + s->input.current = NULL; + s->input.end = NULL; + s->input.eof = false; +} + +__attribute__((visibility("default"))) +void zs_deinit( + zs_scanner_t *s) +{ + if (s == NULL) { + return; + } + + input_deinit(s, false); + free(s->path); +} + +static int set_input_string( + zs_scanner_t *s, + const char *input, + size_t size, + bool final_block) +{ + if (s == NULL) { + return -1; + } + + if (input == NULL) { + ERR(ZS_EINVAL); + return -1; + } + + // Deinit possibly opened file. + input_deinit(s, final_block); + + // Set the scanner input limits. + s->input.start = input; + s->input.current = input; + s->input.end = input + size; + s->input.eof = final_block; + + return 0; +} + +static char *read_file_to_buf( + int fd, + size_t *bufsize) +{ + size_t bufs = 0, newbufs = 8192; + char *buf = malloc(bufs + newbufs); + int ret = 0; + + while (buf != NULL && (ret = read(fd, buf + bufs, newbufs)) == newbufs) { + bufs += newbufs; + newbufs = bufs; + char *newbuf = realloc(buf, bufs + newbufs); + if (newbuf == NULL) { + free(buf); + } + buf = newbuf; + } + if (ret < 0) { + free(buf); + return NULL; + } + + *bufsize = bufs + ret; + return buf; +} + +__attribute__((visibility("default"))) +int zs_set_input_string( + zs_scanner_t *s, + const char *input, + size_t size) +{ + s->state = ZS_STATE_NONE; + + return set_input_string(s, input, size, false); +} + +__attribute__((visibility("default"))) +int zs_set_input_file( + zs_scanner_t *s, + const char *file_name) +{ + if (s == NULL) { + return -1; + } + + if (file_name == NULL) { + ERR(ZS_EINVAL); + return -1; + } + + // Deinit possibly opened file. + input_deinit(s, false); + + // Try to open the file. + s->file.descriptor = open(file_name, O_RDONLY); + if (s->file.descriptor == -1) { + ERR(ZS_FILE_OPEN); + return -1; + } + + char *start = NULL; + size_t size = 0; + + // Check the input. + struct stat file_stat; + if (fstat(s->file.descriptor, &file_stat) == -1) { + ERR(ZS_FILE_INVALID); + input_deinit(s, false); + return -1; + } else if (S_ISCHR(file_stat.st_mode) || + S_ISBLK(file_stat.st_mode) || + S_ISFIFO(file_stat.st_mode)) { + // Workaround if cannot mmap, read to memory. + start = read_file_to_buf(s->file.descriptor, &size); + if (start == NULL) { + ERR(ZS_FILE_INVALID); + input_deinit(s, false); + return -1; + } + } else if (!S_ISREG(file_stat.st_mode)) { // Require regular file. + ERR(ZS_FILE_INVALID); + input_deinit(s, false); + return -1; + } else if (file_stat.st_size > 0) { // Mmap non-emtpy file. + start = mmap(0, file_stat.st_size, PROT_READ, MAP_SHARED, + s->file.descriptor, 0); + if (start == MAP_FAILED) { + ERR(ZS_FILE_INVALID); + input_deinit(s, false); + return -1; + } + + size = file_stat.st_size; + s->input.mmaped = true; + + // Try to set the mapped memory advise to sequential. + (void)madvise(start, size, MADV_SEQUENTIAL); + } + + // Set the scanner input limits. + s->input.start = start; + s->input.current = start; + s->input.end = start + size; + + // Get absolute path of the zone file if possible. + char *full_name = realpath(file_name, NULL); + if (full_name != NULL) { + free(s->path); + s->path = strdup(dirname(full_name)); + free(full_name); + if (s->path == NULL) { + ERR(ZS_ENOMEM); + input_deinit(s, false); + return -1; + } + } + + s->file.name = strdup(file_name); + if (s->file.name == NULL) { + ERR(ZS_ENOMEM); + input_deinit(s, false); + return -1; + } + + s->state = ZS_STATE_NONE; + + return 0; +} + +__attribute__((visibility("default"))) +int zs_set_processing( + zs_scanner_t *s, + void (*process_record)(zs_scanner_t *), + void (*process_error)(zs_scanner_t *), + void *data) +{ + if (s == NULL) { + return -1; + } + + s->process.record = process_record; + s->process.error = process_error; + s->process.data = data; + + return 0; +} + +typedef enum { + WRAP_NONE, // Initial state. + WRAP_DETECTED, // Input block end is a first '\' in rdata. + WRAP_PROCESS // Parsing of auxiliary block = "\". +} wrap_t; + +static void parse( + zs_scanner_t *s, + wrap_t *wrap) +{ + // Restore scanner input limits (Ragel internals). + const char *p = s->input.current; + const char *pe = s->input.end; + const char *eof = s->input.eof ? pe : NULL; + + // Restore state variables (Ragel internals). + int cs = s->cs; + int top = s->top; + int stack[ZS_RAGEL_STACK_SIZE]; + memcpy(stack, s->stack, sizeof(stack)); + + // Next 2 variables are for better performance. + // Restoring r_data pointer to next free space. + uint8_t *rdata_tail = s->r_data + s->r_data_tail; + // Initialization of the last r_data byte. + uint8_t *rdata_stop = s->r_data + ZS_MAX_RDATA_LENGTH - 1; + + // Write scanner body (in C). + %% write exec; + + // Check if the scanner state machine is in an uncovered state. + bool extra_error = false; + if (cs == %%{ write error; }%%) { + ERR(ZS_UNCOVERED_STATE); + extra_error = true; + // Check for an unclosed multiline record. + } else if (s->input.eof && s->multiline) { + ERR(ZS_UNCLOSED_MULTILINE); + extra_error = true; + } + + // Treat the extra error. + if (extra_error) { + s->error.counter++; + s->state = ZS_STATE_ERROR; + + // Copy the error context just for the part of the current line. + s->buffer_length = 0; + while (p < pe && *p != '\n' && s->buffer_length < 50) { + s->buffer[s->buffer_length++] = *p++; + } + s->buffer[s->buffer_length++] = 0; + + // Execute the error callback. + if (s->process.automatic && s->process.error != NULL) { + s->process.error(s); + } + + return; + } + + // Storing scanner states. + s->cs = cs; + s->top = top; + memcpy(s->stack, stack, sizeof(stack)); + + // Store the current parser position. + s->input.current = p; + + // Storing r_data pointer. + s->r_data_tail = rdata_tail - s->r_data; + + if (*wrap == WRAP_DETECTED) { + if (set_input_string(s, "\\", 1, true) != 0) { + return; + } + + *wrap = WRAP_PROCESS; + parse(s, wrap); + } else { + *wrap = WRAP_NONE; + } +} + +__attribute__((visibility("default"))) +int zs_parse_record( + zs_scanner_t *s) +{ + if (s == NULL) { + return -1; + } + + // Check if parsing is possible. + switch (s->state) { + case ZS_STATE_NONE: + case ZS_STATE_DATA: + case ZS_STATE_INCLUDE: + break; + case ZS_STATE_ERROR: + if (s->error.fatal) { + return -1; + } + break; + default: + // Return if stop or end of file. + return 0; + } + + // Check for the end of the input. + if (s->input.current != s->input.end) { + // Try to parse another item. + s->state = ZS_STATE_NONE; + wrap_t wrap = WRAP_NONE; + parse(s, &wrap); + + // Finish if nothing was parsed. + if (s->state == ZS_STATE_NONE) { + // Parse the final block. + if (set_input_string(s, "\n", 1, true) != 0) { + return -1; + } + parse(s, &wrap); + if (s->state == ZS_STATE_NONE) { + s->state = ZS_STATE_EOF; + } + } + } else { + s->state = ZS_STATE_EOF; + } + + return 0; +} + +__attribute__((visibility("default"))) +int zs_parse_all( + zs_scanner_t *s) +{ + if (s == NULL) { + return -1; + } + + s->process.automatic = true; + + // Parse input block. + wrap_t wrap = WRAP_NONE; + parse(s, &wrap); + + // Parse trailing newline-char block if it makes sense. + if (s->state != ZS_STATE_STOP && !s->error.fatal) { + if (set_input_string(s, "\n", 1, true) != 0) { + return -1; + } + parse(s, &wrap); + } + + // Check if any errors have occurred. + if (s->error.counter > 0) { + return -1; + } + + return 0; +} diff --git a/src/libzscanner/scanner_body.rl b/src/libzscanner/scanner_body.rl new file mode 100644 index 0000000..022cc9b --- /dev/null +++ b/src/libzscanner/scanner_body.rl @@ -0,0 +1,2088 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +%%{ + machine zone_scanner; + + # Comeback function to calling state machine. + action _ret { + fhold; fret; + } + + # BEGIN - Blank space processing + action _newline { + s->line_counter++; + } + + action _check_multiline_begin { + if (s->multiline == true) { + ERR(ZS_LEFT_PARENTHESIS); + fhold; fgoto err_line; + } + s->multiline = true; + } + action _check_multiline_end { + if (s->multiline == false) { + ERR(ZS_RIGHT_PARENTHESIS); + fhold; fgoto err_line; + } + s->multiline = false; + } + + action _comment_init { + s->buffer_length = 0; + } + action _comment { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = fc; + } + } + action _comment_exit { + s->buffer[s->buffer_length++] = 0; + } + + action _rest_init { + s->buffer[0] = 0; + s->buffer_length = 0; + } + action _rest_error { + WARN(ZS_BAD_REST); + fhold; fgoto err_line; + } + + newline = '\n' $_newline; + comment = (';' . (^newline)* $_comment) >_comment_init %_comment_exit; + + # White space separation. With respect to parentheses and included comments. + sep = ( [ \t] # Blank characters. + | (comment? . newline) when { s->multiline } # Comment in multiline. + | '(' $_check_multiline_begin # Start of multiline. + | ')' $_check_multiline_end # End of multiline. + )+; # Apply more times. + + rest = (sep? :> comment?) >_rest_init $!_rest_error; # Comments. + + # Artificial machines which are used for next state transition only! + all_wchar = [ \t\n;()]; + end_wchar = [\n;] when { !s->multiline }; # For noncontinuous ending tokens. + # END + + # BEGIN - Error line processing + action _err_line_init { + s->buffer_length = 0; + } + action _err_line { + if (fc == '\r') { + ERR(ZS_DOS_NEWLINE); + } + + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = fc; + } + } + action _err_line_exit { + // Terminate the error context string. + s->buffer[s->buffer_length++] = 0; + + // Error counter incrementation. + s->error.counter++; + + // Initialize the fcall stack. + top = 0; + + // Reset the multiline context. + s->multiline = false; + + s->state = ZS_STATE_ERROR; + + // Execute the error callback. + if (s->process.automatic) { + if (s->process.error != NULL) { + s->process.error(s); + + // Stop if required from the callback. + if (s->state == ZS_STATE_STOP) { + fbreak; + } + } + + // Stop the scanner if fatal error. + if (s->error.fatal) { + fbreak; + } + fgoto main; + } else { + // Return if external processing. + fhold; fnext main; fbreak; + } + } + + # Fill rest of the line to buffer and skip to main loop. + err_line := (^newline $_err_line)* >_err_line_init + %_err_line_exit . newline; + # END + + # BEGIN - Domain name labels processing + action _label_init { + s->item_length = 0; + s->item_length_position = s->dname_tmp_length++; + } + action _label_char { + // Check for maximum dname label length. + if (s->item_length < ZS_MAX_LABEL_LENGTH) { + (s->dname)[s->dname_tmp_length++] = fc; + s->item_length++; + } else { + WARN(ZS_LABEL_OVERFLOW); + fhold; fgoto err_line; + } + } + action _label_exit { + // Check for maximum dname length overflow after each label. + // (at least the next label length must follow). + if (s->dname_tmp_length < ZS_MAX_DNAME_LENGTH) { + (s->dname)[s->item_length_position] = + (uint8_t)(s->item_length); + } else { + WARN(ZS_DNAME_OVERFLOW); + fhold; fgoto err_line; + } + } + + action _label_dec_init { + if (s->item_length < ZS_MAX_LABEL_LENGTH) { + (s->dname)[s->dname_tmp_length] = 0; + s->item_length++; + } else { + WARN(ZS_LABEL_OVERFLOW); + fhold; fgoto err_line; + } + } + action _label_dec { + (s->dname)[s->dname_tmp_length] *= 10; + (s->dname)[s->dname_tmp_length] += digit_to_num[(uint8_t)fc]; + } + action _label_dec_exit { + s->dname_tmp_length++; + } + action _label_dec_error { + WARN(ZS_BAD_NUMBER); + fhold; fgoto err_line; + } + + label_char = + ( (alnum | [*\-_/]) $_label_char # One common char. + | ('\\' . ^digit) @_label_char # One "\x" char. + | ('\\' %_label_dec_init # Initial "\" char. + . digit {3} $_label_dec %_label_dec_exit # "DDD" rest. + $!_label_dec_error + ) + ); + + label = label_char+ >_label_init %_label_exit; + labels = (label . '.')* . label; + # END + + # BEGIN - Domain name processing. + action _absolute_dname_exit { + // Enough room for the terminal label is guaranteed (_label_exit). + (s->dname)[s->dname_tmp_length++] = 0; + } + action _relative_dname_exit { + // Check for (relative + origin) dname length overflow. + if (s->dname_tmp_length + s->zone_origin_length <= ZS_MAX_DNAME_LENGTH) { + memcpy(s->dname + s->dname_tmp_length, + s->zone_origin, + s->zone_origin_length); + + s->dname_tmp_length += s->zone_origin_length; + } else { + WARN(ZS_DNAME_OVERFLOW); + fhold; fgoto err_line; + } + } + action _origin_dname_exit { + // Copy already verified zone origin. + memcpy(s->dname, + s->zone_origin, + s->zone_origin_length); + + s->dname_tmp_length = s->zone_origin_length; + } + + action _dname_init { + s->item_length_position = 0; + s->dname_tmp_length = 0; + } + action _dname_error { + WARN(ZS_BAD_DNAME_CHAR); + fhold; fgoto err_line; + } + + relative_dname = (labels ) >_dname_init %_relative_dname_exit; + absolute_dname = (labels? . '.') >_dname_init %_absolute_dname_exit; + + dname_ := ( relative_dname + | absolute_dname + | '@' %_origin_dname_exit + ) $!_dname_error %_ret . all_wchar; + dname = (alnum | [\-_/\\] | [*.@]) ${ fhold; fcall dname_; }; + # END + + # BEGIN - Common r_data item processing + action _item_length_init { + if (rdata_tail <= rdata_stop) { + s->item_length_location = rdata_tail++; + } else { + WARN(ZS_RDATA_OVERFLOW); + fhold; fgoto err_line; + } + } + action _item_length_exit { + s->item_length = rdata_tail - s->item_length_location - 1; + + if (s->item_length <= MAX_ITEM_LENGTH) { + *(s->item_length_location) = (uint8_t)(s->item_length); + } else { + WARN(ZS_ITEM_OVERFLOW); + fhold; fgoto err_line; + } + } + # END + + # BEGIN - Owner processing + action _r_owner_init { + s->dname = s->r_owner; + s->r_owner_length = 0; + } + action _r_owner_exit { + s->r_owner_length = s->dname_tmp_length; + } + action _r_owner_empty_exit { + if (s->r_owner_length == 0) { + WARN(ZS_BAD_PREVIOUS_OWNER); + fhold; fgoto err_line; + } + } + action _r_owner_error { + s->r_owner_length = 0; + WARN(ZS_BAD_OWNER); + fhold; fgoto err_line; + } + + r_owner = ( dname >_r_owner_init %_r_owner_exit + | zlen %_r_owner_empty_exit # Empty owner - use the previous one. + ) $!_r_owner_error; + # END + + # BEGIN - domain name in record data processing + action _r_dname_init { + s->dname = rdata_tail; + } + action _r_dname_exit { + rdata_tail += s->dname_tmp_length; + } + + r_dname = dname >_r_dname_init %_r_dname_exit; + # END + + # BEGIN - Number processing + action _number_digit { + // Overflow check: 10*(s->number64) + fc - '0' <= UINT64_MAX + if ((s->number64 < (UINT64_MAX / 10)) || // Dominant fast check. + ((s->number64 == (UINT64_MAX / 10)) && // Marginal case. + ((uint8_t)fc <= (UINT64_MAX % 10) + '0') + ) + ) { + s->number64 *= 10; + s->number64 += digit_to_num[(uint8_t)fc]; + } else { + WARN(ZS_NUMBER64_OVERFLOW); + fhold; fgoto err_line; + } + } + + number_digit = [0-9] $_number_digit; + + action _number_init { + s->number64 = 0; + } + action _number_error { + WARN(ZS_BAD_NUMBER); + fhold; fgoto err_line; + } + + # General integer number that cover all necessary integer ranges. + number = number_digit+ >_number_init; + + action _float_init { + s->decimal_counter = 0; + } + action _decimal_init { + s->number64_tmp = s->number64; + } + action _decimal_digit { + s->decimal_counter++; + } + + action _float_exit { + if (s->decimal_counter == 0 && s->number64 < UINT32_MAX) { + s->number64 *= pow(10, s->decimals); + } else if (s->decimal_counter <= s->decimals && + s->number64_tmp < UINT32_MAX) { + s->number64 *= pow(10, s->decimals - s->decimal_counter); + s->number64 += s->number64_tmp * pow(10, s->decimals); + } else { + WARN(ZS_FLOAT_OVERFLOW); + fhold; fgoto err_line; + } + } + + # Next float can't be used directly (doesn't contain decimals init)! + float = (number . ('.' . number? >_decimal_init $_decimal_digit)?) + >_float_init %_float_exit; + + action _float2_init { + s->decimals = 2; + } + action _float3_init { + s->decimals = 3; + } + + # Float number (in hundredths)with 2 possible decimal digits. + float2 = float >_float2_init; + # Float number (in thousandths) with 3 possible decimal digits. + float3 = float >_float3_init; + + action _num8_write { + if (s->number64 <= UINT8_MAX) { + *rdata_tail = (uint8_t)(s->number64); + rdata_tail += 1; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + fhold; fgoto err_line; + } + } + action _num16_write { + if (s->number64 <= UINT16_MAX) { + uint16_t num16 = htons((uint16_t)s->number64); + memcpy(rdata_tail, &num16, 2); + rdata_tail += 2; + } else { + WARN(ZS_NUMBER16_OVERFLOW); + fhold; fgoto err_line; + } + } + action _num32_write { + if (s->number64 <= UINT32_MAX) { + uint32_t num32 = htonl((uint32_t)s->number64); + memcpy(rdata_tail, &num32, 4); + rdata_tail += 4; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + fhold; fgoto err_line; + } + } + + action _type_number_exit { + if (s->number64 <= UINT16_MAX) { + s->r_type = (uint16_t)(s->number64); + } else { + WARN(ZS_NUMBER16_OVERFLOW); + fhold; fgoto err_line; + } + } + + action _length_number_exit { + if (s->number64 <= UINT16_MAX) { + s->r_data_length = (uint16_t)(s->number64); + } else { + WARN(ZS_NUMBER16_OVERFLOW); + fhold; fgoto err_line; + } + } + num8 = number %_num8_write $!_number_error; + num16 = number %_num16_write $!_number_error; + num32 = number %_num32_write $!_number_error; + + type_number = number %_type_number_exit $!_number_error; + length_number = number %_length_number_exit $!_number_error; + # END + + # BEGIN - Time processing + action _time_unit_error { + WARN(ZS_BAD_TIME_UNIT); + fhold; fgoto err_line; + } + + time_unit = + ( 's'i + | 'm'i ${ if (s->number64 <= (UINT32_MAX / 60)) { + s->number64 *= 60; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + fhold; fgoto err_line; + } + } + | 'h'i ${ if (s->number64 <= (UINT32_MAX / 3600)) { + s->number64 *= 3600; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + fhold; fgoto err_line; + } + } + | 'd'i ${ if (s->number64 <= (UINT32_MAX / 86400)) { + s->number64 *= 86400; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + fhold; fgoto err_line; + } + } + | 'w'i ${ if (s->number64 <= (UINT32_MAX / 604800)) { + s->number64 *= 604800; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + fhold; fgoto err_line; + } + } + ) $!_time_unit_error; + + + action _time_block_init { + s->number64_tmp = s->number64; + } + action _time_block_exit { + if (s->number64 + s->number64_tmp < UINT32_MAX) { + s->number64 += s->number64_tmp; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + fhold; fgoto err_line; + } + } + + time_block = (number . time_unit) >_time_block_init %_time_block_exit; + + # Time is either a number or a sequence of time blocks (1w1h1m). + time = (number . (time_unit . (time_block)*)?) $!_number_error; + + time32 = time %_num32_write; + # END + + # BEGIN - Timestamp processing + action _timestamp_init { + s->buffer_length = 0; + } + action _timestamp { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = fc; + } else { + WARN(ZS_RDATA_OVERFLOW); + fhold; fgoto err_line; + } + } + action _timestamp_exit { + s->buffer[s->buffer_length] = 0; + + if (s->buffer_length == 14) { // Date; 14 = len("YYYYMMDDHHmmSS"). + uint32_t timestamp; + int ret = date_to_timestamp(s->buffer, ×tamp); + + if (ret == ZS_OK) { + *((uint32_t *)rdata_tail) = htonl(timestamp); + rdata_tail += 4; + } else { + WARN(ret); + fhold; fgoto err_line; + } + } else if (s->buffer_length <= 10) { // Timestamp format. + char *end; + + s->number64 = strtoull((char *)(s->buffer), &end, 10); + + if (end == (char *)(s->buffer) || *end != '\0') { + WARN(ZS_BAD_TIMESTAMP); + fhold; fgoto err_line; + } + + if (s->number64 <= UINT32_MAX) { + *((uint32_t *)rdata_tail) = htonl((uint32_t)s->number64); + rdata_tail += 4; + } else { + WARN(ZS_NUMBER32_OVERFLOW); + fhold; fgoto err_line; + } + } else { + WARN(ZS_BAD_TIMESTAMP_LENGTH); + fhold; fgoto err_line; + } + } + action _timestamp_error { + WARN(ZS_BAD_TIMESTAMP_CHAR); + fhold; fgoto err_line; + } + + timestamp = digit+ >_timestamp_init $_timestamp + %_timestamp_exit $!_timestamp_error; + # END + + # BEGIN - Text processing + action _text_char { + if (rdata_tail <= rdata_stop) { + // Split long string. + if (s->long_string && + rdata_tail - s->item_length_location == 1 + MAX_ITEM_LENGTH) { + // _item_length_exit equivalent. + *(s->item_length_location) = MAX_ITEM_LENGTH; + // _item_length_init equivalent. + s->item_length_location = rdata_tail++; + + if (rdata_tail > rdata_stop) { + WARN(ZS_TEXT_OVERFLOW); + fhold; fgoto err_line; + } + } + + *(rdata_tail++) = fc; + } else { + WARN(ZS_TEXT_OVERFLOW); + fhold; fgoto err_line; + } + } + action _text_char_error { + WARN(ZS_BAD_TEXT_CHAR); + fhold; fgoto err_line; + } + action _text_error { + WARN(ZS_BAD_TEXT); + fhold; fgoto err_line; + } + + action _text_dec_init { + if (rdata_tail <= rdata_stop) { + // Split long string. + if (s->long_string && + rdata_tail - s->item_length_location == 1 + MAX_ITEM_LENGTH) { + // _item_length_exit equivalent. + *(s->item_length_location) = MAX_ITEM_LENGTH; + // _item_length_init equivalent. + s->item_length_location = rdata_tail++; + + if (rdata_tail > rdata_stop) { + WARN(ZS_TEXT_OVERFLOW); + fhold; fgoto err_line; + } + } + + *rdata_tail = 0; + s->item_length++; + } else { + WARN(ZS_TEXT_OVERFLOW); + fhold; fgoto err_line; + } + } + action _text_dec { + if ((*rdata_tail < (UINT8_MAX / 10)) || // Dominant fast check. + ((*rdata_tail == (UINT8_MAX / 10)) && // Marginal case. + (fc <= (UINT8_MAX % 10) + '0') + ) + ) { + *rdata_tail *= 10; + *rdata_tail += digit_to_num[(uint8_t)fc]; + } else { + WARN(ZS_NUMBER8_OVERFLOW); + fhold; fgoto err_line; + } + } + action _text_dec_exit { + rdata_tail++; + } + action _text_dec_error { + WARN(ZS_BAD_NUMBER); + fhold; fgoto err_line; + } + + text_char = + ( (33..126 - [\\;\"]) $_text_char # One printable char. + | ('\\' . (32..126 - digit)) @_text_char # One "\x" char. + | ('\\' %_text_dec_init # Initial "\" char. + . digit {3} $_text_dec %_text_dec_exit # "DDD" rest. + $!_text_dec_error + ) + ) $!_text_char_error; + + quoted_text_char = + ( text_char + | ([ \t;] | [\n] when { s->multiline }) $_text_char + ) $!_text_char_error; + + # Text string machine instantiation (for smaller code). + text_ := (('\"' . quoted_text_char* . '\"') | text_char+) + $!_text_error %_ret . all_wchar; + text = ^all_wchar ${ fhold; fcall text_; }; + + # Text string with forward 1-byte length. + text_string = text >_item_length_init %_item_length_exit; + + action _text_array_init { + s->long_string = true; + } + action _text_array_exit { + s->long_string = false; + } + + # Text string array as one rdata item. + text_array = + ( (text_string . (sep . text_string)* . sep?) + ) >_text_array_init %_text_array_exit $!_text_array_exit; + # END + + # BEGIN - TTL directive processing + action _default_ttl_exit { + if (s->number64 <= UINT32_MAX) { + s->default_ttl = (uint32_t)(s->number64); + } else { + ERR(ZS_NUMBER32_OVERFLOW); + fhold; fgoto err_line; + } + } + action _default_ttl_error { + ERR(ZS_BAD_TTL); + fhold; fgoto err_line; + } + + default_ttl_ := (sep . time . rest) $!_default_ttl_error + %_default_ttl_exit %_ret . newline; + default_ttl = all_wchar ${ fhold; fcall default_ttl_; }; + # END + + # BEGIN - ORIGIN directive processing + action _zone_origin_init { + s->dname = s->zone_origin; + } + action _zone_origin_exit { + s->zone_origin_length = s->dname_tmp_length; + } + action _zone_origin_error { + ERR(ZS_BAD_ORIGIN); + fhold; fgoto err_line; + } + + zone_origin_ := (sep . absolute_dname >_zone_origin_init . rest) + $!_zone_origin_error %_zone_origin_exit %_ret . newline; + zone_origin = all_wchar ${ fhold; fcall zone_origin_; }; + # END + + # BEGIN - INCLUDE directive processing + action _incl_filename_init { + rdata_tail = s->r_data; + } + action _incl_filename_exit { + size_t len = rdata_tail - s->r_data; + if (len >= sizeof(s->include_filename)) { + ERR(ZS_BAD_INCLUDE_FILENAME); + fhold; fgoto err_line; + } + + // Store zero terminated include filename. + memcpy(s->include_filename, s->r_data, len); + s->include_filename[len] = '\0'; + + // For detection whether origin is not present. + s->dname = NULL; + } + action _incl_filename_error { + ERR(ZS_BAD_INCLUDE_FILENAME); + fhold; fgoto err_line; + } + + action _incl_origin_init { + s->dname = s->r_data; + } + action _incl_origin_exit { + s->r_data_length = s->dname_tmp_length; + } + action _incl_origin_error { + ERR(ZS_BAD_INCLUDE_ORIGIN); + fhold; fgoto err_line; + } + + action _include_exit { + // Extend relative file path. + if (s->include_filename[0] != '/') { + int ret = snprintf((char *)(s->buffer), sizeof(s->buffer), + "%s/%s", s->path, s->include_filename); + if (ret <= 0 || ret > sizeof(s->buffer)) { + ERR(ZS_BAD_INCLUDE_FILENAME); + fhold; fgoto err_line; + } + memcpy(s->include_filename, s->buffer, ret); + } + + // Origin conversion from wire to text form in \DDD notation. + if (s->dname == NULL) { // Use current origin. + wire_dname_to_str(s->zone_origin, + s->zone_origin_length, + (char *)s->buffer); + } else { // Use specified origin. + wire_dname_to_str(s->r_data, + s->r_data_length, + (char *)s->buffer); + } + + // Let the caller to solve the include. + if (s->process.automatic) { + // Create new scanner for included zone file. + zs_scanner_t *ss = malloc(sizeof(zs_scanner_t)); + if (ss == NULL) { + ERR(ZS_UNPROCESSED_INCLUDE); + fhold; fgoto err_line; + } + + // Parse included zone file. + if (zs_init(ss, (char *)s->buffer, s->default_class, + s->default_ttl) != 0 || + zs_set_input_file(ss, (char *)(s->include_filename)) != 0 || + zs_set_processing(ss, s->process.record, s->process.error, + s->process.data) != 0 || + zs_parse_all(ss) != 0) { + // File internal errors are handled by error callback. + if (ss->error.counter > 0) { + s->error.counter += ss->error.counter; + ERR(ZS_UNPROCESSED_INCLUDE); + // General include file error. + } else { + ERR(ss->error.code); + } + zs_deinit(ss); + free(ss); + fhold; fgoto err_line; + } + zs_deinit(ss); + free(ss); + } else { + s->state = ZS_STATE_INCLUDE; + fhold; fnext main; fbreak; + } + } + + include_file_ := + (sep . text >_incl_filename_init %_incl_filename_exit + $!_incl_filename_error . + (sep . absolute_dname >_incl_origin_init %_incl_origin_exit + $!_incl_origin_error + )? . rest + ) %_include_exit %_ret newline; + include_file = all_wchar ${ fhold; fcall include_file_; }; + # END + + # BEGIN - Directive switch + # Each error/warning in directive should stop processing. + # Some internal errors cause warning only. This causes stop processing. + action _directive_init { + ERR(ZS_OK); + } + # Remove stop processing flag. + action _directive_exit { + NOERR; + } + action _directive_error { + ERR(ZS_BAD_DIRECTIVE); + fhold; fgoto err_line; + } + + directive = '$' . ( ("TTL"i . default_ttl) + | ("ORIGIN"i . zone_origin) + | ("INCLUDE"i . include_file) + ) >_directive_init %_directive_exit $!_directive_error; + # END + + # BEGIN - RRecord class and ttl processing + action _default_r_class_exit { + s->r_class = s->default_class; + } + + action _default_r_ttl_exit { + s->r_ttl = s->default_ttl; + } + + action _r_class_in_exit { + s->r_class = KNOT_CLASS_IN; + } + + action _r_ttl_exit { + if (s->number64 <= UINT32_MAX) { + s->r_ttl = (uint32_t)(s->number64); + } else { + WARN(ZS_NUMBER32_OVERFLOW); + fhold; fgoto err_line; + } + } + + r_class = "IN"i %_r_class_in_exit; + + r_ttl = time %_r_ttl_exit; + # END + + # BEGIN - IPv4 and IPv6 address processing + action _addr_init { + s->buffer_length = 0; + } + action _addr { + if (s->buffer_length < sizeof(s->buffer) - 1) { + s->buffer[s->buffer_length++] = fc; + } else { + WARN(ZS_RDATA_OVERFLOW); + fhold; fgoto err_line; + } + } + action _addr_error { + WARN(ZS_BAD_ADDRESS_CHAR); + fhold; fgoto err_line; + } + + action _ipv4_addr_exit { + s->buffer[s->buffer_length] = 0; + + if (inet_pton(AF_INET, (char *)s->buffer, s->addr) <= 0) { + WARN(ZS_BAD_IPV4); + fhold; fgoto err_line; + } + } + action _ipv4_addr_write { + memcpy(rdata_tail, s->addr, ZS_INET4_ADDR_LENGTH); + rdata_tail += ZS_INET4_ADDR_LENGTH; + } + + action _ipv6_addr_exit { + s->buffer[s->buffer_length] = 0; + + if (inet_pton(AF_INET6, (char *)s->buffer, s->addr) <= 0) { + WARN(ZS_BAD_IPV6); + fhold; fgoto err_line; + } + } + action _ipv6_addr_write { + memcpy(rdata_tail, s->addr, ZS_INET6_ADDR_LENGTH); + rdata_tail += ZS_INET6_ADDR_LENGTH; + } + + # Address parsers only. + ipv4_addr = (digit | '.')+ >_addr_init $_addr %_ipv4_addr_exit + $!_addr_error; + ipv6_addr = (xdigit | [.:])+ >_addr_init $_addr %_ipv6_addr_exit + $!_addr_error; + + # Write parsed address to r_data. + ipv4_addr_write = ipv4_addr %_ipv4_addr_write; + ipv6_addr_write = ipv6_addr %_ipv6_addr_write; + # END + + # BEGIN - apl record processing + action _apl_init { + memset(&(s->apl), 0, sizeof(s->apl)); + } + action _apl_excl_flag { + s->apl.excl_flag = 128; // dec 128 = bin 10000000. + } + action _apl_addr_1 { + s->apl.addr_family = 1; + } + action _apl_addr_2 { + s->apl.addr_family = 2; + } + action _apl_prefix_length { + if ((s->apl.addr_family == 1 && s->number64 <= 32) || + (s->apl.addr_family == 2 && s->number64 <= 128)) { + s->apl.prefix_length = (uint8_t)(s->number64); + } else { + WARN(ZS_BAD_APL); + fhold; fgoto err_line; + } + } + action _apl_exit { + // Copy address to buffer. + uint8_t len; + switch (s->apl.addr_family) { + case 1: + len = ZS_INET4_ADDR_LENGTH; + memcpy(s->buffer, s->addr, len); + break; + case 2: + len = ZS_INET6_ADDR_LENGTH; + memcpy(s->buffer, s->addr, len); + break; + default: + WARN(ZS_BAD_APL); + fhold; fgoto err_line; + } + // Find prefix without trailing zeroes. + while (len > 0) { + if ((s->buffer[len - 1] & 255) != 0) { + break; + } + len--; + } + // Check for rdata overflow. + if (rdata_tail + 4 + len > rdata_stop) { + WARN(ZS_RDATA_OVERFLOW); + fhold; fgoto err_line; + } + // Write address family. + *((uint16_t *)rdata_tail) = htons(s->apl.addr_family); + rdata_tail += 2; + // Write prefix length in bits. + *(rdata_tail) = s->apl.prefix_length; + rdata_tail += 1; + // Write negation flag + prefix length in bytes. + *(rdata_tail) = len + s->apl.excl_flag; + rdata_tail += 1; + // Write address prefix non-null data. + memcpy(rdata_tail, s->buffer, len); + rdata_tail += len; + } + action _apl_error { + WARN(ZS_BAD_APL); + fhold; fgoto err_line; + } + + apl = ('!'? $_apl_excl_flag . + ( ('1' $_apl_addr_1 . ':' . ipv4_addr . '/' . number + %_apl_prefix_length) + | ('2' $_apl_addr_2 . ':' . ipv6_addr . '/' . number + %_apl_prefix_length) + ) + ) >_apl_init %_apl_exit $!_apl_error; + + # Array of APL records (can be empty). + apl_array = apl? . (sep . apl)* . sep?; + # END + + # BEGIN - Hexadecimal string array processing + action _first_hex_char { + if (rdata_tail <= rdata_stop) { + *rdata_tail = first_hex_to_num[(uint8_t)fc]; + } else { + WARN(ZS_RDATA_OVERFLOW); + fhold; fgoto err_line; + } + } + action _second_hex_char { + *rdata_tail += second_hex_to_num[(uint8_t)fc]; + rdata_tail++; + } + action _hex_char_error { + WARN(ZS_BAD_HEX_CHAR); + fhold; fgoto err_line; + } + + hex_char = (xdigit $_first_hex_char . xdigit $_second_hex_char); + + # Hex array with possibility of inside white spaces and multiline. + hex_array = (hex_char+ . sep?)+ $!_hex_char_error; + + # Continuous hex array (or "-") with forward length processing. + salt = (hex_char+ | '-') >_item_length_init %_item_length_exit + $!_hex_char_error; + + action _type_data_exit { + if ((rdata_tail - s->r_data) != s->r_data_length) { + WARN(ZS_BAD_RDATA_LENGTH); + fhold; fgoto err_line; + } + } + + action _type_data_error { + WARN(ZS_BAD_HEX_RDATA); + fhold; fgoto err_line; + } + + # Hex array with control to forward length statement. + type_data = hex_array %_type_data_exit $!_type_data_error; + # END + + # BEGIN - Base64 processing (RFC 4648) + action _first_base64_char { + if (rdata_tail <= rdata_stop) { + *rdata_tail = first_base64_to_num[(uint8_t)fc]; + } else { + WARN(ZS_RDATA_OVERFLOW); + fhold; fgoto err_line; + } + } + action _second_base64_char { + *(rdata_tail++) += second_left_base64_to_num[(uint8_t)fc]; + + if (rdata_tail <= rdata_stop) { + *rdata_tail = second_right_base64_to_num[(uint8_t)fc]; + } else { + WARN(ZS_RDATA_OVERFLOW); + fhold; fgoto err_line; + } + } + action _third_base64_char { + *(rdata_tail++) += third_left_base64_to_num[(uint8_t)fc]; + + if (rdata_tail <= rdata_stop) { + *rdata_tail = third_right_base64_to_num[(uint8_t)fc]; + } else { + WARN(ZS_RDATA_OVERFLOW); + fhold; fgoto err_line; + } + } + action _fourth_base64_char { + *(rdata_tail++) += fourth_base64_to_num[(uint8_t)fc]; + } + + action _base64_char_error { + WARN(ZS_BAD_BASE64_CHAR); + fhold; fgoto err_line; + } + + base64_char = alnum | [+/]; + base64_padd = '='; + base64_quartet = + ( base64_char $_first_base64_char . # A + base64_char $_second_base64_char . # AB + ( ( base64_char $_third_base64_char . # ABC + ( base64_char $_fourth_base64_char # ABCD + | base64_padd{1} # ABC= + ) + ) + | base64_padd{2} # AB== + ) + ); + + # Base64 array with possibility of inside white spaces and multiline. + base64_ := (base64_quartet+ . sep?)+ $!_base64_char_error + %_ret . end_wchar; + base64 = base64_char ${ fhold; fcall base64_; }; + # END + + # BEGIN - Base32hex processing (RFC 4648) + action _first_base32hex_char { + if (rdata_tail <= rdata_stop) { + *rdata_tail = first_base32hex_to_num[(uint8_t)fc]; + } else { + WARN(ZS_RDATA_OVERFLOW); + fhold; fgoto err_line; + } + } + action _second_base32hex_char { + *(rdata_tail++) += second_left_base32hex_to_num[(uint8_t)fc]; + + if (rdata_tail <= rdata_stop) { + *rdata_tail = second_right_base32hex_to_num[(uint8_t)fc]; + } else { + WARN(ZS_RDATA_OVERFLOW); + fhold; fgoto err_line; + } + } + action _third_base32hex_char { + *rdata_tail += third_base32hex_to_num[(uint8_t)fc]; + } + action _fourth_base32hex_char { + *(rdata_tail++) += fourth_left_base32hex_to_num[(uint8_t)fc]; + + if (rdata_tail <= rdata_stop) { + *rdata_tail = fourth_right_base32hex_to_num[(uint8_t)fc]; + } else { + WARN(ZS_RDATA_OVERFLOW); + fhold; fgoto err_line; + } + } + action _fifth_base32hex_char { + *(rdata_tail++) += fifth_left_base32hex_to_num[(uint8_t)fc]; + + if (rdata_tail <= rdata_stop) { + *rdata_tail = fifth_right_base32hex_to_num[(uint8_t)fc]; + } else { + WARN(ZS_RDATA_OVERFLOW); + fhold; fgoto err_line; + } + } + action _sixth_base32hex_char { + *rdata_tail += sixth_base32hex_to_num[(uint8_t)fc]; + } + action _seventh_base32hex_char { + *(rdata_tail++) += seventh_left_base32hex_to_num[(uint8_t)fc]; + + if (rdata_tail <= rdata_stop) { + *rdata_tail = seventh_right_base32hex_to_num[(uint8_t)fc]; + } else { + WARN(ZS_RDATA_OVERFLOW); + fhold; fgoto err_line; + } + } + action _eighth_base32hex_char { + *(rdata_tail++) += eighth_base32hex_to_num[(uint8_t)fc]; + } + + action _base32hex_char_error { + WARN(ZS_BAD_BASE32HEX_CHAR); + fhold; fgoto err_line; + } + + base32hex_char = [0-9a-vA-V]; + base32hex_padd = '='; + base32hex_octet = + ( base32hex_char $_first_base32hex_char . # A + base32hex_char $_second_base32hex_char . # AB + ( ( base32hex_char $_third_base32hex_char . # ABC + base32hex_char $_fourth_base32hex_char . # ABCD + ( ( base32hex_char $_fifth_base32hex_char . # ABCDE + ( ( base32hex_char $_sixth_base32hex_char . # ABCDEF + base32hex_char $_seventh_base32hex_char . # ABCDEFG + ( base32hex_char $_eighth_base32hex_char # ABCDEFGH + | base32hex_padd{1} # ABCDEFG= + ) + ) + | base32hex_padd{3} # ABCDE=== + ) + ) + | base32hex_padd{4} # ABCD==== + ) + ) + | base32hex_padd{6} # AB====== + ) + ); + + # Continuous base32hex (with padding!) array with forward length processing. + hash = base32hex_octet+ >_item_length_init %_item_length_exit + $!_base32hex_char_error; + # END + + # BEGIN - Simple number write functions. + action _write8_0 { + *(rdata_tail++) = 0; + } + action _write8_1 { + *(rdata_tail++) = 1; + } + action _write8_2 { + *(rdata_tail++) = 2; + } + action _write8_3 { + *(rdata_tail++) = 3; + } + action _write8_5 { + *(rdata_tail++) = 5; + } + action _write8_6 { + *(rdata_tail++) = 6; + } + action _write8_7 { + *(rdata_tail++) = 7; + } + action _write8_8 { + *(rdata_tail++) = 8; + } + action _write8_10 { + *(rdata_tail++) = 10; + } + action _write8_12 { + *(rdata_tail++) = 12; + } + action _write8_13 { + *(rdata_tail++) = 13; + } + action _write8_14 { + *(rdata_tail++) = 14; + } + action _write8_15 { + *(rdata_tail++) = 15; + } + action _write8_16 { + *(rdata_tail++) = 16; + } + action _write8_252 { + *(rdata_tail++) = 252; + } + action _write8_253 { + *(rdata_tail++) = 253; + } + action _write8_254 { + *(rdata_tail++) = 254; + } + + action _write16_1 { + *((uint16_t *)rdata_tail) = htons(1); + rdata_tail += 2; + } + action _write16_2 { + *((uint16_t *)rdata_tail) = htons(2); + rdata_tail += 2; + } + action _write16_3 { + *((uint16_t *)rdata_tail) = htons(3); + rdata_tail += 2; + } + action _write16_4 { + *((uint16_t *)rdata_tail) = htons(4); + rdata_tail += 2; + } + action _write16_5 { + *((uint16_t *)rdata_tail) = htons(5); + rdata_tail += 2; + } + action _write16_6 { + *((uint16_t *)rdata_tail) = htons(6); + rdata_tail += 2; + } + action _write16_7 { + *((uint16_t *)rdata_tail) = htons(7); + rdata_tail += 2; + } + action _write16_8 { + *((uint16_t *)rdata_tail) = htons(8); + rdata_tail += 2; + } + action _write16_253 { + *((uint16_t *)rdata_tail) = htons(253); + rdata_tail += 2; + } + action _write16_254 { + *((uint16_t *)rdata_tail) = htons(254); + rdata_tail += 2; + } + # END + + # BEGIN - Gateway + action _gateway_error { + WARN(ZS_BAD_GATEWAY); + fhold; fgoto err_line; + } + action _gateway_key_error { + WARN(ZS_BAD_GATEWAY_KEY); + fhold; fgoto err_line; + } + + gateway = (( ('0' $_write8_0 . sep . num8 . sep . '.') + | ('1' $_write8_1 . sep . num8 . sep . ipv4_addr_write) + | ('2' $_write8_2 . sep . num8 . sep . ipv6_addr_write) + | ('3' $_write8_3 . sep . num8 . sep . r_dname) + ) $!_gateway_error . + # If algorithm is 0 then key isn't present and vice versa. + ( ((sep . base64) when { s->number64 != 0 }) + | ((sep?) when { s->number64 == 0 }) # remove blank space + ) $!_gateway_key_error + ); + # END + + # BEGIN - Type processing + action _type_error { + WARN(ZS_UNSUPPORTED_TYPE); + fhold; fgoto err_line; + } + + type_num = + ( "A"i %{ type_num(KNOT_RRTYPE_A, &rdata_tail); } + | "NS"i %{ type_num(KNOT_RRTYPE_NS, &rdata_tail); } + | "CNAME"i %{ type_num(KNOT_RRTYPE_CNAME, &rdata_tail); } + | "SOA"i %{ type_num(KNOT_RRTYPE_SOA, &rdata_tail); } + | "PTR"i %{ type_num(KNOT_RRTYPE_PTR, &rdata_tail); } + | "HINFO"i %{ type_num(KNOT_RRTYPE_HINFO, &rdata_tail); } + | "MINFO"i %{ type_num(KNOT_RRTYPE_MINFO, &rdata_tail); } + | "MX"i %{ type_num(KNOT_RRTYPE_MX, &rdata_tail); } + | "TXT"i %{ type_num(KNOT_RRTYPE_TXT, &rdata_tail); } + | "RP"i %{ type_num(KNOT_RRTYPE_RP, &rdata_tail); } + | "AFSDB"i %{ type_num(KNOT_RRTYPE_AFSDB, &rdata_tail); } + | "RT"i %{ type_num(KNOT_RRTYPE_RT, &rdata_tail); } + | "KEY"i %{ type_num(KNOT_RRTYPE_KEY, &rdata_tail); } + | "AAAA"i %{ type_num(KNOT_RRTYPE_AAAA, &rdata_tail); } + | "LOC"i %{ type_num(KNOT_RRTYPE_LOC, &rdata_tail); } + | "SRV"i %{ type_num(KNOT_RRTYPE_SRV, &rdata_tail); } + | "NAPTR"i %{ type_num(KNOT_RRTYPE_NAPTR, &rdata_tail); } + | "KX"i %{ type_num(KNOT_RRTYPE_KX, &rdata_tail); } + | "CERT"i %{ type_num(KNOT_RRTYPE_CERT, &rdata_tail); } + | "DNAME"i %{ type_num(KNOT_RRTYPE_DNAME, &rdata_tail); } + | "APL"i %{ type_num(KNOT_RRTYPE_APL, &rdata_tail); } + | "DS"i %{ type_num(KNOT_RRTYPE_DS, &rdata_tail); } + | "SSHFP"i %{ type_num(KNOT_RRTYPE_SSHFP, &rdata_tail); } + | "IPSECKEY"i %{ type_num(KNOT_RRTYPE_IPSECKEY, &rdata_tail); } + | "RRSIG"i %{ type_num(KNOT_RRTYPE_RRSIG, &rdata_tail); } + | "NSEC"i %{ type_num(KNOT_RRTYPE_NSEC, &rdata_tail); } + | "DNSKEY"i %{ type_num(KNOT_RRTYPE_DNSKEY, &rdata_tail); } + | "DHCID"i %{ type_num(KNOT_RRTYPE_DHCID, &rdata_tail); } + | "NSEC3"i %{ type_num(KNOT_RRTYPE_NSEC3, &rdata_tail); } + | "NSEC3PARAM"i %{ type_num(KNOT_RRTYPE_NSEC3PARAM, &rdata_tail); } + | "TLSA"i %{ type_num(KNOT_RRTYPE_TLSA, &rdata_tail); } + | "CDS"i %{ type_num(KNOT_RRTYPE_CDS, &rdata_tail); } + | "CDNSKEY"i %{ type_num(KNOT_RRTYPE_CDNSKEY, &rdata_tail); } + | "SPF"i %{ type_num(KNOT_RRTYPE_SPF, &rdata_tail); } + | "NID"i %{ type_num(KNOT_RRTYPE_NID, &rdata_tail); } + | "L32"i %{ type_num(KNOT_RRTYPE_L32, &rdata_tail); } + | "L64"i %{ type_num(KNOT_RRTYPE_L64, &rdata_tail); } + | "LP"i %{ type_num(KNOT_RRTYPE_LP, &rdata_tail); } + | "EUI48"i %{ type_num(KNOT_RRTYPE_EUI48, &rdata_tail); } + | "EUI64"i %{ type_num(KNOT_RRTYPE_EUI64, &rdata_tail); } + | "URI"i %{ type_num(KNOT_RRTYPE_URI, &rdata_tail); } + | "CAA"i %{ type_num(KNOT_RRTYPE_CAA, &rdata_tail); } + | "TYPE"i . num16 # TYPE0-TYPE65535. + ) $!_type_error; + # END + + # BEGIN - Bitmap processing + action _type_bitmap_exit { + if (s->number64 <= UINT16_MAX) { + window_add_bit(s->number64, s); + } else { + WARN(ZS_NUMBER16_OVERFLOW); + fhold; fgoto err_line; + } + } + + # TYPE0-TYPE65535. + type_bitmap = number %_type_bitmap_exit; + + type_bit = + ( "A"i %{ window_add_bit(KNOT_RRTYPE_A, s); } + | "NS"i %{ window_add_bit(KNOT_RRTYPE_NS, s); } + | "CNAME"i %{ window_add_bit(KNOT_RRTYPE_CNAME, s); } + | "SOA"i %{ window_add_bit(KNOT_RRTYPE_SOA, s); } + | "PTR"i %{ window_add_bit(KNOT_RRTYPE_PTR, s); } + | "HINFO"i %{ window_add_bit(KNOT_RRTYPE_HINFO, s); } + | "MINFO"i %{ window_add_bit(KNOT_RRTYPE_MINFO, s); } + | "MX"i %{ window_add_bit(KNOT_RRTYPE_MX, s); } + | "TXT"i %{ window_add_bit(KNOT_RRTYPE_TXT, s); } + | "RP"i %{ window_add_bit(KNOT_RRTYPE_RP, s); } + | "AFSDB"i %{ window_add_bit(KNOT_RRTYPE_AFSDB, s); } + | "RT"i %{ window_add_bit(KNOT_RRTYPE_RT, s); } + | "KEY"i %{ window_add_bit(KNOT_RRTYPE_KEY, s); } + | "AAAA"i %{ window_add_bit(KNOT_RRTYPE_AAAA, s); } + | "LOC"i %{ window_add_bit(KNOT_RRTYPE_LOC, s); } + | "SRV"i %{ window_add_bit(KNOT_RRTYPE_SRV, s); } + | "NAPTR"i %{ window_add_bit(KNOT_RRTYPE_NAPTR, s); } + | "KX"i %{ window_add_bit(KNOT_RRTYPE_KX, s); } + | "CERT"i %{ window_add_bit(KNOT_RRTYPE_CERT, s); } + | "DNAME"i %{ window_add_bit(KNOT_RRTYPE_DNAME, s); } + | "APL"i %{ window_add_bit(KNOT_RRTYPE_APL, s); } + | "DS"i %{ window_add_bit(KNOT_RRTYPE_DS, s); } + | "SSHFP"i %{ window_add_bit(KNOT_RRTYPE_SSHFP, s); } + | "IPSECKEY"i %{ window_add_bit(KNOT_RRTYPE_IPSECKEY, s); } + | "RRSIG"i %{ window_add_bit(KNOT_RRTYPE_RRSIG, s); } + | "NSEC"i %{ window_add_bit(KNOT_RRTYPE_NSEC, s); } + | "DNSKEY"i %{ window_add_bit(KNOT_RRTYPE_DNSKEY, s); } + | "DHCID"i %{ window_add_bit(KNOT_RRTYPE_DHCID, s); } + | "NSEC3"i %{ window_add_bit(KNOT_RRTYPE_NSEC3, s); } + | "NSEC3PARAM"i %{ window_add_bit(KNOT_RRTYPE_NSEC3PARAM, s); } + | "TLSA"i %{ window_add_bit(KNOT_RRTYPE_TLSA, s); } + | "CDS"i %{ window_add_bit(KNOT_RRTYPE_CDS, s); } + | "CDNSKEY"i %{ window_add_bit(KNOT_RRTYPE_CDNSKEY, s); } + | "SPF"i %{ window_add_bit(KNOT_RRTYPE_SPF, s); } + | "NID"i %{ window_add_bit(KNOT_RRTYPE_NID, s); } + | "L32"i %{ window_add_bit(KNOT_RRTYPE_L32, s); } + | "L64"i %{ window_add_bit(KNOT_RRTYPE_L64, s); } + | "LP"i %{ window_add_bit(KNOT_RRTYPE_LP, s); } + | "EUI48"i %{ window_add_bit(KNOT_RRTYPE_EUI48, s); } + | "EUI64"i %{ window_add_bit(KNOT_RRTYPE_EUI64, s); } + | "URI"i %{ window_add_bit(KNOT_RRTYPE_URI, s); } + | "CAA"i %{ window_add_bit(KNOT_RRTYPE_CAA, s); } + | "TYPE"i . type_bitmap # TYPE0-TYPE65535. + ); + + action _bitmap_init { + memset(s->windows, 0, sizeof(s->windows)); + s->last_window = -1; + } + action _bitmap_exit { + for (uint16_t window = 0; window <= s->last_window; window++) { + if ((s->windows[window]).length > 0) { + if (rdata_tail + 2 + (s->windows[window]).length <= rdata_stop) + { + // Window number. + *rdata_tail = (uint8_t)window; + rdata_tail += 1; + // Bitmap length. + *rdata_tail = (s->windows[window]).length; + rdata_tail += 1; + // Copying bitmap. + memcpy(rdata_tail, + (s->windows[window]).bitmap, + (s->windows[window]).length); + rdata_tail += (s->windows[window]).length; + } else { + WARN(ZS_RDATA_OVERFLOW); + fhold; fgoto err_line; + } + } + } + } + action _bitmap_error { + WARN(ZS_BAD_BITMAP); + fhold; fgoto err_line; + } + + # Blank bitmap is allowed too. + bitmap_ := ((sep . type_bit)* . sep?) >_bitmap_init + %_bitmap_exit %_ret $!_bitmap_error . end_wchar; + bitmap = all_wchar ${ fhold; fcall bitmap_; }; + # END + + # BEGIN - Location processing + action _d1_exit { + if (s->number64 <= 90) { + s->loc.d1 = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + fhold; fgoto err_line; + } + } + action _d2_exit { + if (s->number64 <= 180) { + s->loc.d2 = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + fhold; fgoto err_line; + } + } + action _m1_exit { + if (s->number64 <= 59) { + s->loc.m1 = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + fhold; fgoto err_line; + } + } + action _m2_exit { + if (s->number64 <= 59) { + s->loc.m2 = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + fhold; fgoto err_line; + } + } + action _s1_exit { + if (s->number64 <= 59999) { + s->loc.s1 = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + fhold; fgoto err_line; + } + } + action _s2_exit { + if (s->number64 <= 59999) { + s->loc.s2 = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + fhold; fgoto err_line; + } + } + action _alt_exit { + if ((s->loc.alt_sign == 1 && s->number64 <= 4284967295) || + (s->loc.alt_sign == -1 && s->number64 <= 10000000)) + { + s->loc.alt = (uint32_t)(s->number64); + } else { + WARN(ZS_BAD_NUMBER); + fhold; fgoto err_line; + } + } + action _siz_exit { + if (s->number64 <= 9000000000ULL) { + s->loc.siz = s->number64; + } else { + WARN(ZS_BAD_NUMBER); + fhold; fgoto err_line; + } + } + action _hp_exit { + if (s->number64 <= 9000000000ULL) { + s->loc.hp = s->number64; + } else { + WARN(ZS_BAD_NUMBER); + fhold; fgoto err_line; + } + } + action _vp_exit { + if (s->number64 <= 9000000000ULL) { + s->loc.vp = s->number64; + } else { + WARN(ZS_BAD_NUMBER); + fhold; fgoto err_line; + } + } + action _lat_sign { + s->loc.lat_sign = -1; + } + action _long_sign { + s->loc.long_sign = -1; + } + action _alt_sign { + s->loc.alt_sign = -1; + } + + d1 = number %_d1_exit; + d2 = number %_d2_exit; + m1 = number %_m1_exit; + m2 = number %_m2_exit; + s1 = float3 %_s1_exit; + s2 = float3 %_s2_exit; + siz = float2 %_siz_exit; + hp = float2 %_hp_exit; + vp = float2 %_vp_exit; + alt = ('-' %_alt_sign)? . float2 %_alt_exit; + lat_sign = 'N' | 'S' %_lat_sign; + long_sign = 'E' | 'W' %_long_sign; + + action _loc_init { + memset(&(s->loc), 0, sizeof(s->loc)); + // Defaults. + s->loc.siz = 100; + s->loc.vp = 1000; + s->loc.hp = 1000000; + s->loc.lat_sign = 1; + s->loc.long_sign = 1; + s->loc.alt_sign = 1; + } + action _loc_exit { + // Write version. + *(rdata_tail) = 0; + rdata_tail += 1; + // Write size. + *(rdata_tail) = loc64to8(s->loc.siz); + rdata_tail += 1; + // Write horizontal precision. + *(rdata_tail) = loc64to8(s->loc.hp); + rdata_tail += 1; + // Write vertical precision. + *(rdata_tail) = loc64to8(s->loc.vp); + rdata_tail += 1; + // Write latitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LAT_ZERO + s->loc.lat_sign * + (3600000 * s->loc.d1 + 60000 * s->loc.m1 + s->loc.s1)); + rdata_tail += 4; + // Write longitude. + *((uint32_t *)rdata_tail) = htonl(LOC_LONG_ZERO + s->loc.long_sign * + (3600000 * s->loc.d2 + 60000 * s->loc.m2 + s->loc.s2)); + rdata_tail += 4; + // Write altitude. + *((uint32_t *)rdata_tail) = htonl(LOC_ALT_ZERO + s->loc.alt_sign * + (s->loc.alt)); + rdata_tail += 4; + } + action _loc_error { + WARN(ZS_BAD_LOC_DATA); + fhold; fgoto err_line; + } + + loc = (d1 . sep . (m1 . sep . (s1 . sep)?)? . lat_sign . sep . + d2 . sep . (m2 . sep . (s2 . sep)?)? . long_sign . sep . + alt 'm'? . (sep . siz 'm'? . (sep . hp 'm'? . (sep . vp 'm'?)?)?)? . + sep? + ) >_loc_init %_loc_exit $!_loc_error; + # END + + # BEGIN - Hexadecimal rdata processing + action _hex_r_data_error { + WARN(ZS_BAD_HEX_RDATA); + fhold; fgoto err_line; + } + + nonempty_hex_r_data := + (sep . length_number . sep . type_data) + $!_hex_r_data_error %_ret . end_wchar; + + hex_r_data := + (sep . + ( ('0' %_ret . all_wchar) + | (length_number . sep . type_data %_ret . end_wchar) + ) + ) $!_hex_r_data_error; + # END + + # BEGIN - EUI processing + action _eui_init { + s->item_length = 0; + } + action _eui_count { + s->item_length++; + } + action _eui48_exit { + if (s->item_length != 6) { + WARN(ZS_BAD_EUI_LENGTH); + fhold; fgoto err_line; + } + } + action _eui64_exit { + if (s->item_length != 8) { + WARN(ZS_BAD_EUI_LENGTH); + fhold; fgoto err_line; + } + } + action _eui_sep_error { + WARN(ZS_BAD_CHAR_DASH); + fhold; fgoto err_line; + } + + eui48 = (hex_char %_eui_count . + ('-' >!_eui_sep_error . hex_char %_eui_count)+ + ) $!_hex_char_error >_eui_init %_eui48_exit; + + eui64 = (hex_char %_eui_count . + ('-' >!_eui_sep_error . hex_char %_eui_count)+ + ) $!_hex_char_error >_eui_init %_eui64_exit; + # END + + # BEGIN - ILNP processing + action _l64_init { + s->item_length = 0; + } + action _l64_count { + s->item_length++; + } + action _l64_exit { + if (s->item_length != 4) { + WARN(ZS_BAD_L64_LENGTH); + fhold; fgoto err_line; + } + } + action _l64_sep_error { + WARN(ZS_BAD_CHAR_COLON); + fhold; fgoto err_line; + } + + l64_label = (hex_char . hex_char) $!_hex_char_error %_l64_count; + l64 = (l64_label . (':' >!_l64_sep_error . l64_label)+ + ) $!_hex_char_error >_l64_init %_l64_exit; + + l32 = ipv4_addr %_ipv4_addr_write; + # END + + # BEGIN - Mnemomic names processing + action _dns_alg_error { + WARN(ZS_BAD_ALGORITHM); + fhold; fgoto err_line; + } + action _cert_type_error { + WARN(ZS_BAD_CERT_TYPE); + fhold; fgoto err_line; + } + + dns_alg_ := + ( number %_num8_write + | "RSAMD5"i %_write8_1 + | "DH"i %_write8_2 + | "DSA"i %_write8_3 + | "RSASHA1"i %_write8_5 + | "DSA-NSEC3-SHA1"i %_write8_6 + | "RSASHA1-NSEC3-SHA1"i %_write8_7 + | "RSASHA256"i %_write8_8 + | "RSASHA512"i %_write8_10 + | "ECC-GOST"i %_write8_12 + | "ECDSAP256SHA256"i %_write8_13 + | "ECDSAP384SHA384"i %_write8_14 + | "ED25519"i %_write8_15 + | "ED448"i %_write8_16 + | "INDIRECT"i %_write8_252 + | "PRIVATEDNS"i %_write8_253 + | "PRIVATEOID"i %_write8_254 + ) $!_dns_alg_error %_ret . all_wchar; + dns_alg = alnum ${ fhold; fcall dns_alg_; }; + + cert_type_ := + ( number %_num16_write + | "PKIX"i %_write16_1 + | "SPKI"i %_write16_2 + | "PGP"i %_write16_3 + | "IPKIX"i %_write16_4 + | "ISPKI"i %_write16_5 + | "IPGP"i %_write16_6 + | "ACPKIX"i %_write16_7 + | "IACPKIX"i %_write16_8 + | "URI"i %_write16_253 + | "OID"i %_write16_254 + ) $!_cert_type_error %_ret . all_wchar; + cert_type = alnum ${ fhold; fcall cert_type_; }; + # END + + # BEGIN - Rdata processing + action _r_data_init { + rdata_tail = s->r_data; + } + action _r_data_error { + WARN(ZS_BAD_RDATA); + fhold; fgoto err_line; + } + + r_data_a := + (ipv4_addr_write) + $!_r_data_error %_ret . all_wchar; + + r_data_ns := + (r_dname) + $!_r_data_error %_ret . all_wchar; + + r_data_soa := + (r_dname . sep . r_dname . sep . num32 . sep . time32 . + sep . time32 . sep . time32 . sep . time32) + $!_r_data_error %_ret . all_wchar; + + r_data_hinfo := + (text_string . sep . text_string) + $!_r_data_error %_ret . all_wchar; + + r_data_minfo := + (r_dname . sep . r_dname) + $!_r_data_error %_ret . all_wchar; + + r_data_mx := + (num16 . sep . r_dname) + $!_r_data_error %_ret . all_wchar; + + r_data_txt := + (text_array) + $!_r_data_error %_ret . end_wchar; + + r_data_aaaa := + (ipv6_addr_write) + $!_r_data_error %_ret . all_wchar; + + r_data_loc := + (loc) + $!_r_data_error %_ret . end_wchar; + + r_data_srv := + (num16 . sep . num16 . sep . num16 . sep . r_dname) + $!_r_data_error %_ret . all_wchar; + + r_data_naptr := + (num16 . sep . num16 . sep . text_string . sep . text_string . + sep . text_string . sep . r_dname) + $!_r_data_error %_ret . all_wchar; + + r_data_cert := + (cert_type . sep . num16 . sep . dns_alg . sep . base64) + $!_r_data_error %_ret . end_wchar; + + r_data_apl := + (apl_array) + $!_r_data_error %_ret . end_wchar; + + r_data_ds := + (num16 . sep . dns_alg . sep . num8 . sep . hex_array) + $!_r_data_error %_ret . end_wchar; + + r_data_sshfp := + (num8 . sep . num8 . sep . hex_array) + $!_r_data_error %_ret . end_wchar; + + r_data_ipseckey := + (num8 . sep . gateway) + $!_r_data_error %_ret . end_wchar; + + r_data_rrsig := + (type_num . sep . dns_alg . sep . num8 . sep . num32 . sep . + timestamp . sep . timestamp . sep . num16 . sep . r_dname . + sep . base64) + $!_r_data_error %_ret . end_wchar; + + r_data_nsec := + (r_dname . bitmap) + $!_r_data_error %_ret . all_wchar; + + r_data_dnskey := + (num16 . sep . num8 . sep . dns_alg . sep . base64) + $!_r_data_error %_ret . end_wchar; + + r_data_dhcid := + (base64) + $!_r_data_error %_ret . end_wchar; + + r_data_nsec3 := + (num8 . sep . num8 . sep . num16 . sep . salt . sep . + hash . bitmap) + $!_r_data_error %_ret . all_wchar; + + r_data_nsec3param := + (num8 . sep . num8 . sep . num16 . sep . salt) + $!_r_data_error %_ret . all_wchar; + + r_data_tlsa := + (num8 . sep . num8 . sep . num8 . sep . hex_array) + $!_r_data_error %_ret . end_wchar; + + r_data_l32 := + (num16 . sep . l32) + $!_r_data_error %_ret . all_wchar; + + r_data_l64 := + (num16 . sep . l64) + $!_r_data_error %_ret . all_wchar; + + r_data_eui48 := + (eui48) + $!_r_data_error %_ret . all_wchar; + + r_data_eui64 := + (eui64) + $!_r_data_error %_ret . all_wchar; + + r_data_uri := + (num16 . sep . num16 . sep . text) + $!_r_data_error %_ret . all_wchar; + + r_data_caa := + (num8 . sep . text_string . sep . text) + $!_r_data_error %_ret . all_wchar; + + action _text_r_data { + fhold; + switch (s->r_type) { + case KNOT_RRTYPE_A: + fcall r_data_a; + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + fcall r_data_ns; + case KNOT_RRTYPE_SOA: + fcall r_data_soa; + case KNOT_RRTYPE_HINFO: + fcall r_data_hinfo; + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_RP: + fcall r_data_minfo; + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_LP: + fcall r_data_mx; + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + fcall r_data_txt; + case KNOT_RRTYPE_AAAA: + fcall r_data_aaaa; + case KNOT_RRTYPE_LOC: + fcall r_data_loc; + case KNOT_RRTYPE_SRV: + fcall r_data_srv; + case KNOT_RRTYPE_NAPTR: + fcall r_data_naptr; + case KNOT_RRTYPE_CERT: + fcall r_data_cert; + case KNOT_RRTYPE_APL: + fcall r_data_apl; + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_CDS: + fcall r_data_ds; + case KNOT_RRTYPE_SSHFP: + fcall r_data_sshfp; + case KNOT_RRTYPE_IPSECKEY: + fcall r_data_ipseckey; + case KNOT_RRTYPE_RRSIG: + fcall r_data_rrsig; + case KNOT_RRTYPE_NSEC: + fcall r_data_nsec; + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_CDNSKEY: + fcall r_data_dnskey; + case KNOT_RRTYPE_DHCID: + fcall r_data_dhcid; + case KNOT_RRTYPE_NSEC3: + fcall r_data_nsec3; + case KNOT_RRTYPE_NSEC3PARAM: + fcall r_data_nsec3param; + case KNOT_RRTYPE_TLSA: + fcall r_data_tlsa; + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L64: + fcall r_data_l64; + case KNOT_RRTYPE_L32: + fcall r_data_l32; + case KNOT_RRTYPE_EUI48: + fcall r_data_eui48; + case KNOT_RRTYPE_EUI64: + fcall r_data_eui64; + case KNOT_RRTYPE_URI: + fcall r_data_uri; + case KNOT_RRTYPE_CAA: + fcall r_data_caa; + default: + WARN(ZS_CANNOT_TEXT_DATA); + fgoto err_line; + } + } + action _hex_r_data { + switch (s->r_type) { + // Next types must not have empty rdata. + case KNOT_RRTYPE_A: + case KNOT_RRTYPE_NS: + case KNOT_RRTYPE_CNAME: + case KNOT_RRTYPE_PTR: + case KNOT_RRTYPE_DNAME: + case KNOT_RRTYPE_SOA: + case KNOT_RRTYPE_HINFO: + case KNOT_RRTYPE_MINFO: + case KNOT_RRTYPE_MX: + case KNOT_RRTYPE_AFSDB: + case KNOT_RRTYPE_RT: + case KNOT_RRTYPE_KX: + case KNOT_RRTYPE_TXT: + case KNOT_RRTYPE_SPF: + case KNOT_RRTYPE_RP: + case KNOT_RRTYPE_AAAA: + case KNOT_RRTYPE_LOC: + case KNOT_RRTYPE_SRV: + case KNOT_RRTYPE_NAPTR: + case KNOT_RRTYPE_CERT: + case KNOT_RRTYPE_DS: + case KNOT_RRTYPE_SSHFP: + case KNOT_RRTYPE_IPSECKEY: + case KNOT_RRTYPE_RRSIG: + case KNOT_RRTYPE_NSEC: + case KNOT_RRTYPE_KEY: + case KNOT_RRTYPE_DNSKEY: + case KNOT_RRTYPE_DHCID: + case KNOT_RRTYPE_NSEC3: + case KNOT_RRTYPE_NSEC3PARAM: + case KNOT_RRTYPE_TLSA: + case KNOT_RRTYPE_CDS: + case KNOT_RRTYPE_CDNSKEY: + case KNOT_RRTYPE_NID: + case KNOT_RRTYPE_L32: + case KNOT_RRTYPE_L64: + case KNOT_RRTYPE_LP: + case KNOT_RRTYPE_EUI48: + case KNOT_RRTYPE_EUI64: + case KNOT_RRTYPE_URI: + case KNOT_RRTYPE_CAA: + fcall nonempty_hex_r_data; + // Next types can have empty rdata. + case KNOT_RRTYPE_APL: + default: + fcall hex_r_data; + } + } + + # Avoidance of multiple fhold at the input block end. + action _wrap_in { + if (pe - p == 1) { + *wrap = WRAP_DETECTED; + } + } + action _wrap_out { + if (*wrap == WRAP_NONE) { + fhold; + } + } + + # rdata can be in text or hex format with leading "\#" string. + r_data = + ( sep . ^('\\' | all_wchar) $_text_r_data + | sep . '\\' $_wrap_in . ^'#' $_wrap_out $_text_r_data + | sep . '\\' . '#' $_hex_r_data # Hex format. + | sep? . end_wchar $_text_r_data # Empty rdata. + ) >_r_data_init $!_r_data_error; + # END + + # BEGIN - Record type processing + action _r_type_error { + WARN(ZS_UNSUPPORTED_TYPE); + fhold; fgoto err_line; + } + + r_type = + ( "A"i %{ s->r_type = KNOT_RRTYPE_A; } + | "NS"i %{ s->r_type = KNOT_RRTYPE_NS; } + | "CNAME"i %{ s->r_type = KNOT_RRTYPE_CNAME; } + | "SOA"i %{ s->r_type = KNOT_RRTYPE_SOA; } + | "PTR"i %{ s->r_type = KNOT_RRTYPE_PTR; } + | "HINFO"i %{ s->r_type = KNOT_RRTYPE_HINFO; } + | "MINFO"i %{ s->r_type = KNOT_RRTYPE_MINFO; } + | "MX"i %{ s->r_type = KNOT_RRTYPE_MX; } + | "TXT"i %{ s->r_type = KNOT_RRTYPE_TXT; } + | "RP"i %{ s->r_type = KNOT_RRTYPE_RP; } + | "AFSDB"i %{ s->r_type = KNOT_RRTYPE_AFSDB; } + | "RT"i %{ s->r_type = KNOT_RRTYPE_RT; } + | "KEY"i %{ s->r_type = KNOT_RRTYPE_KEY; } + | "AAAA"i %{ s->r_type = KNOT_RRTYPE_AAAA; } + | "LOC"i %{ s->r_type = KNOT_RRTYPE_LOC; } + | "SRV"i %{ s->r_type = KNOT_RRTYPE_SRV; } + | "NAPTR"i %{ s->r_type = KNOT_RRTYPE_NAPTR; } + | "KX"i %{ s->r_type = KNOT_RRTYPE_KX; } + | "CERT"i %{ s->r_type = KNOT_RRTYPE_CERT; } + | "DNAME"i %{ s->r_type = KNOT_RRTYPE_DNAME; } + | "APL"i %{ s->r_type = KNOT_RRTYPE_APL; } + | "DS"i %{ s->r_type = KNOT_RRTYPE_DS; } + | "SSHFP"i %{ s->r_type = KNOT_RRTYPE_SSHFP; } + | "IPSECKEY"i %{ s->r_type = KNOT_RRTYPE_IPSECKEY; } + | "RRSIG"i %{ s->r_type = KNOT_RRTYPE_RRSIG; } + | "NSEC"i %{ s->r_type = KNOT_RRTYPE_NSEC; } + | "DNSKEY"i %{ s->r_type = KNOT_RRTYPE_DNSKEY; } + | "DHCID"i %{ s->r_type = KNOT_RRTYPE_DHCID; } + | "NSEC3"i %{ s->r_type = KNOT_RRTYPE_NSEC3; } + | "NSEC3PARAM"i %{ s->r_type = KNOT_RRTYPE_NSEC3PARAM; } + | "TLSA"i %{ s->r_type = KNOT_RRTYPE_TLSA; } + | "CDS"i %{ s->r_type = KNOT_RRTYPE_CDS; } + | "CDNSKEY"i %{ s->r_type = KNOT_RRTYPE_CDNSKEY; } + | "SPF"i %{ s->r_type = KNOT_RRTYPE_SPF; } + | "NID"i %{ s->r_type = KNOT_RRTYPE_NID; } + | "L32"i %{ s->r_type = KNOT_RRTYPE_L32; } + | "L64"i %{ s->r_type = KNOT_RRTYPE_L64; } + | "LP"i %{ s->r_type = KNOT_RRTYPE_LP; } + | "EUI48"i %{ s->r_type = KNOT_RRTYPE_EUI48; } + | "EUI64"i %{ s->r_type = KNOT_RRTYPE_EUI64; } + | "URI"i %{ s->r_type = KNOT_RRTYPE_URI; } + | "CAA"i %{ s->r_type = KNOT_RRTYPE_CAA; } + | "TYPE"i . type_number + ) $!_r_type_error; + # END + + # BEGIN - The highest level processing + action _record_exit { + if (rdata_tail - s->r_data > UINT16_MAX) { + WARN(ZS_RDATA_OVERFLOW); + fhold; fgoto err_line; + } + s->r_data_length = rdata_tail - s->r_data; + + s->state = ZS_STATE_DATA; + + // Execute the record callback. + if (s->process.automatic) { + if (s->process.record != NULL) { + s->process.record(s); + + // Stop if required from the callback. + if (s->state == ZS_STATE_STOP) { + fbreak; + } + } + } else { + // Return if external processing. + fhold; fbreak; + } + } + + # Resource record. + record = + r_owner . sep . + ( (r_class . sep . ((r_ttl . sep) | (zlen %_default_r_ttl_exit ))) + | (r_ttl . sep . ((r_class . sep) | (zlen %_default_r_class_exit))) + | zlen %_default_r_class_exit %_default_r_ttl_exit + ) $!_r_type_error . + r_type . r_data . + rest %_record_exit . + newline; + + # Blank spaces with comments. + blank = rest . newline; + + # Main processing loop. + main := (record | directive | blank)*; + # END +}%% diff --git a/src/libzscanner/version.h b/src/libzscanner/version.h new file mode 100644 index 0000000..59b19e4 --- /dev/null +++ b/src/libzscanner/version.h @@ -0,0 +1,25 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#define ZSCANNER_VERSION_MAJOR 2 +#define ZSCANNER_VERSION_MINOR 7 +#define ZSCANNER_VERSION_PATCH 0x06 + +#define ZSCANNER_VERSION_HEX ((ZSCANNER_VERSION_MAJOR << 16) | \ + (ZSCANNER_VERSION_MINOR << 8) | \ + (ZSCANNER_VERSION_PATCH)) diff --git a/src/libzscanner/version.h.in b/src/libzscanner/version.h.in new file mode 100644 index 0000000..bdbee9a --- /dev/null +++ b/src/libzscanner/version.h.in @@ -0,0 +1,25 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#define ZSCANNER_VERSION_MAJOR @KNOT_VERSION_MAJOR@ +#define ZSCANNER_VERSION_MINOR @KNOT_VERSION_MINOR@ +#define ZSCANNER_VERSION_PATCH 0x0@KNOT_VERSION_PATCH@ + +#define ZSCANNER_VERSION_HEX ((ZSCANNER_VERSION_MAJOR << 16) | \ + (ZSCANNER_VERSION_MINOR << 8) | \ + (ZSCANNER_VERSION_PATCH)) diff --git a/src/utils/Makefile.inc b/src/utils/Makefile.inc new file mode 100644 index 0000000..bc28961 --- /dev/null +++ b/src/utils/Makefile.inc @@ -0,0 +1,139 @@ +if HAVE_LIBUTILS +noinst_LTLIBRARIES += libknotus.la + +libknotus_la_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAG_VISIBILITY) $(libidn2_CFLAGS) \ + $(libidn_CFLAGS) $(libedit_CFLAGS) $(gnutls_CFLAGS) +libknotus_la_LDFLAGS = $(AM_LDFLAGS) $(LDFLAG_EXCLUDE_LIBS) +libknotus_la_LIBADD = libcontrib.la libknot.la $(libidn2_LIBS) $(libidn_LIBS) \ + $(libedit_LIBS) $(gnutls_LIBS) + +libknotus_la_SOURCES = \ + utils/common/cert.c \ + utils/common/cert.h \ + utils/common/exec.c \ + utils/common/exec.h \ + utils/common/hex.c \ + utils/common/hex.h \ + utils/common/lookup.c \ + utils/common/lookup.h \ + utils/common/msg.c \ + utils/common/msg.h \ + utils/common/netio.c \ + utils/common/netio.h \ + utils/common/params.c \ + utils/common/params.h \ + utils/common/resolv.c \ + utils/common/resolv.h \ + utils/common/sign.c \ + utils/common/sign.h \ + utils/common/tls.c \ + utils/common/tls.h \ + utils/common/token.c \ + utils/common/token.h +endif HAVE_LIBUTILS + +if HAVE_UTILS +bin_PROGRAMS = kdig khost knsec3hash knsupdate + +kdig_SOURCES = \ + utils/kdig/kdig_exec.c \ + utils/kdig/kdig_exec.h \ + utils/kdig/kdig_main.c \ + utils/kdig/kdig_params.c \ + utils/kdig/kdig_params.h + +khost_SOURCES = \ + utils/kdig/kdig_exec.c \ + utils/kdig/kdig_exec.h \ + utils/kdig/kdig_params.c \ + utils/kdig/kdig_params.h \ + utils/khost/khost_main.c \ + utils/khost/khost_params.c \ + utils/khost/khost_params.h + +knsec3hash_SOURCES = \ + utils/knsec3hash/knsec3hash.c + +knsupdate_SOURCES = \ + utils/knsupdate/knsupdate_exec.c \ + utils/knsupdate/knsupdate_exec.h \ + utils/knsupdate/knsupdate_main.c \ + utils/knsupdate/knsupdate_params.c \ + utils/knsupdate/knsupdate_params.h + +kdig_CPPFLAGS = $(AM_CPPFLAGS) $(gnutls_CFLAGS) +kdig_LDADD = libknotus.la +khost_CPPFLAGS = $(AM_CPPFLAGS) $(gnutls_CFLAGS) +khost_LDADD = libknotus.la +knsec3hash_CPPFLAGS = $(AM_CPPFLAGS) +knsec3hash_LDADD = libcontrib.la libdnssec.la libknot.la libshared.la +knsupdate_CPPFLAGS = $(AM_CPPFLAGS) $(gnutls_CFLAGS) +knsupdate_LDADD = libknotus.la libzscanner.la + +if HAVE_DNSTAP +kdig_LDADD += $(DNSTAP_LIBS) libdnstap.la +khost_LDADD += $(DNSTAP_LIBS) libdnstap.la +kdig_CPPFLAGS += $(DNSTAP_CFLAGS) +khost_CPPFLAGS += $(DNSTAP_CFLAGS) +endif HAVE_DNSTAP +endif HAVE_UTILS + +if HAVE_DAEMON +# Create storage and run-time directories +install-data-hook: + $(INSTALL) -d $(DESTDIR)/@config_dir@ + $(INSTALL) -d $(DESTDIR)/@run_dir@ + $(INSTALL) -d $(DESTDIR)/@storage_dir@ + +sbin_PROGRAMS = knotc knotd + +knotc_SOURCES = \ + utils/knotc/commands.c \ + utils/knotc/commands.h \ + utils/knotc/estimator.c \ + utils/knotc/estimator.h \ + utils/knotc/interactive.c \ + utils/knotc/interactive.h \ + utils/knotc/process.c \ + utils/knotc/process.h \ + utils/knotc/main.c + +knotd_SOURCES = \ + utils/knotd/main.c + +knotc_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAG_VISIBILITY) $(libedit_CFLAGS) +knotc_LDADD = libcontrib.la libknotd.la libknotus.la $(libedit_LIBS) +knotc_LDFLAGS = $(AM_LDFLAGS) -rdynamic +knotd_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAG_VISIBILITY) $(liburcu_CFLAGS) +knotd_LDADD = $(malloc_LIBS) libcontrib.la libknotd.la $(liburcu_LIBS) \ + $(cap_ng_LIBS) +knotd_LDFLAGS = $(AM_LDFLAGS) -rdynamic + +if HAVE_UTILS +bin_PROGRAMS += kzonecheck +sbin_PROGRAMS += keymgr kjournalprint + +kzonecheck_SOURCES = \ + utils/kzonecheck/main.c \ + utils/kzonecheck/zone_check.c \ + utils/kzonecheck/zone_check.h + +keymgr_SOURCES = \ + utils/keymgr/bind_privkey.c \ + utils/keymgr/bind_privkey.h \ + utils/keymgr/functions.c \ + utils/keymgr/functions.h \ + utils/keymgr/main.c + +kjournalprint_SOURCES = \ + utils/kjournalprint/main.c + +kzonecheck_CPPFLAGS = $(AM_CPPFLAGS) +kzonecheck_LDADD = libcontrib.la libknotd.la +keymgr_CPPFLAGS = $(AM_CPPFLAGS) $(gnutls_CFLAGS) +keymgr_LDADD = libcontrib.la libknotd.la libknotus.la libdnssec.la \ + libshared.la libzscanner.la +kjournalprint_CPPFLAGS = $(AM_CPPFLAGS) $(gnutls_CFLAGS) +kjournalprint_LDADD = libcontrib.la libknotd.la +endif HAVE_UTILS +endif HAVE_DAEMON diff --git a/src/utils/common/cert.c b/src/utils/common/cert.c new file mode 100644 index 0000000..b9cf2c4 --- /dev/null +++ b/src/utils/common/cert.c @@ -0,0 +1,61 @@ +/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <gnutls/abstract.h> +#include <gnutls/crypto.h> + +#include "utils/common/cert.h" +#include "libknot/error.h" + +static int spki_hash(gnutls_x509_crt_t cert, gnutls_digest_algorithm_t alg, + uint8_t *hash, size_t size) +{ + if (!cert || !hash || gnutls_hash_get_len(alg) != size) { + return KNOT_EINVAL; + } + + gnutls_pubkey_t key = { 0 }; + if (gnutls_pubkey_init(&key) != GNUTLS_E_SUCCESS) { + return KNOT_ENOMEM; + } + + if (gnutls_pubkey_import_x509(key, cert, 0) != GNUTLS_E_SUCCESS) { + gnutls_pubkey_deinit(key); + return KNOT_ERROR; + } + + gnutls_datum_t der = { 0 }; + if (gnutls_pubkey_export2(key, GNUTLS_X509_FMT_DER, &der) != GNUTLS_E_SUCCESS) { + gnutls_pubkey_deinit(key); + return KNOT_ERROR; + } + + int ret = gnutls_hash_fast(alg, der.data, der.size, hash); + + gnutls_free(der.data); + gnutls_pubkey_deinit(key); + + if (ret != GNUTLS_E_SUCCESS) { + return KNOT_ERROR; + } + + return KNOT_EOK; +} + +int cert_get_pin(gnutls_x509_crt_t cert, uint8_t *pin, size_t size) +{ + return spki_hash(cert, GNUTLS_DIG_SHA256, pin, size); +} diff --git a/src/utils/common/cert.h b/src/utils/common/cert.h new file mode 100644 index 0000000..ad4901b --- /dev/null +++ b/src/utils/common/cert.h @@ -0,0 +1,36 @@ +/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <gnutls/x509.h> +#include <stdint.h> +#include <stdlib.h> + +#define CERT_PIN_LEN 32 + +/*! + * \brief Get certificate pin value. + * + * The pin is a SHA-256 hash of the X.509 SubjectPublicKeyInfo. + * + * \param[in] crt Certificate. + * \param[out] pin Pin. + * \param[in] size Length of the pin, must be CERT_PIN_LEN. + * + * \return Error code, KNOT_EOK if successful. + */ +int cert_get_pin(gnutls_x509_crt_t crt, uint8_t *pin, size_t size); diff --git a/src/utils/common/exec.c b/src/utils/common/exec.c new file mode 100644 index 0000000..1d97600 --- /dev/null +++ b/src/utils/common/exec.c @@ -0,0 +1,692 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <arpa/inet.h> +#include <stdlib.h> +#include <time.h> + +#include "libdnssec/random.h" +#include "utils/common/exec.h" +#include "utils/common/msg.h" +#include "utils/common/netio.h" +#include "utils/common/params.h" +#include "libknot/libknot.h" +#include "contrib/ctype.h" +#include "contrib/sockaddr.h" +#include "contrib/openbsd/strlcat.h" +#include "contrib/ucw/lists.h" +#include "contrib/wire_ctx.h" + +static knot_lookup_t rtypes[] = { + { KNOT_RRTYPE_A, "has IPv4 address" }, + { KNOT_RRTYPE_NS, "nameserver is" }, + { KNOT_RRTYPE_CNAME, "is an alias for" }, + { KNOT_RRTYPE_SOA, "start of authority is" }, + { KNOT_RRTYPE_PTR, "points to" }, + { KNOT_RRTYPE_MX, "mail is handled by" }, + { KNOT_RRTYPE_TXT, "description is" }, + { KNOT_RRTYPE_AAAA, "has IPv6 address" }, + { KNOT_RRTYPE_LOC, "location is" }, + { KNOT_RRTYPE_DS, "delegation signature is" }, + { KNOT_RRTYPE_SSHFP, "SSH fingerprint is" }, + { KNOT_RRTYPE_RRSIG, "RR set signature is" }, + { KNOT_RRTYPE_DNSKEY, "DNSSEC key is" }, + { KNOT_RRTYPE_TLSA, "has TLS certificate" }, + { 0, NULL } +}; + +static void print_header(const knot_pkt_t *packet, const style_t *style) +{ + char flags[64] = ""; + char unknown_rcode[64] = ""; + char unknown_opcode[64] = ""; + + const char *rcode_str = NULL; + const char *opcode_str = NULL; + + // Get extended RCODE. + const char *code_name = knot_pkt_ext_rcode_name(packet); + if (code_name[0] != '\0') { + rcode_str = code_name; + } else { + uint16_t code = knot_pkt_ext_rcode(packet); + (void)snprintf(unknown_rcode, sizeof(unknown_rcode), "RCODE %d", code); + rcode_str = unknown_rcode; + } + + // Get OPCODE. + uint8_t code = knot_wire_get_opcode(packet->wire); + const knot_lookup_t *opcode = knot_lookup_by_id(knot_opcode_names, code); + if (opcode != NULL) { + opcode_str = opcode->name; + } else { + (void)snprintf(unknown_opcode, sizeof(unknown_opcode), "OPCODE %d", code); + opcode_str = unknown_opcode; + } + + // Get flags. + size_t flags_rest = sizeof(flags); + const size_t flag_len = 4; + if (knot_wire_get_qr(packet->wire) != 0 && flags_rest > flag_len) { + flags_rest -= strlcat(flags, " qr", flags_rest); + } + if (knot_wire_get_aa(packet->wire) != 0 && flags_rest > flag_len) { + flags_rest -= strlcat(flags, " aa", flags_rest); + } + if (knot_wire_get_tc(packet->wire) != 0 && flags_rest > flag_len) { + flags_rest -= strlcat(flags, " tc", flags_rest); + } + if (knot_wire_get_rd(packet->wire) != 0 && flags_rest > flag_len) { + flags_rest -= strlcat(flags, " rd", flags_rest); + } + if (knot_wire_get_ra(packet->wire) != 0 && flags_rest > flag_len) { + flags_rest -= strlcat(flags, " ra", flags_rest); + } + if (knot_wire_get_z(packet->wire) != 0 && flags_rest > flag_len) { + flags_rest -= strlcat(flags, " z", flags_rest); + } + if (knot_wire_get_ad(packet->wire) != 0 && flags_rest > flag_len) { + flags_rest -= strlcat(flags, " ad", flags_rest); + } + if (knot_wire_get_cd(packet->wire) != 0 && flags_rest > flag_len) { + strlcat(flags, " cd", flags_rest); + } + + uint16_t id = knot_wire_get_id(packet->wire); + uint16_t qdcount = knot_wire_get_qdcount(packet->wire); + uint16_t ancount = knot_wire_get_ancount(packet->wire); + uint16_t nscount = knot_wire_get_nscount(packet->wire); + uint16_t arcount = knot_wire_get_arcount(packet->wire); + + if (knot_pkt_has_tsig(packet)) { + arcount++; + } + + // Print formatted info. + switch (style->format) { + case FORMAT_NSUPDATE: + printf(";; ->>HEADER<<- opcode: %s; status: %s; id: %u\n" + ";; Flags:%1s; " + "ZONE: %u; PREREQ: %u; UPDATE: %u; ADDITIONAL: %u\n", + opcode_str, rcode_str, id, flags, qdcount, ancount, + nscount, arcount); + break; + default: + printf(";; ->>HEADER<<- opcode: %s; status: %s; id: %u\n" + ";; Flags:%1s; " + "QUERY: %u; ANSWER: %u; AUTHORITY: %u; ADDITIONAL: %u\n", + opcode_str, rcode_str, id, flags, qdcount, ancount, + nscount, arcount); + break; + } +} + +static void print_footer(const size_t total_len, + const size_t msg_count, + const size_t rr_count, + const net_t *net, + const float elapsed, + time_t exec_time, + const bool incoming) +{ + struct tm tm; + char date[64]; + + // Get current timestamp. + if (exec_time == 0) { + exec_time = time(NULL); + } + + // Create formatted date-time string. + localtime_r(&exec_time, &tm); + strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S %Z", &tm); + + // Print messages statistics. + if (incoming) { + printf(";; Received %zu B", total_len); + } else { + printf(";; Sent %zu B", total_len); + } + + // If multimessage (XFR) print additional statistics. + if (msg_count > 0) { + printf(" (%zu messages, %zu records)\n", msg_count, rr_count); + } else { + printf("\n"); + } + // Print date. + printf(";; Time %s\n", date); + + // Print connection statistics. + if (net != NULL) { + if (incoming) { + printf(";; From %s", net->remote_str); + } else { + printf(";; To %s", net->remote_str); + } + + if (elapsed >= 0) { + printf(" in %.1f ms\n", elapsed); + } else { + printf("\n"); + } + } +} + +static void print_hex(const uint8_t *data, uint16_t len) +{ + for (int i = 0; i < len; i++) { + printf("%02X", data[i]); + } +} + +static void print_nsid(const uint8_t *data, uint16_t len) +{ + if (len == 0) { + return; + } + + print_hex(data, len); + + // Check if printable string. + for (int i = 0; i < len; i++) { + if (!is_print(data[i])) { + return; + } + } + printf(" \"%.*s\"", len, data); +} + +static void print_edns_client_subnet(const uint8_t *data, uint16_t len) +{ + knot_edns_client_subnet_t ecs = { 0 }; + int ret = knot_edns_client_subnet_parse(&ecs, data, len); + if (ret != KNOT_EOK) { + return; + } + + struct sockaddr_storage addr = { 0 }; + ret = knot_edns_client_subnet_get_addr(&addr, &ecs); + assert(ret == KNOT_EOK); + + char addr_str[SOCKADDR_STRLEN] = { 0 }; + sockaddr_tostr(addr_str, sizeof(addr_str), (struct sockaddr *)&addr); + + printf("%s/%u/%u", addr_str, ecs.source_len, ecs.scope_len); +} + +static void print_section_opt(const knot_pkt_t *packet) +{ + char unknown_ercode[64] = ""; + const char *ercode_str = NULL; + + uint16_t ercode = knot_edns_get_ext_rcode(packet->opt_rr); + if (ercode > 0) { + ercode = knot_edns_whole_rcode(ercode, + knot_wire_get_rcode(packet->wire)); + } + + const knot_lookup_t *item = knot_lookup_by_id(knot_rcode_names, ercode); + if (item != NULL) { + ercode_str = item->name; + } else { + (void)snprintf(unknown_ercode, sizeof(unknown_ercode), "RCODE %d", ercode); + ercode_str = unknown_ercode; + } + + printf("Version: %u; flags: %s; UDP size: %u B; ext-rcode: %s\n", + knot_edns_get_version(packet->opt_rr), + (knot_edns_do(packet->opt_rr) != 0) ? "do" : "", + knot_edns_get_payload(packet->opt_rr), + ercode_str); + + assert(packet->opt_rr->rrs.count > 0); + knot_rdata_t *rdata = packet->opt_rr->rrs.rdata; + wire_ctx_t wire = wire_ctx_init_const(rdata->data, rdata->len); + + while (wire_ctx_available(&wire) >= KNOT_EDNS_OPTION_HDRLEN) { + uint16_t opt_code = wire_ctx_read_u16(&wire); + uint16_t opt_len = wire_ctx_read_u16(&wire); + uint8_t *opt_data = wire.position; + + if (wire.error != KNOT_EOK) { + WARN("invalid OPT record data\n"); + return; + } + + switch (opt_code) { + case KNOT_EDNS_OPTION_NSID: + printf(";; NSID: "); + print_nsid(opt_data, opt_len); + break; + case KNOT_EDNS_OPTION_CLIENT_SUBNET: + printf(";; CLIENT-SUBNET: "); + print_edns_client_subnet(opt_data, opt_len); + break; + case KNOT_EDNS_OPTION_PADDING: + printf(";; PADDING: %u B", opt_len); + break; + case KNOT_EDNS_OPTION_COOKIE: + printf(";; COOKIE: "); + print_hex(opt_data, opt_len); + break; + default: + printf(";; Option (%u): ", opt_code); + print_hex(opt_data, opt_len); + } + printf("\n"); + + wire_ctx_skip(&wire, opt_len); + } + + if (wire_ctx_available(&wire) > 0) { + WARN("invalid OPT record data\n"); + } +} + +static void print_section_question(const knot_dname_t *owner, + const uint16_t qclass, + const uint16_t qtype, + const style_t *style) +{ + size_t buflen = 8192; + char *buf = calloc(buflen, 1); + + // Don't print zero TTL. + knot_dump_style_t qstyle = style->style; + qstyle.empty_ttl = true; + + knot_rrset_t *question = knot_rrset_new(owner, qtype, qclass, 0, NULL); + + if (knot_rrset_txt_dump_header(question, 0, buf, buflen, &qstyle) < 0) { + WARN("can't print whole question section\n"); + } + + printf("%s\n", buf); + + knot_rrset_free(question, NULL); + free(buf); +} + +static void print_section_full(const knot_rrset_t *rrsets, + const uint16_t count, + const style_t *style, + const bool no_tsig) +{ + size_t buflen = 8192; + char *buf = calloc(buflen, 1); + + for (size_t i = 0; i < count; i++) { + // Ignore OPT records. + if (rrsets[i].type == KNOT_RRTYPE_OPT) { + continue; + } + + // Exclude TSIG record. + if (no_tsig && rrsets[i].type == KNOT_RRTYPE_TSIG) { + continue; + } + + if (knot_rrset_txt_dump(&rrsets[i], &buf, &buflen, + &(style->style)) < 0) { + WARN("can't print whole section\n"); + break; + } + printf("%s", buf); + } + + free(buf); +} + +static void print_section_dig(const knot_rrset_t *rrsets, + const uint16_t count, + const style_t *style) +{ + size_t buflen = 8192; + char *buf = calloc(buflen, 1); + + for (size_t i = 0; i < count; i++) { + const knot_rrset_t *rrset = &rrsets[i]; + uint16_t rrset_rdata_count = rrset->rrs.count; + for (uint16_t j = 0; j < rrset_rdata_count; j++) { + while (knot_rrset_txt_dump_data(rrset, j, buf, buflen, + &(style->style)) < 0) { + buflen += 4096; + // Oversize protection. + if (buflen > 100000) { + WARN("can't print whole section\n"); + break; + } + + char *newbuf = realloc(buf, buflen); + if (newbuf == NULL) { + WARN("can't print whole section\n"); + break; + } + buf = newbuf; + } + printf("%s\n", buf); + } + } + + free(buf); +} + +static void print_section_host(const knot_rrset_t *rrsets, + const uint16_t count, + const style_t *style) +{ + size_t buflen = 8192; + char *buf = calloc(buflen, 1); + + for (size_t i = 0; i < count; i++) { + const knot_rrset_t *rrset = &rrsets[i]; + const knot_lookup_t *descr; + char type[32] = "NULL"; + char *owner; + + owner = knot_dname_to_str_alloc(rrset->owner); + if (style->style.ascii_to_idn != NULL) { + style->style.ascii_to_idn(&owner); + } + descr = knot_lookup_by_id(rtypes, rrset->type); + + uint16_t rrset_rdata_count = rrset->rrs.count; + for (uint16_t j = 0; j < rrset_rdata_count; j++) { + if (rrset->type == KNOT_RRTYPE_CNAME && + style->hide_cname) { + continue; + } + + while (knot_rrset_txt_dump_data(rrset, j, buf, buflen, + &(style->style)) < 0) { + buflen += 4096; + // Oversize protection. + if (buflen > 100000) { + WARN("can't print whole section\n"); + break; + } + + char *newbuf = realloc(buf, buflen); + if (newbuf == NULL) { + WARN("can't print whole section\n"); + break; + } + buf = newbuf; + } + + if (descr != NULL) { + printf("%s %s %s\n", owner, descr->name, buf); + } else { + knot_rrtype_to_string(rrset->type, type, + sizeof(type)); + printf("%s has %s record %s\n", + owner, type, buf); + } + } + + free(owner); + } + + free(buf); +} + +static void print_error_host(const knot_pkt_t *packet, const style_t *style) +{ + char type[32] = "Unknown"; + const char *rcode_str = "Unknown"; + + knot_rrtype_to_string(knot_pkt_qtype(packet), type, sizeof(type)); + + // Get extended RCODE. + const char *code_name = knot_pkt_ext_rcode_name(packet); + if (code_name[0] != '\0') { + rcode_str = code_name; + } + + // Get record owner. + char *owner = knot_dname_to_str_alloc(knot_pkt_qname(packet)); + if (style->style.ascii_to_idn != NULL) { + style->style.ascii_to_idn(&owner); + } + + if (knot_pkt_ext_rcode(packet) == KNOT_RCODE_NOERROR) { + printf("Host %s has no %s record\n", owner, type); + } else { + printf("Host %s type %s error: %s\n", owner, type, rcode_str); + } + + free(owner); +} + +knot_pkt_t *create_empty_packet(const uint16_t max_size) +{ + // Create packet skeleton. + knot_pkt_t *packet = knot_pkt_new(NULL, max_size, NULL); + if (packet == NULL) { + DBG_NULL; + return NULL; + } + + // Set random sequence id. + knot_wire_set_id(packet->wire, dnssec_random_uint16_t()); + + return packet; +} + +void print_header_xfr(const knot_pkt_t *packet, const style_t *style) +{ + if (style == NULL) { + DBG_NULL; + return; + } + + char xfr[16] = "AXFR"; + + switch (knot_pkt_qtype(packet)) { + case KNOT_RRTYPE_AXFR: + break; + case KNOT_RRTYPE_IXFR: + xfr[0] = 'I'; + break; + default: + return; + } + + if (style->show_header) { + char *owner = knot_dname_to_str_alloc(knot_pkt_qname(packet)); + if (style->style.ascii_to_idn != NULL) { + style->style.ascii_to_idn(&owner); + } + if (owner != NULL) { + printf(";; %s for %s\n", xfr, owner); + free(owner); + } + } +} + +void print_data_xfr(const knot_pkt_t *packet, + const style_t *style) +{ + if (packet == NULL || style == NULL) { + DBG_NULL; + return; + } + + const knot_pktsection_t *answers = knot_pkt_section(packet, KNOT_ANSWER); + + switch (style->format) { + case FORMAT_DIG: + print_section_dig(knot_pkt_rr(answers, 0), answers->count, style); + break; + case FORMAT_HOST: + print_section_host(knot_pkt_rr(answers, 0), answers->count, style); + break; + case FORMAT_FULL: + print_section_full(knot_pkt_rr(answers, 0), answers->count, style, true); + + // Print TSIG record. + if (style->show_tsig && knot_pkt_has_tsig(packet)) { + print_section_full(packet->tsig_rr, 1, style, false); + } + break; + default: + break; + } +} + +void print_footer_xfr(const size_t total_len, + const size_t msg_count, + const size_t rr_count, + const net_t *net, + const float elapsed, + const time_t exec_time, + const style_t *style) +{ + if (style == NULL) { + DBG_NULL; + return; + } + + if (style->show_footer) { + print_footer(total_len, msg_count, rr_count, net, elapsed, + exec_time, true); + } +} + +void print_packet(const knot_pkt_t *packet, + const net_t *net, + const size_t size, + const float elapsed, + const time_t exec_time, + const bool incoming, + const style_t *style) +{ + if (packet == NULL || style == NULL) { + DBG_NULL; + return; + } + + const knot_pktsection_t *answers = knot_pkt_section(packet, + KNOT_ANSWER); + const knot_pktsection_t *authority = knot_pkt_section(packet, + KNOT_AUTHORITY); + const knot_pktsection_t *additional = knot_pkt_section(packet, + KNOT_ADDITIONAL); + + uint16_t qdcount = knot_wire_get_qdcount(packet->wire); + uint16_t ancount = knot_wire_get_ancount(packet->wire); + uint16_t nscount = knot_wire_get_nscount(packet->wire); + uint16_t arcount = knot_wire_get_arcount(packet->wire); + + // Disable additionals printing if there are no other records. + // OPT record may be placed anywhere within additionals! + if (knot_pkt_has_edns(packet) && arcount == 1) { + arcount = 0; + } + + // Print packet information header. + if (style->show_header) { + if (net != NULL) { + print_tls(&net->tls); + } + print_header(packet, style); + } + + // Print EDNS section. + if (style->show_edns && knot_pkt_has_edns(packet)) { + printf("%s", style->show_section ? "\n;; EDNS PSEUDOSECTION:\n;; " : ";;"); + print_section_opt(packet); + } + + // Print DNS sections. + switch (style->format) { + case FORMAT_DIG: + if (ancount > 0) { + print_section_dig(knot_pkt_rr(answers, 0), ancount, style); + } + break; + case FORMAT_HOST: + if (ancount > 0) { + print_section_host(knot_pkt_rr(answers, 0), ancount, style); + } else { + print_error_host(packet, style); + } + break; + case FORMAT_NSUPDATE: + if (style->show_question && qdcount > 0) { + printf("%s", style->show_section ? "\n;; ZONE SECTION:\n;; " : ";;"); + print_section_question(knot_pkt_qname(packet), + knot_pkt_qclass(packet), + knot_pkt_qtype(packet), + style); + } + + if (style->show_answer && ancount > 0) { + printf("%s", style->show_section ? "\n;; PREREQUISITE SECTION:\n" : ""); + print_section_full(knot_pkt_rr(answers, 0), ancount, style, true); + } + + if (style->show_authority && nscount > 0) { + printf("%s", style->show_section ? "\n;; UPDATE SECTION:\n" : ""); + print_section_full(knot_pkt_rr(authority, 0), nscount, style, true); + } + + if (style->show_additional && arcount > 0) { + printf("%s", style->show_section ? "\n;; ADDITIONAL DATA:\n" : ""); + print_section_full(knot_pkt_rr(additional, 0), arcount, style, true); + } + break; + case FORMAT_FULL: + if (style->show_question && qdcount > 0) { + printf("%s", style->show_section ? "\n;; QUESTION SECTION:\n;; " : ";;"); + print_section_question(knot_pkt_qname(packet), + knot_pkt_qclass(packet), + knot_pkt_qtype(packet), + style); + } + + if (style->show_answer && ancount > 0) { + printf("%s", style->show_section ? "\n;; ANSWER SECTION:\n" : ""); + print_section_full(knot_pkt_rr(answers, 0), ancount, style, true); + } + + if (style->show_authority && nscount > 0) { + printf("%s", style->show_section ? "\n;; AUTHORITY SECTION:\n" : ""); + print_section_full(knot_pkt_rr(authority, 0), nscount, style, true); + } + + if (style->show_additional && arcount > 0) { + printf("%s", style->show_section ? "\n;; ADDITIONAL SECTION:\n" : ""); + print_section_full(knot_pkt_rr(additional, 0), arcount, style, true); + } + break; + default: + break; + } + + // Print TSIG section. + if (style->show_tsig && knot_pkt_has_tsig(packet)) { + printf("%s", style->show_section ? "\n;; TSIG PSEUDOSECTION:\n" : ""); + print_section_full(packet->tsig_rr, 1, style, false); + } + + // Print packet statistics. + if (style->show_footer) { + printf("\n"); + print_footer(size, 0, 0, net, elapsed, exec_time, incoming); + } +} diff --git a/src/utils/common/exec.h b/src/utils/common/exec.h new file mode 100644 index 0000000..0c5ad5d --- /dev/null +++ b/src/utils/common/exec.h @@ -0,0 +1,97 @@ +/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \file + * + * \brief Common executives for utils. + * + * \addtogroup knot_utils + * @{ + */ + +#pragma once + +#include <time.h> + +#include "utils/common/netio.h" +#include "utils/common/params.h" +#include "libknot/libknot.h" + +/*! + * \brief Allocates empty packet and sets packet size and random id. + * + * \param max_size Maximal packet size. + * + * \retval packet if success. + * \retval NULL if error. + */ +knot_pkt_t *create_empty_packet(const uint16_t max_size); + +/*! + * \brief Prints information header for transfer. + * + * \param packet Parsed packet. + * \param style Style of the output. + */ +void print_header_xfr(const knot_pkt_t *packet, const style_t *style); + +/*! + * \brief Prints answer section for 1 transfer message. + * + * \param packet Response packet. + * \param style Style of the output. + */ +void print_data_xfr(const knot_pkt_t *packet, const style_t *style); + +/*! + * \brief Prints trailing statistics for transfer. + * + * \param total_len Total reply size (all messages). + * \param msg_count Number of messages. + * \param rr_count Total number of answer records. + * \param net Connection information. + * \param elapsed Total elapsed time. + * \param exec_time Time of the packet creation. + * \param style Style of the otput. + */ +void print_footer_xfr(const size_t total_len, + const size_t msg_count, + const size_t rr_count, + const net_t *net, + const float elapsed, + const time_t exec_time, + const style_t *style); + +/*! + * \brief Prints one response packet. + * + * \param packet Response packet. + * \param net Connection information. + * \param size Original packet wire size. + * \param elapsed Total elapsed time. + * \param exec_time Time of the packet creation. + * \param incoming Indicates if the packet is input. + * \param style Style of the otput. + */ +void print_packet(const knot_pkt_t *packet, + const net_t *net, + const size_t size, + const float elapsed, + const time_t exec_time, + const bool incoming, + const style_t *style); + +/*! @} */ diff --git a/src/utils/common/hex.c b/src/utils/common/hex.c new file mode 100644 index 0000000..2ffa9f2 --- /dev/null +++ b/src/utils/common/hex.c @@ -0,0 +1,82 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <assert.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include "libknot/libknot.h" +#include "contrib/ctype.h" +#include "contrib/tolower.h" + +/*! + * \brief Convert HEX char to byte. + * \note Expects valid lowercase letters. + */ +static uint8_t hex_to_num(int c) +{ + if (c >= '0' && c <= '9') { + return c - '0'; + } else { + return c - 'a' + 10; + } +} + +/*! + * \brief Convert string encoded in hex to bytes. + */ +int hex_decode(const char *input, uint8_t **output, size_t *output_size) +{ + if (!input || input[0] == '\0' || !output || !output_size) { + return KNOT_EINVAL; + } + + // input validation (length and content) + + size_t input_size = strlen(input); + if (input_size % 2 != 0) { + return KNOT_EMALF; + } + + for (size_t i = 0; i < input_size; i++) { + if (!is_xdigit(input[i])) { + return KNOT_EMALF; + } + } + + // output allocation + + size_t result_size = input_size / 2; + assert(result_size > 0); + uint8_t *result = malloc(result_size); + if (!result) { + return KNOT_ENOMEM; + } + + // conversion + + for (size_t i = 0; i < result_size; i++) { + int high_nib = knot_tolower(input[2 * i]); + int low_nib = knot_tolower(input[2 * i + 1]); + + result[i] = hex_to_num(high_nib) << 4 | hex_to_num(low_nib); + } + + *output = result; + *output_size = result_size; + + return KNOT_EOK; +} diff --git a/src/utils/common/hex.h b/src/utils/common/hex.h new file mode 100644 index 0000000..8f9f445 --- /dev/null +++ b/src/utils/common/hex.h @@ -0,0 +1,42 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +/*! + * \file + * + * \brief Coversion between HEX strings and bytes. + * + * \addtogroup knot_utils + * @{ + */ + +#pragma once + +#include <stdint.h> +#include <stdlib.h> + +/*! + * \brief Convert string encoded in hex to bytes. + * + * \param input Hex encoded input string. + * \param output Decoded bytes. + * \param output_size Size of the output. + * + * \return Error code, KNOT_EOK if successful. + */ +int hex_decode(const char *input, uint8_t **output, size_t *output_size); + +/*! @} */ diff --git a/src/utils/common/lookup.c b/src/utils/common/lookup.c new file mode 100644 index 0000000..168d649 --- /dev/null +++ b/src/utils/common/lookup.c @@ -0,0 +1,279 @@ +/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <string.h> + +#include "utils/common/lookup.h" +#include "contrib/mempattern.h" +#include "contrib/ucw/mempool.h" +#include "libknot/error.h" + +int lookup_init(lookup_t *lookup) +{ + if (lookup == NULL) { + return KNOT_EINVAL; + } + memset(lookup, 0, sizeof(*lookup)); + + mm_ctx_mempool(&lookup->mm, MM_DEFAULT_BLKSIZE); + lookup->trie = trie_create(&lookup->mm); + if (lookup->trie == NULL) { + mp_delete(lookup->mm.ctx); + return KNOT_ENOMEM; + } + + return KNOT_EOK; +} + +static void reset_output(lookup_t *lookup) +{ + if (lookup == NULL) { + return; + } + + mm_free(&lookup->mm, lookup->found.key); + lookup->found.key = NULL; + lookup->found.data = NULL; + + lookup->iter.count = 0; + + mm_free(&lookup->mm, lookup->iter.first_key); + lookup->iter.first_key = NULL; + + trie_it_free(lookup->iter.it); + lookup->iter.it = NULL; +} + +void lookup_deinit(lookup_t *lookup) +{ + if (lookup == NULL) { + return; + } + + reset_output(lookup); + + trie_free(lookup->trie); + mp_delete(lookup->mm.ctx); +} + +int lookup_insert(lookup_t *lookup, const char *str, void *data) +{ + if (lookup == NULL || str == NULL) { + return KNOT_EINVAL; + } + + size_t str_len = strlen(str); + if (str_len == 0) { + return KNOT_EINVAL; + } + + trie_val_t *val = trie_get_ins(lookup->trie, str, str_len); + if (val == NULL) { + return KNOT_ENOMEM; + } + *val = data; + + return KNOT_EOK; +} + +static int set_key(lookup_t *lookup, char **dst, const char *key, size_t key_len) +{ + if (*dst != NULL) { + mm_free(&lookup->mm, *dst); + } + *dst = mm_alloc(&lookup->mm, key_len + 1); + if (*dst == NULL) { + return KNOT_ENOMEM; + } + memcpy(*dst, key, key_len); + (*dst)[key_len] = '\0'; + + return KNOT_EOK; +} + +int lookup_search(lookup_t *lookup, const char *str, size_t str_len) +{ + if (lookup == NULL) { + return KNOT_EINVAL; + } + + // Change NULL string to the empty one. + if (str == NULL) { + str = ""; + } + + reset_output(lookup); + + size_t new_len = 0; + trie_it_t *it = trie_it_begin(lookup->trie); + for (; !trie_it_finished(it); trie_it_next(it)) { + size_t len; + const char *key = trie_it_key(it, &len); + + // Compare with a shorter key. + if (len < str_len) { + int ret = memcmp(str, key, len); + if (ret >= 0) { + continue; + } else { + break; + } + } + + // Compare with an equal length or longer key. + int ret = memcmp(str, key, str_len); + if (ret == 0) { + lookup->iter.count++; + + // First candidate. + if (lookup->iter.count == 1) { + ret = set_key(lookup, &lookup->found.key, key, len); + if (ret != KNOT_EOK) { + break; + } + lookup->found.data = *trie_it_val(it); + new_len = len; + // Another candidate. + } else if (new_len > str_len) { + if (new_len > len) { + new_len = len; + } + while (memcmp(lookup->found.key, key, new_len) != 0) { + new_len--; + } + } + // Stop if greater than the key, and also than all the following keys. + } else if (ret < 0) { + break; + } + } + trie_it_free(it); + + switch (lookup->iter.count) { + case 0: + return KNOT_ENOENT; + case 1: + return KNOT_EOK; + default: + // Store full name of the first candidate. + if (set_key(lookup, &lookup->iter.first_key, lookup->found.key, + strlen(lookup->found.key)) != KNOT_EOK) { + return KNOT_ENOMEM; + } + lookup->found.key[new_len] = '\0'; + lookup->found.data = NULL; + + return KNOT_EFEWDATA; + } +} + +void lookup_list(lookup_t *lookup) +{ + if (lookup == NULL || lookup->iter.first_key == NULL) { + return; + } + + if (lookup->iter.it != NULL) { + if (trie_it_finished(lookup->iter.it)) { + trie_it_free(lookup->iter.it); + lookup->iter.it = NULL; + return; + } + + trie_it_next(lookup->iter.it); + + size_t len; + const char *key = trie_it_key(lookup->iter.it, &len); + + int ret = set_key(lookup, &lookup->found.key, key, len); + if (ret == KNOT_EOK) { + lookup->found.data = *trie_it_val(lookup->iter.it); + } + return; + } + + lookup->iter.it = trie_it_begin(lookup->trie); + while (!trie_it_finished(lookup->iter.it)) { + size_t len; + const char *key = trie_it_key(lookup->iter.it, &len); + + if (strncmp(key, lookup->iter.first_key, len) == 0) { + int ret = set_key(lookup, &lookup->found.key, key, len); + if (ret == KNOT_EOK) { + lookup->found.data = *trie_it_val(lookup->iter.it); + } + break; + } + trie_it_next(lookup->iter.it); + } +} + +static void print_options(lookup_t *lookup, EditLine *el) +{ + // Get terminal lines. + unsigned lines = 0; + if (el_get(el, EL_GETTC, "li", &lines) != 0 || lines < 3) { + return; + } + + for (size_t i = 1; i <= lookup->iter.count; i++) { + lookup_list(lookup); + printf("\n%s", lookup->found.key); + + if (i > 1 && i % (lines - 1) == 0 && i < lookup->iter.count) { + printf("\n Display next from %zu possibilities? (y or n)", + lookup->iter.count); + char next; + el_getc(el, &next); + if (next != 'y') { + break; + } + } + } + + printf("\n"); + fflush(stdout); +} + +void lookup_complete(lookup_t *lookup, const char *str, size_t str_len, + EditLine *el, bool add_space) +{ + if (lookup == NULL || el == NULL) { + return; + } + + // Try to complete the command name. + int ret = lookup_search(lookup, str, str_len); + switch (ret) { + case KNOT_EOK: + el_deletestr(el, str_len); + el_insertstr(el, lookup->found.key); + if (add_space) { + el_insertstr(el, " "); + } + break; + case KNOT_EFEWDATA: + if (strlen(lookup->found.key) > str_len) { + el_deletestr(el, str_len); + el_insertstr(el, lookup->found.key); + } else { + print_options(lookup, el); + } + break; + default: + break; + } +} diff --git a/src/utils/common/lookup.h b/src/utils/common/lookup.h new file mode 100644 index 0000000..a8eb065 --- /dev/null +++ b/src/utils/common/lookup.h @@ -0,0 +1,122 @@ +/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \file + * + * \brief Lookup container for textual strings. + * + * \addtogroup knot_utils + * @{ + */ + +#pragma once + +#include <histedit.h> + +#include "libknot/mm_ctx.h" +#include "contrib/qp-trie/trie.h" + +/*! Lookup context. */ +typedef struct { + /*! Memory pool context. */ + knot_mm_t mm; + /*! Main trie storage. */ + trie_t *trie; + + /*! Current (iteration) data context. */ + struct { + /*! Stored key. */ + char *key; + /*! Corresponding key data. */ + void *data; + } found; + + /*! Iteration context. */ + struct { + /*! Total number of possibilies. */ + size_t count; + /*! The first possibility. */ + char *first_key; + /*! Hat-trie iterator. */ + trie_it_t *it; + } iter; +} lookup_t; + +/*! + * Initializes the lookup context. + * + * \param[in] lookup Lookup context. + * + * \return Error code, KNOT_EOK if successful. + */ +int lookup_init(lookup_t *lookup); + +/*! + * Deinitializes the lookup context. + * + * \param[in] lookup Lookup context. + */ +void lookup_deinit(lookup_t *lookup); + +/*! + * Inserts given key and data into the lookup. + * + * \param[in] lookup Lookup context. + * \param[in] str Textual key. + * \param[in] data Key textual data. + * + * \return Error code, KNOT_EOK if successful. + */ +int lookup_insert(lookup_t *lookup, const char *str, void *data); + +/*! + * Searches the lookup container for the given key. + * + * \note If one candidate, lookup.found contains the key/data, + * if more candidates, lookup.found contains the common key prefix and + * lookup.iter.first_key is the first candidate key. + * + * \param[in] lookup Lookup context. + * \param[in] str Textual key. + * \param[in] str_len Textual key length. + * + * \return Error code, KNOT_EOK if 1 candidate, KNOT_ENOENT if no candidate, + * and KNOT_EFEWDATA if more candidates are possible. + */ +int lookup_search(lookup_t *lookup, const char *str, size_t str_len); + +/*! + * Moves the lookup iterator to the next key candidate. + * + * \note lookup.found is updated. + * + * \param[in] lookup Lookup context. + */ +void lookup_list(lookup_t *lookup); + +/*! + * Completes the string based on the lookup content or prints all candidates. + * + * \param[in] lookup Lookup context. + * \param[in] str Textual key. + * \param[in] str_len Textual key length. + * \param[in] el Editline context. + * \param[in] add_space Add one space after completed string flag. + */ +void lookup_complete(lookup_t *lookup, const char *str, size_t str_len, + EditLine *el, bool add_space); + +/*! @} */ diff --git a/src/utils/common/msg.c b/src/utils/common/msg.c new file mode 100644 index 0000000..c3375d3 --- /dev/null +++ b/src/utils/common/msg.c @@ -0,0 +1,40 @@ +/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> + +#include "utils/common/msg.h" + +static volatile int MSG_DBG_STATE = 0; /* True if debugging is enabled. */ + +int msg_enable_debug(int val) +{ + return MSG_DBG_STATE = val; +} + +int msg_debug(const char *fmt, ...) +{ + int n = 0; + if (MSG_DBG_STATE) { + va_list ap; + va_start(ap, fmt); + n = vprintf(fmt, ap); + va_end(ap); + } + return n; +} diff --git a/src/utils/common/msg.h b/src/utils/common/msg.h new file mode 100644 index 0000000..a300afe --- /dev/null +++ b/src/utils/common/msg.h @@ -0,0 +1,50 @@ +/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \file msg.h + * + * \author Daniel Salzman <daniel.salzman@nic.cz> + * + * \brief Simple output formatting framework. + * + * \addtogroup knot_utils + * @{ + */ + +#pragma once + +#include <stdio.h> + +#define ERROR_ ";; ERROR: " +#define INFO_ ";; INFO: " +#define WARNING_ ";; WARNING: " +#define DEBUG_ ";; DEBUG: " + +#define ERR(msg, ...) { fprintf(stderr, ERROR_ msg, ##__VA_ARGS__); fflush(stderr); } +#define INFO(msg, ...) { fprintf(stdout, INFO_ msg, ##__VA_ARGS__); fflush(stdout); } +#define WARN(msg, ...) { fprintf(stderr, WARNING_ msg, ##__VA_ARGS__); fflush(stderr); } +#define DBG(msg, ...) msg_debug(DEBUG_ msg, ##__VA_ARGS__) + +/*! \brief Enable/disable debugging. */ +int msg_enable_debug(int val); + +/*! \brief Print debug message. */ +int msg_debug(const char *fmt, ...); + +/*! \brief Debug message for null input. */ +#define DBG_NULL DBG("%s: null parameter\n", __func__) + +/*! @} */ diff --git a/src/utils/common/netio.c b/src/utils/common/netio.c new file mode 100644 index 0000000..9a27d33 --- /dev/null +++ b/src/utils/common/netio.c @@ -0,0 +1,593 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <arpa/inet.h> +#include <fcntl.h> +#include <netdb.h> +#include <poll.h> +#include <stdlib.h> +#include <netinet/in.h> +#include <sys/socket.h> + +#ifdef HAVE_SYS_UIO_H +#include <sys/uio.h> +#endif + +#include "utils/common/netio.h" +#include "utils/common/msg.h" +#include "utils/common/tls.h" +#include "libknot/libknot.h" +#include "contrib/sockaddr.h" + +srv_info_t *srv_info_create(const char *name, const char *service) +{ + if (name == NULL || service == NULL) { + DBG_NULL; + return NULL; + } + + // Create output structure. + srv_info_t *server = calloc(1, sizeof(srv_info_t)); + + // Check output. + if (server == NULL) { + return NULL; + } + + // Fill output. + server->name = strdup(name); + server->service = strdup(service); + + if (server->name == NULL || server->service == NULL) { + srv_info_free(server); + return NULL; + } + + // Return result. + return server; +} + +void srv_info_free(srv_info_t *server) +{ + if (server == NULL) { + DBG_NULL; + return; + } + + free(server->name); + free(server->service); + free(server); +} + +int get_iptype(const ip_t ip) +{ + switch (ip) { + case IP_4: + return AF_INET; + case IP_6: + return AF_INET6; + default: + return AF_UNSPEC; + } +} + +int get_socktype(const protocol_t proto, const uint16_t type) +{ + switch (proto) { + case PROTO_TCP: + return SOCK_STREAM; + case PROTO_UDP: + return SOCK_DGRAM; + default: + if (type == KNOT_RRTYPE_AXFR || type == KNOT_RRTYPE_IXFR) { + return SOCK_STREAM; + } else { + return SOCK_DGRAM; + } + } +} + +const char *get_sockname(const int socktype) +{ + switch (socktype) { + case SOCK_STREAM: + return "TCP"; + case SOCK_DGRAM: + return "UDP"; + default: + return "UNKNOWN"; + } +} + +static int get_addr(const srv_info_t *server, + const int iptype, + const int socktype, + struct addrinfo **info) +{ + struct addrinfo hints; + + // Set connection hints. + memset(&hints, 0, sizeof(hints)); + hints.ai_family = iptype; + hints.ai_socktype = socktype; + + // Get connection parameters. + if (getaddrinfo(server->name, server->service, &hints, info) != 0) { + ERR("can't resolve address %s@%s\n", + server->name, server->service); + return -1; + } + + return 0; +} + +void get_addr_str(const struct sockaddr_storage *ss, + const int socktype, + char **dst) +{ + char addr_str[SOCKADDR_STRLEN] = {0}; + + // Get network address string and port number. + sockaddr_tostr(addr_str, sizeof(addr_str), (struct sockaddr *)ss); + + // Calculate needed buffer size + const char *sock_name = get_sockname(socktype); + size_t buflen = strlen(addr_str) + strlen(sock_name) + 3 /* () */; + + // Free previous string if any and write result + free(*dst); + *dst = malloc(buflen); + if (*dst != NULL) { + int ret = snprintf(*dst, buflen, "%s(%s)", addr_str, sock_name); + if (ret <= 0 || ret >= buflen) { + **dst = '\0'; + } + } +} + +int net_init(const srv_info_t *local, + const srv_info_t *remote, + const int iptype, + const int socktype, + const int wait, + const net_flags_t flags, + const tls_params_t *tls_params, + net_t *net) +{ + if (remote == NULL || net == NULL) { + DBG_NULL; + return KNOT_EINVAL; + } + + // Clean network structure. + memset(net, 0, sizeof(*net)); + + // Get remote address list. + if (get_addr(remote, iptype, socktype, &net->remote_info) != 0) { + net_clean(net); + return KNOT_NET_EADDR; + } + + // Set current remote address. + net->srv = net->remote_info; + + // Get local address if specified. + if (local != NULL) { + if (get_addr(local, iptype, socktype, &net->local_info) != 0) { + net_clean(net); + return KNOT_NET_EADDR; + } + } + + // Store network parameters. + net->iptype = iptype; + net->socktype = socktype; + net->wait = wait; + net->local = local; + net->remote = remote; + net->flags = flags; + + // Prepare for TLS. + if (tls_params != NULL && tls_params->enable) { + int ret = tls_ctx_init(&net->tls, tls_params, net->wait); + if (ret != KNOT_EOK) { + net_clean(net); + return ret; + } + } + + return KNOT_EOK; +} + +/** + * Connect with TCP Fast Open. + */ +static int fastopen_connect(int sockfd, const struct addrinfo *srv) +{ +#if __APPLE__ + // connection is performed lazily when first data are sent + struct sa_endpoints ep = {0}; + ep.sae_dstaddr = srv->ai_addr; + ep.sae_dstaddrlen = srv->ai_addrlen; + int flags = CONNECT_DATA_IDEMPOTENT|CONNECT_RESUME_ON_READ_WRITE; + + return connectx(sockfd, &ep, SAE_ASSOCID_ANY, flags, NULL, 0, NULL, NULL); +#elif defined(MSG_FASTOPEN) // Linux with RFC 7413 + // connect() will be called implicitly with sendto(), sendmsg() + return 0; +#else + errno = ENOTSUP; + return -1; +#endif +} + +/** + * Sends data with TCP Fast Open. + */ +static int fastopen_send(int sockfd, const struct msghdr *msg, int timeout) +{ +#if __APPLE__ + return sendmsg(sockfd, msg, 0); +#elif defined(MSG_FASTOPEN) + int ret = sendmsg(sockfd, msg, MSG_FASTOPEN); + if (ret == -1 && errno == EINPROGRESS) { + struct pollfd pfd = { + .fd = sockfd, + .events = POLLOUT, + .revents = 0, + }; + if (poll(&pfd, 1, 1000 * timeout) != 1) { + errno = ETIMEDOUT; + return -1; + } + ret = sendmsg(sockfd, msg, 0); + } + return ret; +#else + errno = ENOTSUP; + return -1; +#endif +} + +int net_connect(net_t *net) +{ + if (net == NULL || net->srv == NULL) { + DBG_NULL; + return KNOT_EINVAL; + } + + // Set remote information string. + get_addr_str((struct sockaddr_storage *)net->srv->ai_addr, + net->socktype, &net->remote_str); + + // Create socket. + int sockfd = socket(net->srv->ai_family, net->socktype, 0); + if (sockfd == -1) { + WARN("can't create socket for %s\n", net->remote_str); + return KNOT_NET_ESOCKET; + } + + // Initialize poll descriptor structure. + struct pollfd pfd = { + .fd = sockfd, + .events = POLLOUT, + .revents = 0, + }; + + // Set non-blocking socket. + if (fcntl(sockfd, F_SETFL, O_NONBLOCK) == -1) { + WARN("can't set non-blocking socket for %s\n", net->remote_str); + return KNOT_NET_ESOCKET; + } + + // Bind address to socket if specified. + if (net->local_info != NULL) { + if (bind(sockfd, net->local_info->ai_addr, + net->local_info->ai_addrlen) == -1) { + WARN("can't assign address %s\n", net->local->name); + return KNOT_NET_ESOCKET; + } + } + + if (net->socktype == SOCK_STREAM) { + int cs, err, ret = 0; + socklen_t err_len = sizeof(err); + bool fastopen = net->flags & NET_FLAGS_FASTOPEN; + + // Connect using socket. + if (fastopen) { + ret = fastopen_connect(sockfd, net->srv); + } else { + ret = connect(sockfd, net->srv->ai_addr, net->srv->ai_addrlen); + } + if (ret != 0 && errno != EINPROGRESS) { + WARN("can't connect to %s\n", net->remote_str); + close(sockfd); + return KNOT_NET_ECONNECT; + } + + // Check for connection timeout. + if (!fastopen && poll(&pfd, 1, 1000 * net->wait) != 1) { + WARN("connection timeout for %s\n", net->remote_str); + close(sockfd); + return KNOT_NET_ECONNECT; + } + + // Check if NB socket is writeable. + cs = getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &err, &err_len); + if (cs < 0 || err != 0) { + WARN("can't connect to %s\n", net->remote_str); + close(sockfd); + return KNOT_NET_ECONNECT; + } + + // Establish TLS connection. + if (net->tls.params != NULL) { + int ret = tls_ctx_connect(&net->tls, sockfd, net->tls.params->sni); + if (ret != KNOT_EOK) { + close(sockfd); + return ret; + } + } + } + + // Store socket descriptor. + net->sockfd = sockfd; + + return KNOT_EOK; +} + +int net_set_local_info(net_t *net) +{ + if (net == NULL) { + DBG_NULL; + return KNOT_EINVAL; + } + + if (net->local_info != NULL) { + free(net->local_info->ai_addr); + freeaddrinfo(net->local_info); + } + + socklen_t local_addr_len = sizeof(struct sockaddr_storage); + struct sockaddr_storage *local_addr = calloc(1, local_addr_len); + + if (getsockname(net->sockfd, (struct sockaddr *)local_addr, + &local_addr_len) == -1) { + WARN("can't get local address\n"); + free(local_addr); + return KNOT_NET_ESOCKET; + } + + net->local_info = calloc(1, sizeof(struct addrinfo)); + net->local_info->ai_family = net->srv->ai_family; + net->local_info->ai_socktype = net->srv->ai_socktype; + net->local_info->ai_protocol = net->srv->ai_protocol; + net->local_info->ai_addrlen = local_addr_len; + net->local_info->ai_addr = (struct sockaddr *)local_addr; + + get_addr_str((struct sockaddr_storage *)net->local_info->ai_addr, + net->socktype, &net->local_str); + + return KNOT_EOK; +} + +int net_send(const net_t *net, const uint8_t *buf, const size_t buf_len) +{ + if (net == NULL || buf == NULL) { + DBG_NULL; + return KNOT_EINVAL; + } + + // Send data over UDP. + if (net->socktype == SOCK_DGRAM) { + if (sendto(net->sockfd, buf, buf_len, 0, net->srv->ai_addr, + net->srv->ai_addrlen) != (ssize_t)buf_len) { + WARN("can't send query to %s\n", net->remote_str); + return KNOT_NET_ESEND; + } + // Send data over TLS. + } else if (net->tls.params != NULL) { + int ret = tls_ctx_send((tls_ctx_t *)&net->tls, buf, buf_len); + if (ret != KNOT_EOK) { + WARN("can't send query to %s\n", net->remote_str); + return KNOT_NET_ESEND; + } + // Send data over TCP. + } else { + bool fastopen = net->flags & NET_FLAGS_FASTOPEN; + + // Leading packet length bytes. + uint16_t pktsize = htons(buf_len); + + struct iovec iov[2]; + iov[0].iov_base = &pktsize; + iov[0].iov_len = sizeof(pktsize); + iov[1].iov_base = (uint8_t *)buf; + iov[1].iov_len = buf_len; + + // Compute packet total length. + ssize_t total = iov[0].iov_len + iov[1].iov_len; + + struct msghdr msg = {0}; + msg.msg_iov = iov; + msg.msg_iovlen = sizeof(iov) / sizeof(*iov); + msg.msg_name = net->srv->ai_addr; + msg.msg_namelen = net->srv->ai_addrlen; + + int ret = 0; + if (fastopen) { + ret = fastopen_send(net->sockfd, &msg, net->wait); + } else { + ret = sendmsg(net->sockfd, &msg, 0); + } + if (ret != total) { + WARN("can't send query to %s\n", net->remote_str); + return KNOT_NET_ESEND; + } + } + + return KNOT_EOK; +} + +int net_receive(const net_t *net, uint8_t *buf, const size_t buf_len) +{ + if (net == NULL || buf == NULL) { + DBG_NULL; + return KNOT_EINVAL; + } + + // Initialize poll descriptor structure. + struct pollfd pfd = { + .fd = net->sockfd, + .events = POLLIN, + .revents = 0, + }; + + // Receive data over UDP. + if (net->socktype == SOCK_DGRAM) { + struct sockaddr_storage from; + memset(&from, '\0', sizeof(from)); + + // Receive replies unless correct reply or timeout. + while (true) { + socklen_t from_len = sizeof(from); + + // Wait for datagram data. + if (poll(&pfd, 1, 1000 * net->wait) != 1) { + WARN("response timeout for %s\n", + net->remote_str); + return KNOT_NET_ETIMEOUT; + } + + // Receive whole UDP datagram. + ssize_t ret = recvfrom(net->sockfd, buf, buf_len, 0, + (struct sockaddr *)&from, &from_len); + if (ret <= 0) { + WARN("can't receive reply from %s\n", + net->remote_str); + return KNOT_NET_ERECV; + } + + // Compare reply address with the remote one. + if (from_len > sizeof(from) || + memcmp(&from, net->srv->ai_addr, from_len) != 0) { + char *src = NULL; + get_addr_str(&from, net->socktype, &src); + WARN("unexpected reply source %s\n", src); + free(src); + continue; + } + + return ret; + } + // Receive data over TLS. + } else if (net->tls.params != NULL) { + int ret = tls_ctx_receive((tls_ctx_t *)&net->tls, buf, buf_len); + if (ret < 0) { + WARN("can't receive reply from %s\n", net->remote_str); + return KNOT_NET_ERECV; + } + + return ret; + // Receive data over TCP. + } else { + uint32_t total = 0; + + uint16_t msg_len = 0; + // Receive TCP message header. + while (total < sizeof(msg_len)) { + if (poll(&pfd, 1, 1000 * net->wait) != 1) { + WARN("response timeout for %s\n", + net->remote_str); + return KNOT_NET_ETIMEOUT; + } + + // Receive piece of message. + ssize_t ret = recv(net->sockfd, (uint8_t *)&msg_len + total, + sizeof(msg_len) - total, 0); + if (ret <= 0) { + WARN("can't receive reply from %s\n", + net->remote_str); + return KNOT_NET_ERECV; + } + total += ret; + } + + // Convert number to host format. + msg_len = ntohs(msg_len); + if (msg_len > buf_len) { + return KNOT_ESPACE; + } + + total = 0; + + // Receive whole answer message by parts. + while (total < msg_len) { + if (poll(&pfd, 1, 1000 * net->wait) != 1) { + WARN("response timeout for %s\n", + net->remote_str); + return KNOT_NET_ETIMEOUT; + } + + // Receive piece of message. + ssize_t ret = recv(net->sockfd, buf + total, msg_len - total, 0); + if (ret <= 0) { + WARN("can't receive reply from %s\n", + net->remote_str); + return KNOT_NET_ERECV; + } + total += ret; + } + + return total; + } + + return KNOT_NET_ERECV; +} + +void net_close(net_t *net) +{ + if (net == NULL) { + DBG_NULL; + return; + } + + tls_ctx_close(&net->tls); + close(net->sockfd); + net->sockfd = -1; +} + +void net_clean(net_t *net) +{ + if (net == NULL) { + DBG_NULL; + return; + } + + free(net->local_str); + free(net->remote_str); + + if (net->local_info != NULL) { + freeaddrinfo(net->local_info); + } + + if (net->remote_info != NULL) { + freeaddrinfo(net->remote_info); + } + + tls_ctx_deinit(&net->tls); +} diff --git a/src/utils/common/netio.h b/src/utils/common/netio.h new file mode 100644 index 0000000..d861149 --- /dev/null +++ b/src/utils/common/netio.h @@ -0,0 +1,229 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \file + * + * \brief Networking abstraction for utilities. + * + * \addtogroup knot_utils + * @{ + */ + +#pragma once + +#include <netdb.h> +#include <stdint.h> +#include <sys/socket.h> + +#include "utils/common/params.h" +#include "utils/common/tls.h" + +/*! \brief Structure containing server information. */ +typedef struct { + /*! List node (for list container). */ + node_t n; + /*! Name or address of the server. */ + char *name; + /*! Name or number of the service. */ + char *service; +} srv_info_t; + +typedef enum { + NET_FLAGS_NONE = 0, + NET_FLAGS_FASTOPEN = 1 << 0, +} net_flags_t; + +typedef struct { + /*! Socket descriptor. */ + int sockfd; + + /*! IP protocol type. */ + int iptype; + /*! Socket type. */ + int socktype; + /*! Timeout for all network operations. */ + int wait; + /*! Connection flags. */ + net_flags_t flags; + + /*! Local interface parameters. */ + const srv_info_t *local; + /*! Remote server parameters. */ + const srv_info_t *remote; + + /*! Local description string (used for logging). */ + char *local_str; + /*! Remote description string (used for logging). */ + char *remote_str; + + /*! Output from getaddrinfo for remote server. If the server is + * specified using domain name, this structure may contain more + * results. + */ + struct addrinfo *remote_info; + /*! Currently used result from remote_info. */ + struct addrinfo *srv; + /*! Output from getaddrinfo for local address. Only first result is + * used. + */ + struct addrinfo *local_info; + + /*! TLS context. */ + tls_ctx_t tls; +} net_t; + +/*! + * \brief Creates and fills server structure. + * + * \param name Address or host name. + * \param service Port number or service name. + * + * \retval server if success. + * \retval NULL if error. + */ +srv_info_t *srv_info_create(const char *name, const char *service); + +/*! + * \brief Destroys server structure. + * + * \param server Server structure to destroy. + */ +void srv_info_free(srv_info_t *server); + +/*! + * \brief Translates enum IP version type to int version. + * + * \param ip IP version to convert. + * + * \retval AF_INET, AF_INET6 or AF_UNSPEC. + */ +int get_iptype(const ip_t ip); + +/*! + * \brief Translates enum IP protocol type to int version in context to the + * current DNS query type. + * + * \param proto IP protocol type to convert. + * \param type DNS query type number. + * + * \retval SOCK_STREAM or SOCK_DGRAM. + */ +int get_socktype(const protocol_t proto, const uint16_t type); + +/*! + * \brief Translates int socket type to the common string one. + * + * \param socktype Socket type (SOCK_STREAM or SOCK_DGRAM). + * + * \retval "TCP" or "UDP". + */ +const char *get_sockname(const int socktype); + +/*! + * \brief Translates int socket type to the common string one. + * + * \param ss Socket address storage. + * \param socktype Socket type (SOCK_STREAM or SOCK_DGRAM). + * \param dst Output string. + */ +void get_addr_str(const struct sockaddr_storage *ss, + const int socktype, + char **dst); + +/*! + * \brief Initializes network structure and resolves local and remote addresses. + * + * \param local Local address and service description. + * \param remote Remote address and service description. + * \param iptype IP version. + * \param socktype Socket type. + * \param wait Network timeout interval. + * \param tls_params TLS parameters. + * \param flags Connection flags. + * \param net Network structure to initialize. + * + * \retval KNOT_EOK if success. + * \retval errcode if error. + */ +int net_init(const srv_info_t *local, + const srv_info_t *remote, + const int iptype, + const int socktype, + const int wait, + const net_flags_t flags, + const tls_params_t *tls_params, + net_t *net); + +/*! + * \brief Creates socket and connects (if TCP) to remote address specified + * by net->srv. + * + * \param net Connection parameters. + * + * \retval KNOT_EOK if success. + * \retval errcode if error. + */ +int net_connect(net_t *net); + +/*! + * \brief Fills in local address information. + * + * \param net Connection parameters. + * + * \retval KNOT_EOK if success. + * \retval errcode if error. + */ +int net_set_local_info(net_t *net); + +/*! + * \brief Sends data to connected remote server. + * + * \param net Connection parameters. + * \param buf Data to send. + * \param buf_len Length of the data to send. + * + * \retval KNOT_EOK if success. + * \retval errcode if error. + */ +int net_send(const net_t *net, const uint8_t *buf, const size_t buf_len); + +/*! + * \brief Receives data from connected remote server. + * + * \param net Connection parameters. + * \param buf Buffer for incoming data. + * \param buf_len Length of the buffer. + * + * \retval >=0 length of successfully received data. + * \retval errcode if error. + */ +int net_receive(const net_t *net, uint8_t *buf, const size_t buf_len); + +/*! + * \brief Closes current network connection. + * + * \param net Connection parameters. + */ +void net_close(net_t *net); + +/*! + * \brief Cleans up network structure. + * + * \param net Connection parameters. + */ +void net_clean(net_t *net); + +/*! @} */ diff --git a/src/utils/common/params.c b/src/utils/common/params.c new file mode 100644 index 0000000..e374314 --- /dev/null +++ b/src/utils/common/params.c @@ -0,0 +1,347 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <arpa/inet.h> +#include <stdio.h> +#include <stdlib.h> +#include <netinet/in.h> +#include <sys/socket.h> + +#ifdef LIBIDN +#include LIBIDN_HEADER +#endif + +#include "utils/common/params.h" +#include "utils/common/msg.h" +#include "utils/common/resolv.h" +#include "utils/common/token.h" +#include "libknot/libknot.h" +#include "contrib/macros.h" +#include "contrib/mempattern.h" +#include "contrib/openbsd/strlcpy.h" +#include "contrib/strtonum.h" + +#define IPV4_REVERSE_DOMAIN "in-addr.arpa." +#define IPV6_REVERSE_DOMAIN "ip6.arpa." + +char *name_from_idn(const char *idn_name) { +#ifdef LIBIDN + char *name = NULL; + + int rc = idna_to_ascii_lz(idn_name, &name, 0); + if (rc != IDNA_SUCCESS) { + ERR("IDNA (%s)\n", idna_strerror(rc)); + return NULL; + } + + return name; +#endif + return strdup(idn_name); +} + +void name_to_idn(char **name) { +#ifdef LIBIDN + char *idn_name = NULL; + + int rc = idna_to_unicode_8zlz(*name, &idn_name, 0); + if (rc != IDNA_SUCCESS) { + return; + } + + free(*name); + *name = idn_name; +#endif + return; +} + +/*! + * \brief Checks if string is a prefix of reference string. + * + * \param pref Prefix string. + * \param pref_len Prefix length. + * \param str Reference string (must have trailing zero). + * + * \retval -1 \a pref is not a prefix of \a str. + * \retval 0<= number of chars after prefix \a pref in \a str. + */ +static int cmp_prefix(const char *pref, const size_t pref_len, + const char *str) +{ + size_t i = 0; + while (1) { + // Different characters => NOT prefix. + if (pref[i] != str[i]) { + return -1; + } + + i++; + + // Pref IS a prefix of pref. + if (i == pref_len) { + size_t rest = 0; + while (str[i + rest] != '\0') { + rest++; + } + return rest; + // Pref is longer then ref => NOT prefix. + } else if (str[i] == '\0') { + return -1; + } + } +} + +int best_param(const char *str, const size_t str_len, const param_t *tbl, + bool *unique) +{ + if (str == NULL || str_len == 0 || tbl == NULL) { + DBG_NULL; + return KNOT_EINVAL; + } + + int best_pos = -1; + int best_match = INT_MAX; + size_t matches = 0; + for (int i = 0; tbl[i].name != NULL; i++) { + int ret = cmp_prefix(str, str_len, tbl[i].name); + switch (ret) { + case -1: + continue; + case 0: + *unique = true; + return i; + default: + if (ret < best_match) { + best_pos = i; + best_match = ret; + } + matches++; + } + } + + switch (matches) { + case 0: + return KNOT_ENOTSUP; + case 1: + *unique = true; + return best_pos; + default: + *unique = false; + return best_pos; + } +} + +char *get_reverse_name(const char *name) +{ + struct in_addr addr4; + struct in6_addr addr6; + int ret; + char buf[128] = "\0"; + + if (name == NULL) { + DBG_NULL; + return NULL; + } + + // Check name for IPv4 address, IPv6 address or other. + if (inet_pton(AF_INET, name, &addr4) == 1) { + uint32_t num = ntohl(addr4.s_addr); + + // Create IPv4 reverse FQD name. + ret = snprintf(buf, sizeof(buf), "%u.%u.%u.%u.%s", + (num >> 0) & 0xFF, (num >> 8) & 0xFF, + (num >> 16) & 0xFF, (num >> 24) & 0xFF, + IPV4_REVERSE_DOMAIN); + if (ret < 0 || (size_t)ret >= sizeof(buf)) { + return NULL; + } + + return strdup(buf); + } else if (inet_pton(AF_INET6, name, &addr6) == 1) { + char *pos = buf; + size_t len = sizeof(buf); + uint8_t left, right; + + // Create IPv6 reverse name. + for (int i = 15; i >= 0; i--) { + left = ((addr6.s6_addr)[i] & 0xF0) >> 4; + right = (addr6.s6_addr)[i] & 0x0F; + + ret = snprintf(pos, len, "%x.%x.", right, left); + if (ret < 0 || (size_t)ret >= len) { + return NULL; + } + + pos += ret; + len -= ret; + } + + // Add IPv6 reverse domain. + ret = snprintf(pos, len, "%s", IPV6_REVERSE_DOMAIN); + if (ret < 0 || (size_t)ret >= len) { + return NULL; + } + + return strdup(buf); + } else { + return NULL; + } +} + +char *get_fqd_name(const char *name) +{ + char *fqd_name = NULL; + + if (name == NULL) { + DBG_NULL; + return NULL; + } + + size_t name_len = strlen(name); + + // If the name is FQDN, make a copy. + if (name[name_len - 1] == '.') { + fqd_name = strdup(name); + // Else make a copy and append a trailing dot. + } else { + size_t fqd_name_size = name_len + 2; + fqd_name = malloc(fqd_name_size); + if (fqd_name != NULL) { + strlcpy(fqd_name, name, fqd_name_size); + fqd_name[name_len] = '.'; + fqd_name[name_len + 1] = 0; + } + } + + return fqd_name; +} + +int params_parse_class(const char *value, uint16_t *rclass) +{ + if (value == NULL || rclass == NULL) { + DBG_NULL; + return KNOT_EINVAL; + } + + if (knot_rrclass_from_string(value, rclass) == 0) { + return KNOT_EOK; + } else { + return KNOT_EINVAL; + } +} + +int params_parse_type(const char *value, uint16_t *rtype, int64_t *serial, + bool *notify) +{ + if (value == NULL || rtype == NULL || serial == NULL) { + DBG_NULL; + return KNOT_EINVAL; + } + + // Find and parse type name. + size_t param_pos = strcspn(value, "="); + char *type_char = strndup(value, param_pos); + + if (knot_rrtype_from_string(type_char, rtype) != 0) { + size_t cmp_len = MAX(strlen("NOTIFY"), param_pos); + if (strncasecmp(type_char, "NOTIFY", cmp_len) == 0) { + *rtype = KNOT_RRTYPE_SOA; + *notify = true; + } else { + free(type_char); + return KNOT_EINVAL; + } + } else { + *notify = false; + } + + free(type_char); + + // Parse additional parameter. + if (param_pos == strlen(value)) { + // IXFR requires serial parameter. + if (*rtype == KNOT_RRTYPE_IXFR) { + DBG("SOA serial is required for IXFR query\n"); + return KNOT_EINVAL; + } else { + *serial = -1; + } + } else { + // Additional parameter is accepted for IXFR or NOTIFY. + if (*rtype == KNOT_RRTYPE_IXFR || *notify) { + const char *param_str = value + 1 + param_pos; + char *end; + + // Convert string to serial. + unsigned long long num = strtoull(param_str, &end, 10); + + // Check for bad serial string. + if (end == param_str || *end != '\0' || num > UINT32_MAX) { + DBG("bad SOA serial '%s'\n", param_str); + return KNOT_EINVAL; + } + + *serial = num; + } else { + DBG("unsupported parameter '%s'\n", value); + return KNOT_EINVAL; + } + } + + return KNOT_EOK; +} + +int params_parse_server(const char *value, list_t *servers, const char *def_port) +{ + if (value == NULL || servers == NULL) { + DBG_NULL; + return KNOT_EINVAL; + } + + // Add specified nameserver. + srv_info_t *server = parse_nameserver(value, def_port); + if (server == NULL) { + return KNOT_EINVAL; + } + add_tail(servers, (node_t *)server); + + return KNOT_EOK; +} + +int params_parse_wait(const char *value, int32_t *dst) +{ + if (value == NULL || dst == NULL) { + DBG_NULL; + return KNOT_EINVAL; + } + + uint32_t num; + int ret = str_to_u32(value, &num); + if (ret != KNOT_EOK) { + return ret; + } + + // Check for minimal value. + if (num < 1) { + num = 1; + // Reduce maximal value. Poll takes signed int in milliseconds. + } else if (num > INT32_MAX / 1000) { + num = INT32_MAX / 1000; + } + + *dst = num; + + return KNOT_EOK; +} diff --git a/src/utils/common/params.h b/src/utils/common/params.h new file mode 100644 index 0000000..4059446 --- /dev/null +++ b/src/utils/common/params.h @@ -0,0 +1,172 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \file + * + * \brief Common utils parameters processing. + * + * \addtogroup knot_utils + * @{ + */ + +#pragma once + +#include <limits.h> +#include <stdint.h> +#include <stdbool.h> +#include <stdio.h> + +#include "libknot/libknot.h" +#include "contrib/ucw/lists.h" + +#define DEFAULT_IPV4_NAME "127.0.0.1" +#define DEFAULT_IPV6_NAME "::1" +#define DEFAULT_DNS_PORT "53" +#define DEFAULT_DNS_TLS_PORT "853" +#define DEFAULT_UDP_SIZE 512 +#define DEFAULT_EDNS_SIZE 4096 +#define MAX_PACKET_SIZE 65535 + +#define SEP_CHARS "\n\t " + +/*! \brief Variants of IP protocol. */ +typedef enum { + IP_ALL, + IP_4, + IP_6 +} ip_t; + +/*! \brief Variants of transport protocol. */ +typedef enum { + PROTO_ALL, + PROTO_TCP, + PROTO_UDP +} protocol_t; + +/*! \brief Variants of output type. */ +typedef enum { + /*!< Verbose output (same for host and dig). */ + FORMAT_FULL, + /*!< Short dig output. */ + FORMAT_DIG, + /*!< Brief host output. */ + FORMAT_HOST, + /*!< Brief nsupdate output. */ + FORMAT_NSUPDATE +} format_t; + +/*! \brief Text output settings. */ +typedef struct { + /*!< Output format. */ + format_t format; + + /*!< Style of rrset dump. */ + knot_dump_style_t style; + + /*!< Show query packet. */ + bool show_query; + /*!< Show header info. */ + bool show_header; + /*!< Show section name. */ + bool show_section; + /*!< Show EDNS pseudosection. */ + bool show_edns; + /*!< Show QUERY/ZONE section. */ + bool show_question; + /*!< Show ANSWER/PREREQ section. */ + bool show_answer; + /*!< Show UPDATE/AUTHORITY section. */ + bool show_authority; + /*!< Show ADDITIONAL section. */ + bool show_additional; + /*!< Show TSIG pseudosection. */ + bool show_tsig; + /*!< Show footer info. */ + bool show_footer; + + /*!< KHOST - Hide CNAME record in answer (duplicity reduction). */ + bool hide_cname; +} style_t; + +/*! \brief Parameter handler. */ +typedef int (*param_handle_f)(const char *arg, void *params); + +/*! \brief Parameter argument type. */ +typedef enum { + ARG_NONE, + ARG_REQUIRED, + ARG_OPTIONAL +} arg_t; + +/*! \brief Parameter specification. */ +typedef struct { + const char *name; + arg_t arg; + param_handle_f handler; +} param_t; + +inline static void print_version(const char *program_name) +{ + printf("%s (Knot DNS), version %s\n", program_name, PACKAGE_VERSION); +} + +/*! + * \brief Transforms localized IDN string to ASCII punycode. + * + * \param idn_name IDN name to transform. + * + * \retval NULL if transformation fails. + * \retval string if ok. + */ +char *name_from_idn(const char *idn_name); + +/*! + * \brief Transforms ASCII punycode to localized IDN string. + * + * If an error occurs or IDN support is missing, this function does nothing. + * + * \param name ASCII name to transform and replace with IDN name. + */ +void name_to_idn(char **name); + +/*! + * \brief Find the best parameter match in table based on prefix equality. + * + * \param str Parameter name to look up. + * \param str_len Parameter name length. + * \param tbl Parameter table. + * \param unique Indication if output is unique result. + * + * \retval >=0 looked up parameter position in \a tbl. + * \retval err if error. + */ +int best_param(const char *str, const size_t str_len, const param_t *tbl, + bool *unique); + +char *get_reverse_name(const char *name); + +char *get_fqd_name(const char *name); + +int params_parse_class(const char *value, uint16_t *rclass); + +int params_parse_type(const char *value, uint16_t *rtype, int64_t *serial, + bool *notify); + +int params_parse_server(const char *value, list_t *servers, const char *def_port); + +int params_parse_wait(const char *value, int32_t *dst); + +/*! @} */ diff --git a/src/utils/common/resolv.c b/src/utils/common/resolv.c new file mode 100644 index 0000000..b5f84c5 --- /dev/null +++ b/src/utils/common/resolv.c @@ -0,0 +1,208 @@ +/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "utils/common/resolv.h" +#include "utils/common/msg.h" +#include "utils/common/params.h" +#include "libknot/libknot.h" +#include "contrib/ucw/lists.h" + +#define RESOLV_FILE "/etc/resolv.conf" + +srv_info_t* parse_nameserver(const char *str, const char *def_port) +{ + char *host = NULL, *port = NULL; + const char *addr = NULL, *sep = NULL; + size_t addr_len = 0; + char separator = ':'; + + if (str == NULL || def_port == NULL) { + DBG_NULL; + return NULL; + } + + const size_t str_len = strlen(str); + const char *str_end = str + str_len; + + // [address]:port notation. + if (*str == '[') { + addr = str + 1; + const char *addr_end = strchr(addr, ']'); + // Missing closing bracket -> stop processing. + if (addr_end == NULL) { + return NULL; + } + addr_len = addr_end - addr; + str += 1 + addr_len + 1; + // Address@port notation. + } else if ((sep = strchr(str, '@')) != NULL) { + addr = str; + addr_len = sep - addr; + str += addr_len; + separator = '@'; + // Address#port notation. + } else if ((sep = strchr(str, '#')) != NULL) { + addr = str; + addr_len = sep - addr; + str += addr_len; + separator = '#'; + // IPv4:port notation. + } else if ((sep = strchr(str, ':')) != NULL) { + addr = str; + // Not IPv4 address -> no port. + if (strchr(sep + 1, ':') != NULL) { + addr_len = str_len; + str = str_end; + } else { + addr_len = sep - addr; + str += addr_len; + } + // No port specified. + } else { + addr = str; + addr_len = str_len; + str = str_end; + } + + // Process port. + if (str < str_end) { + // Check port separator. + if (*str != separator) { + return NULL; + } + str++; + + // Check for missing port. + if (str >= str_end) { + return NULL; + } + + port = strdup(str); + } else { + port = strdup(def_port); + } + + host = strndup(addr, addr_len); + + // Create server structure. + srv_info_t *server = srv_info_create(host, port); + + free(host); + free(port); + + return server; +} + +static size_t get_resolv_nameservers(list_t *servers, const char *def_port) +{ + char line[512]; + + // Open config file. + FILE *f = fopen(RESOLV_FILE, "r"); + if (f == NULL) { + return 0; + } + + // Read lines from config file. + while (fgets(line, sizeof(line), f) != NULL) { + size_t len; + char *pos = line; + char *option, *value; + + // Find leading white characters. + len = strspn(pos, SEP_CHARS); + pos += len; + + // Start of the first token. + option = pos; + + // Find length of the token. + len = strcspn(pos, SEP_CHARS); + pos += len; + + // Check if the token is not empty. + if (len == 0) { + continue; + } + + // Find separating white characters. + len = strspn(pos, SEP_CHARS); + pos += len; + + // Check if there is a separation between tokens. + if (len == 0) { + continue; + } + + // Copy of the second token. + value = strndup(pos, strcspn(pos, SEP_CHARS)); + + // Process value with respect to option name. + if (strncmp(option, "nameserver", strlen("nameserver")) == 0) { + srv_info_t *server; + + server = parse_nameserver(value, def_port); + + // If value is correct, add nameserver to the list. + if (server != NULL) { + add_tail(servers, (node_t *)server); + } + } + + // Drop value string. + free(value); + } + + // Close config file. + fclose(f); + + // Return number of servers. + return list_size(servers); +} + +void get_nameservers(list_t *servers, const char *def_port) +{ + if (servers == NULL || def_port == NULL) { + DBG_NULL; + return; + } + + // Initialize list of servers. + init_list(servers); + + // Read nameservers from resolv file or use the default ones. + if (get_resolv_nameservers(servers, def_port) == 0) { + srv_info_t *server; + + // Add default ipv6 nameservers. + server = srv_info_create(DEFAULT_IPV6_NAME, def_port); + + if (server != NULL) { + add_tail(servers, (node_t *)server); + } + + // Add default ipv4 nameservers. + server = srv_info_create(DEFAULT_IPV4_NAME, def_port); + + if (server != NULL) { + add_tail(servers, (node_t *)server); + } + } +} diff --git a/src/utils/common/resolv.h b/src/utils/common/resolv.h new file mode 100644 index 0000000..bcf1a0f --- /dev/null +++ b/src/utils/common/resolv.h @@ -0,0 +1,36 @@ +/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \file resolv.h + * + * \author Daniel Salzman <daniel.salzman@nic.cz> + * + * \brief resolv.conf processing. + * + * \addtogroup knot_utils + * @{ + */ + +#pragma once + +#include "utils/common/netio.h" +#include "contrib/ucw/lists.h" + +srv_info_t* parse_nameserver(const char *str, const char *def_port); + +void get_nameservers(list_t *servers, const char *def_port); + +/*! @} */ diff --git a/src/utils/common/sign.c b/src/utils/common/sign.c new file mode 100644 index 0000000..f8e9f29 --- /dev/null +++ b/src/utils/common/sign.c @@ -0,0 +1,109 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <string.h> + +#include "utils/common/sign.h" +#include "libknot/errcode.h" +#include "libknot/tsig-op.h" + +int sign_context_init_tsig(sign_context_t *ctx, const knot_tsig_key_t *key) +{ + if (!ctx || !key) { + return KNOT_EINVAL; + } + + size_t digest_size = dnssec_tsig_algorithm_size(key->algorithm); + if (digest_size == 0) { + return KNOT_EINVAL; + } + + uint8_t *digest = calloc(1, digest_size); + if (!digest) { + return KNOT_ENOMEM; + } + + ctx->digest_size = digest_size; + ctx->digest = digest; + ctx->tsig_key = key; + + return KNOT_EOK; +} + +void sign_context_deinit(sign_context_t *ctx) +{ + if (!ctx) { + return; + } + + free(ctx->digest); + + memset(ctx, 0, sizeof(*ctx)); +} + +int sign_packet(knot_pkt_t *pkt, sign_context_t *sign_ctx) +{ + if (pkt == NULL || sign_ctx == NULL || sign_ctx->digest == NULL) { + return KNOT_EINVAL; + } + + uint8_t *wire = pkt->wire; + size_t *wire_size = &pkt->size; + size_t max_size = pkt->max_size; + + int ret = knot_pkt_reserve(pkt, knot_tsig_wire_size(sign_ctx->tsig_key)); + if (ret != KNOT_EOK) { + return ret; + } + + return knot_tsig_sign(wire, wire_size, max_size, NULL, 0, + sign_ctx->digest, &sign_ctx->digest_size, + sign_ctx->tsig_key, 0, 0); +} + +int verify_packet(const knot_pkt_t *pkt, const sign_context_t *sign_ctx) +{ + if (pkt == NULL || sign_ctx == NULL || sign_ctx->digest == NULL) { + return KNOT_EINVAL; + } + + const uint8_t *wire = pkt->wire; + const size_t *wire_size = &pkt->size; + + if (pkt->tsig_rr == NULL) { + return KNOT_ENOTSIG; + } + + int ret = knot_tsig_client_check(pkt->tsig_rr, wire, *wire_size, + sign_ctx->digest, sign_ctx->digest_size, + sign_ctx->tsig_key, 0); + if (ret != KNOT_EOK) { + return ret; + } + + switch (knot_tsig_rdata_error(pkt->tsig_rr)) { + case KNOT_RCODE_BADSIG: + return KNOT_TSIG_EBADSIG; + case KNOT_RCODE_BADKEY: + return KNOT_TSIG_EBADKEY; + case KNOT_RCODE_BADTIME: + return KNOT_TSIG_EBADTIME; + case KNOT_RCODE_BADTRUNC: + return KNOT_TSIG_EBADTRUNC; + default: + return KNOT_EOK; + } +} diff --git a/src/utils/common/sign.h b/src/utils/common/sign.h new file mode 100644 index 0000000..d1912a0 --- /dev/null +++ b/src/utils/common/sign.h @@ -0,0 +1,63 @@ +/* Copyright (C) 2015 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include "libknot/packet/pkt.h" +#include "libknot/tsig.h" + +/*! + * \brief Holds data required between signing and signature verification. + */ +struct sign_context { + size_t digest_size; + uint8_t *digest; + const knot_tsig_key_t *tsig_key; +}; + +typedef struct sign_context sign_context_t; + +/*! + * \brief Initialize signing context for TSIG. + */ +int sign_context_init_tsig(sign_context_t *ctx, const knot_tsig_key_t *key); + +/*! + * \brief Clean up signing context. + * + * \param ctx Sign context. + */ +void sign_context_deinit(sign_context_t *ctx); + +/*! + * \brief Signs outgoing DNS packet. + * + * \param pkt Packet to sign. + * \param sign_ctx Signing context. + * + * \return Error code, KNOT_EOK if successful. + */ +int sign_packet(knot_pkt_t *pkt, sign_context_t *sign_ctx); + +/*! + * \brief Verifies signature for incoming DNS packet. + * + * \param pkt Packet verify sign. + * \param sign_ctx Signing context. + * + * \return Error code, KNOT_EOK if successful. + */ +int verify_packet(const knot_pkt_t *pkt, const sign_context_t *sign_ctx); diff --git a/src/utils/common/tls.c b/src/utils/common/tls.c new file mode 100644 index 0000000..91b747f --- /dev/null +++ b/src/utils/common/tls.c @@ -0,0 +1,495 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <arpa/inet.h> +#include <stdbool.h> +#include <string.h> +#include <gnutls/gnutls.h> +#include <gnutls/x509.h> +#include <poll.h> + +#include "utils/common/tls.h" +#include "utils/common/cert.h" +#include "utils/common/msg.h" +#include "contrib/base64.h" +#include "libknot/errcode.h" + +void tls_params_init(tls_params_t *params) +{ + if (params == NULL) { + return; + } + + memset(params, 0, sizeof(*params)); + + init_list(¶ms->ca_files); + init_list(¶ms->pins); +} + +int tls_params_copy(tls_params_t *dst, const tls_params_t *src) +{ + if (dst == NULL || src == NULL) { + return KNOT_EINVAL; + } + + tls_params_init(dst); + + dst->enable = src->enable; + dst->system_ca = src->system_ca; + if (src->hostname != NULL) { + dst->hostname = strdup(src->hostname); + if (dst->hostname == NULL) { + tls_params_clean(dst); + return KNOT_ENOMEM; + } + } + + if (src->sni != NULL) { + dst->sni = strdup(src->sni); + if (dst->sni == NULL) { + tls_params_clean(dst); + return KNOT_ENOMEM; + } + } + + ptrnode_t *n = NULL; + WALK_LIST(n, src->ca_files) { + char *src_file = (char *)n->d; + char *file = strdup(src_file); + if (file == NULL || ptrlist_add(&dst->ca_files, file, NULL) == NULL) { + tls_params_clean(dst); + return KNOT_ENOMEM; + } + } + WALK_LIST(n, src->pins) { + uint8_t *src_pin = (uint8_t *)n->d; + uint8_t *pin = malloc(1 + src_pin[0]); + if (pin == NULL || ptrlist_add(&dst->pins, pin, NULL) == NULL) { + tls_params_clean(dst); + return KNOT_ENOMEM; + } + memcpy(pin, src_pin, 1 + src_pin[0]); + } + + return KNOT_EOK; +} + +void tls_params_clean(tls_params_t *params) +{ + if (params == NULL) { + return; + } + + ptrnode_t *node = NULL, *nxt = NULL; + WALK_LIST_DELSAFE(node, nxt, params->ca_files) { + free(node->d); + } + ptrlist_free(¶ms->ca_files, NULL); + + WALK_LIST_DELSAFE(node, nxt, params->pins) { + free(node->d); + } + ptrlist_free(¶ms->pins, NULL); + + free(params->hostname); + free(params->sni); + + memset(params, 0, sizeof(*params)); +} + +static bool check_pin(const uint8_t *cert_pin, size_t cert_pin_len, const list_t *pins) +{ + if (EMPTY_LIST(*pins)) { + return false; + } + + ptrnode_t *n = NULL; + WALK_LIST(n, *pins) { + uint8_t *pin = (uint8_t *)n->d; + if (pin[0] == cert_pin_len && + memcmp(cert_pin, &pin[1], cert_pin_len) == 0) { + return true; + } + } + + return false; +} + +static int check_certificates(gnutls_session_t session, const list_t *pins) +{ + if (gnutls_certificate_type_get(session) != GNUTLS_CRT_X509) { + DBG("TLS, invalid certificate type\n"); + return GNUTLS_E_CERTIFICATE_ERROR; + } + + unsigned cert_list_size; + const gnutls_datum_t *cert_list = + gnutls_certificate_get_peers(session, &cert_list_size); + if (cert_list == NULL || cert_list_size == 0) { + DBG("TLS, empty certificate list\n"); + return GNUTLS_E_CERTIFICATE_ERROR; + } + + size_t matches = 0; + + DBG("TLS, received certificate hierarchy:\n"); + for (int i = 0; i < cert_list_size; i++) { + gnutls_x509_crt_t cert; + int ret = gnutls_x509_crt_init(&cert); + if (ret != GNUTLS_E_SUCCESS) { + return ret; + } + + ret = gnutls_x509_crt_import(cert, &cert_list[i], GNUTLS_X509_FMT_DER); + if (ret != GNUTLS_E_SUCCESS) { + gnutls_x509_crt_deinit(cert); + return ret; + } + + gnutls_datum_t cert_name = { 0 }; + ret = gnutls_x509_crt_get_dn2(cert, &cert_name); + if (ret != GNUTLS_E_SUCCESS) { + gnutls_x509_crt_deinit(cert); + return ret; + } + DBG(" #%i, %s\n", i + 1, cert_name.data); + gnutls_free(cert_name.data); + + uint8_t cert_pin[CERT_PIN_LEN] = { 0 }; + ret = cert_get_pin(cert, cert_pin, sizeof(cert_pin)); + if (ret != KNOT_EOK) { + gnutls_x509_crt_deinit(cert); + return GNUTLS_E_CERTIFICATE_ERROR; + } + + // Check if correspond to a specified PIN. + bool match = check_pin(cert_pin, sizeof(cert_pin), pins); + if (match) { + matches++; + } + + uint8_t *txt_pin; + ret = base64_encode_alloc(cert_pin, sizeof(cert_pin), &txt_pin); + if (ret < 0) { + gnutls_x509_crt_deinit(cert); + return ret; + } + DBG(" SHA-256 PIN: %.*s%s\n", ret, txt_pin, match ? ", MATCH" : ""); + free(txt_pin); + + gnutls_x509_crt_deinit(cert); + } + + if (matches > 0) { + return GNUTLS_E_SUCCESS; + } else if (EMPTY_LIST(*pins)) { + DBG("TLS, skipping certificate PIN check\n"); + return GNUTLS_E_SUCCESS; + } else { + DBG("TLS, no certificate PIN match\n"); + return GNUTLS_E_CERTIFICATE_ERROR; + } +} + +static bool do_verification(const tls_params_t *params) +{ + return params->hostname != NULL || params->system_ca || + !EMPTY_LIST(params->ca_files); +} + +static int verify_certificate(gnutls_session_t session) +{ + tls_ctx_t *ctx = gnutls_session_get_ptr(session); + + // Check for pinned certificates and print certificate hierarchy. + int ret = check_certificates(session, &ctx->params->pins); + if (ret != GNUTLS_E_SUCCESS) { + return ret; + } + + if (!do_verification(ctx->params)) { + DBG("TLS, skipping certificate verification\n"); + return GNUTLS_E_SUCCESS; + } + + // Set server certificate check. + gnutls_typed_vdata_st data[2] = { + { .type = GNUTLS_DT_KEY_PURPOSE_OID, + .data = (void *)GNUTLS_KP_TLS_WWW_SERVER }, + { .type = GNUTLS_DT_DNS_HOSTNAME, + .data = (void *)ctx->params->hostname } + }; + size_t data_count = (ctx->params->hostname != NULL) ? 2 : 1; + if (data_count == 1) { + WARN("TLS, no hostname provided, will not verify certificate owner\n") + } + + unsigned int status; + ret = gnutls_certificate_verify_peers(session, data, data_count, &status); + if (ret != GNUTLS_E_SUCCESS) { + WARN("TLS, failed to verify peer certificate\n"); + return GNUTLS_E_CERTIFICATE_ERROR; + } + + gnutls_datum_t msg; + ret = gnutls_certificate_verification_status_print( + status, gnutls_certificate_type_get(session), &msg, 0); + if (ret == GNUTLS_E_SUCCESS) { + DBG("TLS, %s\n", msg.data); + } + gnutls_free(msg.data); + + return (status == 0) ? GNUTLS_E_SUCCESS : GNUTLS_E_CERTIFICATE_ERROR; +} + +int tls_ctx_init(tls_ctx_t *ctx, const tls_params_t *params, int wait) +{ + if (ctx == NULL || params == NULL || !params->enable) { + return KNOT_EINVAL; + } + + memset(ctx, 0, sizeof(*ctx)); + ctx->params = params; + ctx->wait = wait; + ctx->sockfd = -1; + + int ret = gnutls_certificate_allocate_credentials(&ctx->credentials); + if (ret != GNUTLS_E_SUCCESS) { + return KNOT_ENOMEM; + } + + // Import system certificates. + if (ctx->params->system_ca || + (ctx->params->hostname != NULL && EMPTY_LIST(ctx->params->ca_files))) { + ret = gnutls_certificate_set_x509_system_trust(ctx->credentials); + if (ret < 0) { + WARN("TLS, failed to import system certificates (%s)\n", + gnutls_strerror_name(ret)); + return KNOT_ERROR; + } else { + DBG("TLS, imported %i system certificates\n", ret); + } + } + + // Import provided certificate files. + ptrnode_t *n = NULL; + WALK_LIST(n, ctx->params->ca_files) { + const char *file = (char *)n->d; + ret = gnutls_certificate_set_x509_trust_file(ctx->credentials, file, + GNUTLS_X509_FMT_PEM); + if (ret < 0) { + WARN("TLS, failed to import certificate file '%s' (%s)\n", + file, gnutls_strerror_name(ret)); + return KNOT_ERROR; + } else { + DBG("TLS, imported %i certificates from '%s'\n", ret, file); + } + } + + gnutls_certificate_set_verify_function(ctx->credentials, verify_certificate); + + return KNOT_EOK; +} + +int tls_ctx_connect(tls_ctx_t *ctx, int sockfd, const char *remote) +{ + if (ctx == NULL) { + return KNOT_EINVAL; + } + + int ret = gnutls_init(&ctx->session, GNUTLS_CLIENT | GNUTLS_NONBLOCK); + if (ret != GNUTLS_E_SUCCESS) { + return KNOT_NET_ECONNECT; + } + + ret = gnutls_set_default_priority(ctx->session); + if (ret != GNUTLS_E_SUCCESS) { + return KNOT_NET_ECONNECT; + } + + ret = gnutls_credentials_set(ctx->session, GNUTLS_CRD_CERTIFICATE, + ctx->credentials); + if (ret != GNUTLS_E_SUCCESS) { + return KNOT_NET_ECONNECT; + } + + if (remote != NULL) { + ret = gnutls_server_name_set(ctx->session, GNUTLS_NAME_DNS, remote, + strlen(remote)); + if (ret != GNUTLS_E_SUCCESS) { + return KNOT_NET_ECONNECT; + } + } + + gnutls_session_set_ptr(ctx->session, ctx); + gnutls_transport_set_int(ctx->session, sockfd); + gnutls_handshake_set_timeout(ctx->session, 1000 * ctx->wait); + + // Initialize poll descriptor structure. + struct pollfd pfd = { + .fd = sockfd, + .events = POLLIN, + .revents = 0, + }; + + // Perform the TLS handshake + do { + ret = gnutls_handshake(ctx->session); + if (ret != GNUTLS_E_SUCCESS && gnutls_error_is_fatal(ret) == 0) { + if (poll(&pfd, 1, 1000 * ctx->wait) != 1) { + WARN("TLS, peer took too long to respond\n"); + return KNOT_NET_ETIMEOUT; + } + } + } while (ret != GNUTLS_E_SUCCESS && gnutls_error_is_fatal(ret) == 0); + if (ret != GNUTLS_E_SUCCESS) { + WARN("TLS, handshake failed (%s)\n", gnutls_strerror(ret)); + tls_ctx_close(ctx); + return KNOT_NET_ESOCKET; + } + + // Save the socket descriptor. + ctx->sockfd = sockfd; + + return KNOT_EOK; +} + +int tls_ctx_send(tls_ctx_t *ctx, const uint8_t *buf, const size_t buf_len) +{ + if (ctx == NULL || buf == NULL) { + return KNOT_EINVAL; + } + + uint16_t msg_len = htons(buf_len); + + gnutls_record_cork(ctx->session); + + if (gnutls_record_send(ctx->session, &msg_len, sizeof(msg_len)) <= 0) { + WARN("TLS, failed to send\n"); + return KNOT_NET_ESEND; + } + if (gnutls_record_send(ctx->session, buf, buf_len) <= 0) { + WARN("TLS, failed to send\n"); + return KNOT_NET_ESEND; + } + + while (gnutls_record_check_corked(ctx->session) > 0) { + int ret = gnutls_record_uncork(ctx->session, 0); + if (ret < 0 && gnutls_error_is_fatal(ret) != 0) { + WARN("TLS, failed to send (%s)\n", gnutls_strerror(ret)); + return KNOT_NET_ESEND; + } + } + + return KNOT_EOK; +} + +int tls_ctx_receive(tls_ctx_t *ctx, uint8_t *buf, const size_t buf_len) +{ + if (ctx == NULL || buf == NULL) { + return KNOT_EINVAL; + } + + // Initialize poll descriptor structure. + struct pollfd pfd = { + .fd = ctx->sockfd, + .events = POLLIN, + .revents = 0, + }; + + uint32_t total = 0; + uint16_t msg_len = 0; + + // Receive message header. + while (total < sizeof(msg_len)) { + ssize_t ret = gnutls_record_recv(ctx->session, &msg_len + total, + sizeof(msg_len) - total); + if (ret > 0) { + total += ret; + } else if (ret == 0) { + WARN("TLS, peer has closed the connection\n"); + return KNOT_NET_ERECV; + } else if (gnutls_error_is_fatal(ret) != 0) { + WARN("TLS, failed to receive reply (%s)\n", + gnutls_strerror(ret)); + return KNOT_NET_ERECV; + } else if (poll(&pfd, 1, 1000 * ctx->wait) != 1) { + WARN("TLS, peer took too long to respond\n"); + return KNOT_NET_ETIMEOUT; + } + } + + // Convert number to host format. + msg_len = ntohs(msg_len); + if (msg_len > buf_len) { + return KNOT_ESPACE; + } + + total = 0; + + // Receive data over TLS + while (total < msg_len) { + ssize_t ret = gnutls_record_recv(ctx->session, buf + total, + msg_len - total); + if (ret > 0) { + total += ret; + } else if (ret == 0) { + WARN("TLS, peer has closed the connection\n"); + return KNOT_NET_ERECV; + } else if (gnutls_error_is_fatal(ret) != 0) { + WARN("TLS, failed to receive reply (%s)\n", + gnutls_strerror(ret)); + return KNOT_NET_ERECV; + } else if (poll(&pfd, 1, 1000 * ctx->wait) != 1) { + WARN("TLS, peer took too long to respond\n"); + return KNOT_NET_ETIMEOUT; + } + } + + return total; +} + +void tls_ctx_close(tls_ctx_t *ctx) +{ + if (ctx == NULL || ctx->session == NULL) { + return; + } + + gnutls_bye(ctx->session, GNUTLS_SHUT_RDWR); + gnutls_deinit(ctx->session); +} + +void tls_ctx_deinit(tls_ctx_t *ctx) +{ + if (ctx == NULL) { + return; + } + + if (ctx->credentials != NULL) { + gnutls_certificate_free_credentials(ctx->credentials); + } +} + +void print_tls(const tls_ctx_t *ctx) +{ + if (ctx == NULL || ctx->session == NULL) { + return; + } + + char *msg = gnutls_session_get_desc(ctx->session); + printf(";; TLS session %s\n", msg); + gnutls_free(msg); +} diff --git a/src/utils/common/tls.h b/src/utils/common/tls.h new file mode 100644 index 0000000..487a0de --- /dev/null +++ b/src/utils/common/tls.h @@ -0,0 +1,64 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <stdint.h> +#include <gnutls/gnutls.h> + +#include "contrib/ucw/lists.h" + +/*! \brief TLS parameters. */ +typedef struct { + /*! Use TLS indicator. */ + bool enable; + /*! Import system certificates indicator. */ + bool system_ca; + /*! Certificate files to import. */ + list_t ca_files; + /*! Pinned certificates. */ + list_t pins; + /*! Required server hostname. */ + char *hostname; + /*! Optional server name indicator. */ + char *sni; +} tls_params_t; + +/*! \brief TLS context. */ +typedef struct { + /*! TLS handshake timeout. */ + int wait; + /*! Socket descriptor. */ + int sockfd; + /*! TLS parameters. */ + const tls_params_t *params; + /*! GnuTLS session handle. */ + gnutls_session_t session; + /*! GnuTLS credentials handle. */ + gnutls_certificate_credentials_t credentials; +} tls_ctx_t; + +void tls_params_init(tls_params_t *params); +int tls_params_copy(tls_params_t *dst, const tls_params_t *src); +void tls_params_clean(tls_params_t *params); + +int tls_ctx_init(tls_ctx_t *ctx, const tls_params_t *params, int wait); +int tls_ctx_connect(tls_ctx_t *ctx, int sockfd, const char *remote); +int tls_ctx_send(tls_ctx_t *ctx, const uint8_t *buf, const size_t buf_len); +int tls_ctx_receive(tls_ctx_t *ctx, uint8_t *buf, const size_t buf_len); +void tls_ctx_close(tls_ctx_t *ctx); +void tls_ctx_deinit(tls_ctx_t *ctx); +void print_tls(const tls_ctx_t *ctx); diff --git a/src/utils/common/token.c b/src/utils/common/token.c new file mode 100644 index 0000000..0bba3c7 --- /dev/null +++ b/src/utils/common/token.c @@ -0,0 +1,115 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "utils/common/token.h" +#include "utils/common/msg.h" +#include "libknot/libknot.h" +#include "contrib/ctype.h" + +int tok_scan(const char* lp, const char **tbl, int *lpm) +{ + if (lp == NULL || tbl == NULL || *tbl == NULL || lpm == NULL) { + DBG_NULL; + return -1; + } + + const char *prefix = lp; /* Ptr to line start. */ + int i = 0, pl = 1; /* Match index, prefix length. */ + unsigned char len = 0; /* Read length. */ + for(;;) { + const char *tok = tbl[i]; + if (*lp == '\0' || is_space(*lp)) { + if (tok && TOK_L(tok) == len) { /* Consumed whole w? */ + return i; /* Identifier */ + } else { /* Word is shorter than cmd? */ + break; + } + } + + /* Find next prefix match. */ + ++len; + while (tok) { + if (TOK_L(tok) >= len) { /* Is prefix of current token */ + if (*lp < tok[pl]) { /* Terminate early. */ + tok = NULL; + break; /* No match could be found. */ + } + if (*lp == tok[pl]) { /* Match */ + if(lpm) *lpm = i; + ++pl; + break; + } + } + + /* No early cut, no match - seek next. */ + while ((tok = tbl[++i]) != NULL) { + if (TOK_L(tok) >= len && + memcmp(TOK_S(tok), prefix, len) == 0) { + break; + } + } + } + + if (tok == NULL) { + break; /* All tokens exhausted. */ + } else { + ++lp; /* Next char */ + } + } + + return -1; +} + +int tok_find(const char *lp, const char **tbl) +{ + if (lp == NULL || tbl == NULL || *tbl == NULL) { + DBG_NULL; + return KNOT_EINVAL; + } + + int lpm = -1; + int bp = 0; + if ((bp = tok_scan(lp, tbl, &lpm)) < 0) { + if (lpm > -1) { + ERR("unexpected literal: '%s', did you mean '%s' ?\n", + lp, TOK_S(tbl[lpm])); + } else { + ERR("unexpected literal: '%s'\n", lp); + } + + return KNOT_EPARSEFAIL; + } + + return bp; +} + +const char *tok_skipspace(const char *lp) +{ + if (lp == NULL) { + DBG_NULL; + return NULL; + } + + while (is_space(*lp)) { + lp += 1; + } + + return lp; +} diff --git a/src/utils/common/token.h b/src/utils/common/token.h new file mode 100644 index 0000000..b4d33d2 --- /dev/null +++ b/src/utils/common/token.h @@ -0,0 +1,77 @@ +/* Copyright (C) 2011 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +/*! + * \file token.h + * + * \author Marek Vavrusa <marek.vavrusa@nic.cz> + * + * \brief String tokenizer and simple scanner. + * + * \addtogroup knot_utils + * @{ + */ + +#pragma once + +#include <stdio.h> + +/*! + * \brief Example of token table: + * + * \warning Table _must_ be lexicographically ordered. + * + * const char *tok_tbl[] = { + * // LEN STRING + * "\x4" "abcd", + * "\x5" "class", + * NULL // END + * } + */ +/*! \brief String part of the token. */ +#define TOK_S(x) ((x)+1) +/*! \brief Len of the token. */ +#define TOK_L(x) ((unsigned char)(x)[0]) + +/*! + * \brief Scan for matching token described by a match table. + * + * Table consists of strings, prefixed with 1B length. + * + * \param lp Pointer to current line. + * \param tbl Match description table. + * \param lpm Pointer to longest prefix match. + * \retval index to matching record. + * \retval -1 if no match is found, lpm may be set to longest prefix match. + */ +int tok_scan(const char* lp, const char **tbl, int *lpm); + +/*! + * \brief Find token from table in a line buffer. + * \param lp Pointer to current line. + * \param tbl Match description table. + * \retval index to matching record. + * \retval error code if no match is found + */ +int tok_find(const char *lp, const char **tbl); + +/*! + * \brief Return pointer to next non-blank character. + * \param lp Pointer to current line. + * \return ptr to next non-blank character. + */ +const char *tok_skipspace(const char *lp); + +/*! @} */ diff --git a/src/utils/kdig/kdig_exec.c b/src/utils/kdig/kdig_exec.c new file mode 100644 index 0000000..782a2d5 --- /dev/null +++ b/src/utils/kdig/kdig_exec.c @@ -0,0 +1,1181 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stdlib.h> +#include <netinet/in.h> +#include <sys/socket.h> +#include <sys/time.h> + +#include "utils/kdig/kdig_exec.h" +#include "utils/common/exec.h" +#include "utils/common/msg.h" +#include "utils/common/netio.h" +#include "utils/common/sign.h" +#include "libknot/libknot.h" +#include "contrib/sockaddr.h" +#include "contrib/time.h" +#include "contrib/ucw/lists.h" + +#if USE_DNSTAP +# include "contrib/dnstap/convert.h" +# include "contrib/dnstap/message.h" +# include "contrib/dnstap/writer.h" + +static int write_dnstap(dt_writer_t *writer, + const bool is_query, + const uint8_t *wire, + const size_t wire_len, + net_t *net, + const struct timespec *mtime) +{ + Dnstap__Message msg; + Dnstap__Message__Type msg_type; + int ret; + int protocol = 0; + + if (writer == NULL) { + return KNOT_EOK; + } + + net_set_local_info(net); + + msg_type = is_query ? DNSTAP__MESSAGE__TYPE__TOOL_QUERY : + DNSTAP__MESSAGE__TYPE__TOOL_RESPONSE; + + if (net->socktype == SOCK_DGRAM) { + protocol = IPPROTO_UDP; + } else if (net->socktype == SOCK_STREAM) { + protocol = IPPROTO_TCP; + } + + ret = dt_message_fill(&msg, msg_type, net->local_info->ai_addr, + net->srv->ai_addr, protocol, + wire, wire_len, mtime); + if (ret != KNOT_EOK) { + return ret; + } + + return dt_writer_write(writer, (const ProtobufCMessage *)&msg); +} + +static float get_query_time(const Dnstap__Dnstap *frame) +{ + if (!frame->message->has_query_time_sec || + !frame->message->has_query_time_nsec || + !frame->message->has_response_time_sec || + !frame->message->has_response_time_sec) { + return 0; + } + + struct timespec from = { + .tv_sec = frame->message->query_time_sec, + .tv_nsec = frame->message->query_time_nsec + }; + + struct timespec to = { + .tv_sec = frame->message->response_time_sec, + .tv_nsec = frame->message->response_time_nsec + }; + + return time_diff_ms(&from, &to); +} + +static void fill_remote_addr(net_t *net, Dnstap__Message *message, bool is_initiator) +{ + if (!message->has_socket_family || !message->has_socket_protocol) { + return; + } + + if ((message->response_address.data == NULL && is_initiator) || + message->query_address.data == NULL) { + return; + } + + struct sockaddr_storage ss = { 0 }; + int family = dt_family_decode(message->socket_family); + int proto = dt_protocol_decode(message->socket_protocol); + int sock_type = 0; + + switch (proto) { + case IPPROTO_TCP: + sock_type = SOCK_STREAM; + break; + case IPPROTO_UDP: + sock_type = SOCK_DGRAM; + break; + default: + break; + } + + ProtobufCBinaryData *addr = NULL; + uint32_t port = 0; + if (is_initiator) { + addr = &message->response_address; + port = message->response_port; + } else { + addr = &message->query_address; + port = message->query_port; + } + + sockaddr_set_raw(&ss, family, addr->data, addr->len); + sockaddr_port_set((struct sockaddr *)&ss, port); + + get_addr_str(&ss, sock_type, &net->remote_str); +} + +static int process_dnstap(const query_t *query) +{ + dt_reader_t *reader = query->dt_reader; + + if (query->dt_reader == NULL) { + return -1; + } + + bool first_message = true; + + for (;;) { + Dnstap__Dnstap *frame = NULL; + Dnstap__Message *message = NULL; + ProtobufCBinaryData *wire = NULL; + bool is_query; + bool is_initiator; + + // Read next message. + int ret = dt_reader_read(reader, &frame); + if (ret == KNOT_EOF) { + break; + } else if (ret != KNOT_EOK) { + ERR("can't read dnstap message\n"); + break; + } + + // Check for dnstap message. + if (frame->type == DNSTAP__DNSTAP__TYPE__MESSAGE) { + message = frame->message; + } else { + WARN("ignoring non-dnstap message\n"); + dt_reader_free_frame(reader, &frame); + continue; + } + + // Check for the type of dnstap message. + if (message->has_response_message) { + wire = &message->response_message; + is_query = false; + } else if (message->has_query_message) { + wire = &message->query_message; + is_query = true; + } else { + WARN("dnstap frame contains no message\n"); + dt_reader_free_frame(reader, &frame); + continue; + } + + // Ignore query message if requested. + if (is_query && !query->style.show_query) { + dt_reader_free_frame(reader, &frame); + continue; + } + + // Get the message role. + is_initiator = dt_message_role_is_initiator(message->type); + + // Create dns packet based on dnstap wire data. + knot_pkt_t *pkt = knot_pkt_new(wire->data, wire->len, NULL); + if (pkt == NULL) { + ERR("can't allocate packet\n"); + dt_reader_free_frame(reader, &frame); + break; + } + + // Parse packet and reconstruct required data. + if (knot_pkt_parse(pkt, 0) == KNOT_EOK) { + time_t timestamp = 0; + float query_time = 0.0; + net_t net_ctx = { 0 }; + + if (is_query) { + if (message->has_query_time_sec) { + timestamp = message->query_time_sec; + } + } else { + if (message->has_response_time_sec) { + timestamp = message->response_time_sec; + } + query_time = get_query_time(frame); + } + + // Prepare connection information string. + fill_remote_addr(&net_ctx, message, is_initiator); + + if (first_message) { + first_message = false; + } else { + printf("\n"); + } + + print_packet(pkt, &net_ctx, pkt->size, query_time, timestamp, + is_query ^ is_initiator, &query->style); + + net_clean(&net_ctx); + } else { + ERR("can't print dnstap message\n"); + } + + knot_pkt_free(pkt); + dt_reader_free_frame(reader, &frame); + } + + return 0; +} +#endif // USE_DNSTAP + +static int add_query_edns(knot_pkt_t *packet, const query_t *query, uint16_t max_size) +{ + /* Initialize OPT RR. */ + knot_rrset_t opt_rr; + int ret = knot_edns_init(&opt_rr, max_size, 0, + query->edns > -1 ? query->edns : 0, &packet->mm); + if (ret != KNOT_EOK) { + return ret; + } + + if (query->flags.do_flag) { + knot_edns_set_do(&opt_rr); + } + + /* Append NSID. */ + if (query->nsid) { + ret = knot_edns_add_option(&opt_rr, KNOT_EDNS_OPTION_NSID, + 0, NULL, &packet->mm); + if (ret != KNOT_EOK) { + knot_rrset_clear(&opt_rr, &packet->mm); + return ret; + } + } + + /* Append EDNS-client-subnet. */ + if (query->subnet.family != AF_UNSPEC) { + uint16_t size = knot_edns_client_subnet_size(&query->subnet); + uint8_t data[size]; + + ret = knot_edns_client_subnet_write(data, size, &query->subnet); + if (ret != KNOT_EOK) { + knot_rrset_clear(&opt_rr, &packet->mm); + return ret; + } + + ret = knot_edns_add_option(&opt_rr, KNOT_EDNS_OPTION_CLIENT_SUBNET, + size, data, &packet->mm); + if (ret != KNOT_EOK) { + knot_rrset_clear(&opt_rr, &packet->mm); + return ret; + } + } + + /* Append a cookie option if present. */ + if (query->cc.len > 0) { + uint16_t size = knot_edns_cookie_size(&query->cc, &query->sc); + uint8_t data[size]; + + ret = knot_edns_cookie_write(data, size, &query->cc, &query->sc); + if (ret != KNOT_EOK) { + knot_rrset_clear(&opt_rr, &packet->mm); + return ret; + } + + ret = knot_edns_add_option(&opt_rr, KNOT_EDNS_OPTION_COOKIE, + size, data, &packet->mm); + if (ret != KNOT_EOK) { + knot_rrset_clear(&opt_rr, &packet->mm); + return ret; + } + } + + /* Append EDNS Padding. */ + int padding = query->padding; + if (padding != -3 && query->alignment > 0) { + padding = knot_edns_alignment_size(packet->size, + knot_rrset_size(&opt_rr), + query->alignment); + } else if (query->padding == -2 || (query->padding == -1 && query->tls.enable)) { + padding = knot_pkt_default_padding_size(packet, &opt_rr); + } + if (padding > -1) { + uint8_t zeros[padding]; + memset(zeros, 0, sizeof(zeros)); + + ret = knot_edns_add_option(&opt_rr, KNOT_EDNS_OPTION_PADDING, + padding, zeros, &packet->mm); + if (ret != KNOT_EOK) { + knot_rrset_clear(&opt_rr, &packet->mm); + return ret; + } + } + + /* Append custom EDNS options. */ + node_t *node = NULL; + WALK_LIST(node, query->edns_opts) { + ednsopt_t *opt = (ednsopt_t *)node; + ret = knot_edns_add_option(&opt_rr, opt->code, opt->length, + opt->data, &packet->mm); + if (ret != KNOT_EOK) { + knot_rrset_clear(&opt_rr, &packet->mm); + return ret; + } + } + + /* Add prepared OPT to packet. */ + ret = knot_pkt_put(packet, KNOT_COMPR_HINT_NONE, &opt_rr, KNOT_PF_FREE); + if (ret != KNOT_EOK) { + knot_rrset_clear(&opt_rr, &packet->mm); + } + + return ret; +} + +static bool do_padding(const query_t *query) +{ + return (query->padding != -3) && // Disabled padding. + (query->padding > -1 || query->alignment > 0 || // Explicit padding. + query->padding == -2 || // Default padding. + (query->padding == -1 && query->tls.enable)); // TLS automatic. +} + +static bool use_edns(const query_t *query) +{ + return query->edns > -1 || query->udp_size > -1 || query->nsid || + query->flags.do_flag || query->subnet.family != AF_UNSPEC || + query->cc.len > 0 || do_padding(query) || + !ednsopt_list_empty(&query->edns_opts); +} + +static knot_pkt_t *create_query_packet(const query_t *query) +{ + // Set packet buffer size. + uint16_t max_size; + if (query->udp_size < 0) { + if (use_edns(query)) { + max_size = DEFAULT_EDNS_SIZE; + } else { + max_size = DEFAULT_UDP_SIZE; + } + } else { + max_size = query->udp_size; + } + + // Create packet skeleton. + knot_pkt_t *packet = create_empty_packet(max_size); + if (packet == NULL) { + return NULL; + } + + // Set flags to wireformat. + if (query->flags.aa_flag) { + knot_wire_set_aa(packet->wire); + } + if (query->flags.tc_flag) { + knot_wire_set_tc(packet->wire); + } + if (query->flags.rd_flag) { + knot_wire_set_rd(packet->wire); + } + if (query->flags.ra_flag) { + knot_wire_set_ra(packet->wire); + } + if (query->flags.z_flag) { + knot_wire_set_z(packet->wire); + } + if (query->flags.ad_flag) { + knot_wire_set_ad(packet->wire); + } + if (query->flags.cd_flag) { + knot_wire_set_cd(packet->wire); + } + + // Set NOTIFY opcode. + if (query->notify) { + knot_wire_set_opcode(packet->wire, KNOT_OPCODE_NOTIFY); + } + + // Set packet question if available. + knot_dname_t *qname = knot_dname_from_str_alloc(query->owner); + if (qname != NULL) { + int ret = knot_pkt_put_question(packet, qname, query->class_num, + query->type_num); + if (ret != KNOT_EOK) { + knot_dname_free(qname, NULL); + knot_pkt_free(packet); + return NULL; + } + } + + // For IXFR query or NOTIFY query with SOA serial, add a proper section. + if (query->serial >= 0) { + if (query->notify) { + knot_pkt_begin(packet, KNOT_ANSWER); + } else { + knot_pkt_begin(packet, KNOT_AUTHORITY); + } + + // SOA rdata in wireformat. + uint8_t wire[22] = { 0x0 }; + + // Create rrset with SOA record. + knot_rrset_t *soa = knot_rrset_new(qname, + KNOT_RRTYPE_SOA, + query->class_num, + 0, + &packet->mm); + knot_dname_free(qname, NULL); + if (soa == NULL) { + knot_pkt_free(packet); + return NULL; + } + + // Fill in blank SOA rdata to rrset. + int ret = knot_rrset_add_rdata(soa, wire, sizeof(wire), &packet->mm); + if (ret != KNOT_EOK) { + knot_rrset_free(soa, &packet->mm); + knot_pkt_free(packet); + return NULL; + } + + // Set SOA serial. + knot_soa_serial_set(soa->rrs.rdata, query->serial); + + ret = knot_pkt_put(packet, KNOT_COMPR_HINT_NONE, soa, KNOT_PF_FREE); + if (ret != KNOT_EOK) { + knot_rrset_free(soa, &packet->mm); + knot_pkt_free(packet); + return NULL; + } + + free(soa); + } else { + knot_dname_free(qname, NULL); + } + + // Begin additional section + knot_pkt_begin(packet, KNOT_ADDITIONAL); + + // Create EDNS section if required. + if (use_edns(query)) { + int ret = add_query_edns(packet, query, max_size); + if (ret != KNOT_EOK) { + ERR("can't set up EDNS section\n"); + knot_pkt_free(packet); + return NULL; + } + } + + return packet; +} + +static bool check_reply_id(const knot_pkt_t *reply, + const knot_pkt_t *query) +{ + uint16_t query_id = knot_wire_get_id(query->wire); + uint16_t reply_id = knot_wire_get_id(reply->wire); + + if (reply_id != query_id) { + WARN("reply ID (%u) is different from query ID (%u)\n", + reply_id, query_id); + return false; + } + + return true; +} + +static void check_reply_qr(const knot_pkt_t *reply) +{ + if (!knot_wire_get_qr(reply->wire)) { + WARN("response QR bit not set\n"); + } +} + +static void check_reply_question(const knot_pkt_t *reply, + const knot_pkt_t *query) +{ + if (knot_wire_get_qdcount(reply->wire) < 1) { + WARN("response doesn't have question section\n"); + return; + } + + if (!knot_dname_is_equal(knot_pkt_qname(reply), knot_pkt_qname(query)) || + knot_pkt_qclass(reply) != knot_pkt_qclass(query) || + knot_pkt_qtype(reply) != knot_pkt_qtype(query)) { + WARN("query/response question sections are different\n"); + return; + } +} + +static int64_t first_serial_check(const knot_pkt_t *reply) +{ + const knot_pktsection_t *answer = knot_pkt_section(reply, KNOT_ANSWER); + + if (answer->count <= 0) { + return -1; + } + + const knot_rrset_t *first = knot_pkt_rr(answer, 0); + + if (first->type != KNOT_RRTYPE_SOA) { + return -1; + } else { + return knot_soa_serial(first->rrs.rdata); + } +} + +static bool finished_xfr(const uint32_t serial, const knot_pkt_t *reply, + const size_t msg_count, bool is_ixfr) +{ + const knot_pktsection_t *answer = knot_pkt_section(reply, KNOT_ANSWER); + + if (answer->count <= 0) { + return false; + } + + const knot_rrset_t *last = knot_pkt_rr(answer, answer->count - 1); + + if (last->type != KNOT_RRTYPE_SOA) { + return false; + } else if (answer->count == 1 && msg_count == 1) { + return is_ixfr; + } else { + return knot_soa_serial(last->rrs.rdata) == serial; + } +} + +static int sign_query(knot_pkt_t *pkt, const query_t *query, sign_context_t *ctx) +{ + if (query->tsig_key.name == NULL) { + return KNOT_EOK; + } + + int ret = sign_context_init_tsig(ctx, &query->tsig_key); + if (ret != KNOT_EOK) { + return ret; + } + + ret = sign_packet(pkt, ctx); + if (ret != KNOT_EOK) { + sign_context_deinit(ctx); + return ret; + } + + return KNOT_EOK; +} + +static int process_query_packet(const knot_pkt_t *query, + net_t *net, + const query_t *query_ctx, + const bool ignore_tc, + const sign_context_t *sign_ctx, + const style_t *style) +{ + struct timespec t_start, t_query, t_query_full, t_end, t_end_full; + time_t timestamp; + knot_pkt_t *reply; + uint8_t in[MAX_PACKET_SIZE]; + int in_len; + int ret; + + // Get start query time. + timestamp = time(NULL); + t_start = time_now(); + + // Connect to the server. + ret = net_connect(net); + if (ret != KNOT_EOK) { + return -1; + } + + // Send query packet. + ret = net_send(net, query->wire, query->size); + if (ret != KNOT_EOK) { + net_close(net); + return -1; + } + + // Get stop query time and start reply time. + t_query = time_now(); + t_query_full = time_diff(&t_start, &t_query); + t_query_full.tv_sec += timestamp; + +#if USE_DNSTAP + // Make the dnstap copy of the query. + write_dnstap(query_ctx->dt_writer, true, query->wire, query->size, + net, &t_query_full); +#endif // USE_DNSTAP + + // Print query packet if required. + if (style->show_query) { + // Create copy of query packet for parsing. + knot_pkt_t *q = knot_pkt_new(query->wire, query->size, NULL); + if (q != NULL) { + if (knot_pkt_parse(q, 0) == KNOT_EOK) { + print_packet(q, net, query->size, + time_diff_ms(&t_start, &t_query), + timestamp, false, style); + } else { + ERR("can't print query packet\n"); + } + knot_pkt_free(q); + } else { + ERR("can't print query packet\n"); + } + + printf("\n"); + } + + // Loop over incoming messages, unless reply id is correct or timeout. + while (true) { + // Receive a reply message. + in_len = net_receive(net, in, sizeof(in)); + if (in_len <= 0) { + net_close(net); + return -1; + } + + // Get stop reply time. + t_end = time_now(); + t_end_full = time_diff(&t_start, &t_end); + t_end_full.tv_sec += timestamp; + +#if USE_DNSTAP + // Make the dnstap copy of the response. + write_dnstap(query_ctx->dt_writer, false, in, in_len, net, + &t_end_full); +#endif // USE_DNSTAP + + // Create reply packet structure to fill up. + reply = knot_pkt_new(in, in_len, NULL); + if (reply == NULL) { + ERR("internal error (%s)\n", knot_strerror(KNOT_ENOMEM)); + net_close(net); + return -1; + } + + // Parse reply to the packet structure. + if (knot_pkt_parse(reply, KNOT_PF_NOCANON) != KNOT_EOK) { + ERR("malformed reply packet from %s\n", net->remote_str); + knot_pkt_free(reply); + net_close(net); + return -1; + } + + // Compare reply header id. + if (check_reply_id(reply, query)) { + break; + // Check for timeout. + } else if (time_diff_ms(&t_query, &t_end) > 1000 * net->wait) { + knot_pkt_free(reply); + net_close(net); + return -1; + } + + knot_pkt_free(reply); + } + + // Check for TC bit and repeat query with TCP if required. + if (knot_wire_get_tc(reply->wire) != 0 && + ignore_tc == false && net->socktype == SOCK_DGRAM) { + printf("\n"); + WARN("truncated reply from %s, retrying over TCP\n\n", + net->remote_str); + knot_pkt_free(reply); + net_close(net); + + net->socktype = SOCK_STREAM; + + return process_query_packet(query, net, query_ctx, true, + sign_ctx, style); + } + + // Check for question sections equality. + check_reply_question(reply, query); + + // Check QR bit + check_reply_qr(reply); + + // Print reply packet. + print_packet(reply, net, in_len, time_diff_ms(&t_query, &t_end), timestamp, + true, style); + + // Verify signature if a key was specified. + if (sign_ctx->digest != NULL) { + ret = verify_packet(reply, sign_ctx); + if (ret != KNOT_EOK) { + WARN("reply verification for %s (%s)\n", + net->remote_str, knot_strerror(ret)); + } + } + + // Check for BADCOOKIE RCODE and repeat query with the new cookie if required. + if (knot_pkt_ext_rcode(reply) == KNOT_RCODE_BADCOOKIE && query_ctx->badcookie) { + printf("\n"); + WARN("bad cookie from %s, retrying with the received one\n", + net->remote_str); + net_close(net); + + // Prepare new query context. + query_t new_ctx = *query_ctx; + + uint8_t *opt = knot_pkt_edns_option(reply, KNOT_EDNS_OPTION_COOKIE); + if (opt == NULL) { + ERR("bad cookie, missing EDNS section\n"); + knot_pkt_free(reply); + return -1; + } + + const uint8_t *data = knot_edns_opt_get_data(opt); + uint16_t data_len = knot_edns_opt_get_length(opt); + int ret = knot_edns_cookie_parse(&new_ctx.cc, &new_ctx.sc, + data, data_len); + if (ret != KNOT_EOK) { + knot_pkt_free(reply); + ERR("bad cookie, missing EDNS cookie option\n"); + return -1; + } + knot_pkt_free(reply); + + // Restore the original client cookie. + new_ctx.cc = query_ctx->cc; + + knot_pkt_t *new_query = create_query_packet(&new_ctx); + ret = process_query_packet(new_query, net, &new_ctx, ignore_tc, + sign_ctx, style); + knot_pkt_free(new_query); + + return ret; + } + + knot_pkt_free(reply); + net_close(net); + + return 0; +} + +static int process_query(const query_t *query) +{ + node_t *server = NULL; + knot_pkt_t *out_packet; + net_t net; + int ret; + + // Create query packet. + out_packet = create_query_packet(query); + if (out_packet == NULL) { + ERR("can't create query packet\n"); + return -1; + } + + // Sign the query. + sign_context_t sign_ctx = { 0 }; + ret = sign_query(out_packet, query, &sign_ctx); + if (ret != KNOT_EOK) { + ERR("can't sign the packet (%s)\n", knot_strerror(ret)); + return -1; + } + + // Get connection parameters. + int iptype = get_iptype(query->ip); + int socktype = get_socktype(query->protocol, query->type_num); + int flags = query->fastopen ? NET_FLAGS_FASTOPEN : NET_FLAGS_NONE; + + // Loop over server list to process query. + WALK_LIST(server, query->servers) { + srv_info_t *remote = (srv_info_t *)server; + + DBG("Querying for owner(%s), class(%u), type(%u), server(%s), " + "port(%s), protocol(%s)\n", query->owner, query->class_num, + query->type_num, remote->name, remote->service, + get_sockname(socktype)); + + // Loop over the number of retries. + for (size_t i = 0; i <= query->retries; i++) { + // Initialize network structure for current server. + ret = net_init(query->local, remote, iptype, socktype, + query->wait, flags, &query->tls, &net); + if (ret != KNOT_EOK) { + continue; + } + + // Loop over all resolved addresses for remote. + while (net.srv != NULL) { + ret = process_query_packet(out_packet, &net, + query, + query->ignore_tc, + &sign_ctx, + &query->style); + // If error try next resolved address. + if (ret != 0) { + net.srv = (net.srv)->ai_next; + if (net.srv != NULL && query->style.show_query) { + printf("\n"); + } + + continue; + } + + break; + } + + // Success. + if (ret == 0) { + net_clean(&net); + sign_context_deinit(&sign_ctx); + knot_pkt_free(out_packet); + return 0; + } + + if (i < query->retries) { + DBG("retrying server %s@%s(%s)\n", + remote->name, remote->service, + get_sockname(socktype)); + + if (query->style.show_query) { + printf("\n"); + } + } + + net_clean(&net); + } + + ERR("failed to query server %s@%s(%s)\n", + remote->name, remote->service, get_sockname(socktype)); + + // If not last server, print separation. + if (server->next->next && query->style.show_query) { + printf("\n"); + } + } + + sign_context_deinit(&sign_ctx); + knot_pkt_free(out_packet); + + return -1; +} + +static int process_xfr_packet(const knot_pkt_t *query, + net_t *net, + const query_t *query_ctx, + const sign_context_t *sign_ctx, + const style_t *style) +{ + struct timespec t_start, t_query, t_query_full, t_end, t_end_full; + time_t timestamp; + knot_pkt_t *reply; + uint8_t in[MAX_PACKET_SIZE]; + int in_len; + int ret; + int64_t serial = 0; + size_t total_len = 0; + size_t msg_count = 0; + size_t rr_count = 0; + + // Get start query time. + timestamp = time(NULL); + t_start = time_now(); + + // Connect to the server. + ret = net_connect(net); + if (ret != KNOT_EOK) { + return -1; + } + + // Send query packet. + ret = net_send(net, query->wire, query->size); + if (ret != KNOT_EOK) { + net_close(net); + return -1; + } + + // Get stop query time and start reply time. + t_query = time_now(); + t_query_full = time_diff(&t_start, &t_query); + t_query_full.tv_sec += timestamp; + +#if USE_DNSTAP + // Make the dnstap copy of the query. + write_dnstap(query_ctx->dt_writer, true, query->wire, query->size, + net, &t_query_full); +#endif // USE_DNSTAP + + // Print query packet if required. + if (style->show_query) { + // Create copy of query packet for parsing. + knot_pkt_t *q = knot_pkt_new(query->wire, query->size, NULL); + if (q != NULL) { + if (knot_pkt_parse(q, 0) == KNOT_EOK) { + print_packet(q, net, query->size, + time_diff_ms(&t_start, &t_query), + timestamp, false, style); + } else { + ERR("can't print query packet\n"); + } + knot_pkt_free(q); + } else { + ERR("can't print query packet\n"); + } + + printf("\n"); + } + + // Loop over reply messages unless first and last SOA serials differ. + while (true) { + // Receive a reply message. + in_len = net_receive(net, in, sizeof(in)); + if (in_len <= 0) { + net_close(net); + return -1; + } + + // Get stop message time. + t_end = time_now(); + t_end_full = time_diff(&t_start, &t_end); + t_end_full.tv_sec += timestamp; + +#if USE_DNSTAP + // Make the dnstap copy of the response. + write_dnstap(query_ctx->dt_writer, false, in, in_len, net, + &t_end_full); +#endif // USE_DNSTAP + + // Create reply packet structure to fill up. + reply = knot_pkt_new(in, in_len, NULL); + if (reply == NULL) { + ERR("internal error (%s)\n", knot_strerror(KNOT_ENOMEM)); + net_close(net); + return -1; + } + + // Parse reply to the packet structure. + if (knot_pkt_parse(reply, 0) != KNOT_EOK) { + ERR("malformed reply packet from %s\n", net->remote_str); + knot_pkt_free(reply); + net_close(net); + return -1; + } + + // Compare reply header id. + if (check_reply_id(reply, query) == false) { + ERR("reply ID mismatch from %s\n", net->remote_str); + knot_pkt_free(reply); + net_close(net); + return -1; + } + + // Print leading transfer information. + if (msg_count == 0) { + print_header_xfr(query, style); + } + + // Check for error reply. + if (knot_pkt_ext_rcode(reply) != KNOT_RCODE_NOERROR) { + ERR("server replied with error '%s'\n", + knot_pkt_ext_rcode_name(reply)); + knot_pkt_free(reply); + net_close(net); + return -1; + } + + // The first message has a special treatment. + if (msg_count == 0) { + // Verify 1. signature if a key was specified. + if (sign_ctx->digest != NULL) { + ret = verify_packet(reply, sign_ctx); + if (ret != KNOT_EOK) { + style_t tsig_style = { + .format = style->format, + .style = style->style, + .show_tsig = true + }; + print_data_xfr(reply, &tsig_style); + + ERR("reply verification for %s (%s)\n", + net->remote_str, knot_strerror(ret)); + knot_pkt_free(reply); + net_close(net); + return -1; + } + } + + // Read first SOA serial. + serial = first_serial_check(reply); + + if (serial < 0) { + ERR("first answer record from %s isn't SOA\n", + net->remote_str); + knot_pkt_free(reply); + net_close(net); + return -1; + } + + // Check for question sections equality. + check_reply_question(reply, query); + + // Check QR bit + check_reply_qr(reply); + } + + msg_count++; + rr_count += knot_wire_get_ancount(reply->wire); + total_len += in_len; + + // Print reply packet. + print_data_xfr(reply, style); + + // Check for finished transfer. + if (finished_xfr(serial, reply, msg_count, query_ctx->serial != -1)) { + knot_pkt_free(reply); + break; + } + + knot_pkt_free(reply); + } + + // Get stop reply time. + t_end = time_now(); + + // Print trailing transfer information. + print_footer_xfr(total_len, msg_count, rr_count, net, + time_diff_ms(&t_query, &t_end), timestamp, style); + + net_close(net); + + return 0; +} + +static int process_xfr(const query_t *query) +{ + knot_pkt_t *out_packet; + net_t net; + int ret; + + // Create query packet. + out_packet = create_query_packet(query); + if (out_packet == NULL) { + ERR("can't create query packet\n"); + return -1; + } + + // Sign the query. + sign_context_t sign_ctx = { 0 }; + ret = sign_query(out_packet, query, &sign_ctx); + if (ret != KNOT_EOK) { + ERR("can't sign the packet (%s)\n", knot_strerror(ret)); + return -1; + } + + // Get connection parameters. + int iptype = get_iptype(query->ip); + int socktype = get_socktype(query->protocol, query->type_num); + int flags = query->fastopen ? NET_FLAGS_FASTOPEN : NET_FLAGS_NONE; + + // Use the first nameserver from the list. + srv_info_t *remote = HEAD(query->servers); + + DBG("Querying for owner(%s), class(%u), type(%u), server(%s), " + "port(%s), protocol(%s)\n", query->owner, query->class_num, + query->type_num, remote->name, remote->service, + get_sockname(socktype)); + + // Initialize network structure. + ret = net_init(query->local, remote, iptype, socktype, query->wait, + flags, &query->tls, &net); + if (ret != KNOT_EOK) { + sign_context_deinit(&sign_ctx); + knot_pkt_free(out_packet); + return -1; + } + + // Loop over all resolved addresses for remote. + while (net.srv != NULL) { + ret = process_xfr_packet(out_packet, &net, + query, + &sign_ctx, + &query->style); + // If error try next resolved address. + if (ret != 0) { + net.srv = (net.srv)->ai_next; + continue; + } + + break; + } + + if (ret != 0) { + ERR("failed to query server %s@%s(%s)\n", + remote->name, remote->service, get_sockname(socktype)); + } + + net_clean(&net); + sign_context_deinit(&sign_ctx); + knot_pkt_free(out_packet); + + return ret; +} + +int kdig_exec(const kdig_params_t *params) +{ + node_t *n = NULL; + + if (params == NULL) { + DBG_NULL; + return KNOT_EINVAL; + } + + bool success = true; + + // Loop over query list. + WALK_LIST(n, params->queries) { + query_t *query = (query_t *)n; + + int ret = -1; + switch (query->operation) { + case OPERATION_QUERY: + ret = process_query(query); + break; + case OPERATION_XFR: + ret = process_xfr(query); + break; +#if USE_DNSTAP + case OPERATION_LIST_DNSTAP: + ret = process_dnstap(query); + break; +#endif // USE_DNSTAP + case OPERATION_LIST_SOA: + break; + default: + ERR("unsupported operation\n"); + break; + } + + // All operations must succeed. + if (ret != 0) { + success = false; + } + + // If not last query, print separation. + if (n->next->next && params->config->style.format == FORMAT_FULL) { + printf("\n"); + } + } + + return success ? KNOT_EOK : KNOT_ERROR; +} diff --git a/src/utils/kdig/kdig_exec.h b/src/utils/kdig/kdig_exec.h new file mode 100644 index 0000000..ee6c548 --- /dev/null +++ b/src/utils/kdig/kdig_exec.h @@ -0,0 +1,21 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "utils/kdig/kdig_params.h" + +int kdig_exec(const kdig_params_t *params); diff --git a/src/utils/kdig/kdig_main.c b/src/utils/kdig/kdig_main.c new file mode 100644 index 0000000..0cfcd47 --- /dev/null +++ b/src/utils/kdig/kdig_main.c @@ -0,0 +1,43 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stdlib.h> + +#include "libdnssec/crypto.h" +#include "utils/kdig/kdig_params.h" +#include "utils/kdig/kdig_exec.h" +#include "libknot/libknot.h" + +int main(int argc, char *argv[]) +{ + int ret = EXIT_SUCCESS; + + kdig_params_t params; + if (kdig_parse(¶ms, argc, argv) == KNOT_EOK) { + if (!params.stop) { + dnssec_crypto_init(); + if (kdig_exec(¶ms) != KNOT_EOK) { + ret = EXIT_FAILURE; + } + dnssec_crypto_cleanup(); + } + } else { + ret = EXIT_FAILURE; + } + + kdig_clean(¶ms); + return ret; +} diff --git a/src/utils/kdig/kdig_params.c b/src/utils/kdig/kdig_params.c new file mode 100644 index 0000000..abc231c --- /dev/null +++ b/src/utils/kdig/kdig_params.c @@ -0,0 +1,2317 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <arpa/inet.h> +#include <locale.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "utils/kdig/kdig_params.h" +#include "utils/common/cert.h" +#include "utils/common/hex.h" +#include "utils/common/msg.h" +#include "utils/common/params.h" +#include "utils/common/resolv.h" +#include "libknot/descriptor.h" +#include "libknot/libknot.h" +#include "contrib/base64.h" +#include "contrib/sockaddr.h" +#include "contrib/string.h" +#include "contrib/strtonum.h" +#include "contrib/ucw/lists.h" +#include "libdnssec/error.h" +#include "libdnssec/random.h" + +#define PROGRAM_NAME "kdig" + +#define DEFAULT_RETRIES_DIG 2 +#define DEFAULT_TIMEOUT_DIG 5 +#define DEFAULT_ALIGNMENT_SIZE 128 + +static const flags_t DEFAULT_FLAGS_DIG = { + .aa_flag = false, + .tc_flag = false, + .rd_flag = true, + .ra_flag = false, + .z_flag = false, + .ad_flag = false, + .cd_flag = false, + .do_flag = false +}; + +static const style_t DEFAULT_STYLE_DIG = { + .format = FORMAT_FULL, + .style = { + .wrap = false, + .show_class = true, + .show_ttl = true, + .verbose = false, + .original_ttl = false, + .empty_ttl = false, + .human_ttl = false, + .human_tmstamp = true, + .generic = false, + .ascii_to_idn = name_to_idn + }, + .show_query = false, + .show_header = true, + .show_section = true, + .show_edns = true, + .show_question = true, + .show_answer = true, + .show_authority = true, + .show_additional = true, + .show_tsig = true, + .show_footer = true +}; + +static int opt_multiline(const char *arg, void *query) +{ + query_t *q = query; + + q->style.style.wrap = true; + q->style.format = FORMAT_FULL; + q->style.show_header = true; + q->style.show_edns = true; + q->style.show_footer = true; + q->style.style.verbose = true; + + return KNOT_EOK; +} + +static int opt_nomultiline(const char *arg, void *query) +{ + query_t *q = query; + + q->style.style.wrap = false; + + return KNOT_EOK; +} + +static int opt_short(const char *arg, void *query) +{ + query_t *q = query; + + q->style.format = FORMAT_DIG; + q->style.show_header = false; + q->style.show_edns = false; + q->style.show_footer = false; + + return KNOT_EOK; +} + +static int opt_noshort(const char *arg, void *query) +{ + query_t *q = query; + + q->style.format = FORMAT_FULL; + + return KNOT_EOK; +} + +static int opt_generic(const char *arg, void *query) +{ + query_t *q = query; + + q->style.style.generic = true; + + return KNOT_EOK; +} + +static int opt_nogeneric(const char *arg, void *query) +{ + query_t *q = query; + + q->style.style.generic = false; + + return KNOT_EOK; +} + +static int opt_aaflag(const char *arg, void *query) +{ + query_t *q = query; + + q->flags.aa_flag = true; + + return KNOT_EOK; +} + +static int opt_noaaflag(const char *arg, void *query) +{ + query_t *q = query; + + q->flags.aa_flag = false; + + return KNOT_EOK; +} + +static int opt_tcflag(const char *arg, void *query) +{ + query_t *q = query; + + q->flags.tc_flag = true; + + return KNOT_EOK; +} + +static int opt_notcflag(const char *arg, void *query) +{ + query_t *q = query; + + q->flags.tc_flag = false; + + return KNOT_EOK; +} + +static int opt_rdflag(const char *arg, void *query) +{ + query_t *q = query; + + q->flags.rd_flag = true; + + return KNOT_EOK; +} + +static int opt_nordflag(const char *arg, void *query) +{ + query_t *q = query; + + q->flags.rd_flag = false; + + return KNOT_EOK; +} + +static int opt_raflag(const char *arg, void *query) +{ + query_t *q = query; + + q->flags.ra_flag = true; + + return KNOT_EOK; +} + +static int opt_noraflag(const char *arg, void *query) +{ + query_t *q = query; + + q->flags.ra_flag = false; + + return KNOT_EOK; +} + +static int opt_zflag(const char *arg, void *query) +{ + query_t *q = query; + + q->flags.z_flag = true; + + return KNOT_EOK; +} + +static int opt_nozflag(const char *arg, void *query) +{ + query_t *q = query; + + q->flags.z_flag = false; + + return KNOT_EOK; +} + +static int opt_adflag(const char *arg, void *query) +{ + query_t *q = query; + + q->flags.ad_flag = true; + + return KNOT_EOK; +} + +static int opt_noadflag(const char *arg, void *query) +{ + query_t *q = query; + + q->flags.ad_flag = false; + + return KNOT_EOK; +} + +static int opt_cdflag(const char *arg, void *query) +{ + query_t *q = query; + + q->flags.cd_flag = true; + + return KNOT_EOK; +} + +static int opt_nocdflag(const char *arg, void *query) +{ + query_t *q = query; + + q->flags.cd_flag = false; + + return KNOT_EOK; +} + +static int opt_doflag(const char *arg, void *query) +{ + query_t *q = query; + + q->flags.do_flag = true; + + return KNOT_EOK; +} + +static int opt_nodoflag(const char *arg, void *query) +{ + query_t *q = query; + + q->flags.do_flag = false; + + return KNOT_EOK; +} + +static int opt_all(const char *arg, void *query) +{ + query_t *q = query; + + q->style.show_header = true; + q->style.show_edns = true; + q->style.show_question = true; + q->style.show_answer = true; + q->style.show_authority = true; + q->style.show_additional = true; + q->style.show_tsig = true; + q->style.show_footer = true; + + return KNOT_EOK; +} + +static int opt_noall(const char *arg, void *query) +{ + query_t *q = query; + + q->style.show_header = false; + q->style.show_edns = false; + q->style.show_query = false; + q->style.show_question = false; + q->style.show_answer = false; + q->style.show_authority = false; + q->style.show_additional = false; + q->style.show_tsig = false; + q->style.show_footer = false; + + return KNOT_EOK; +} + +static int opt_qr(const char *arg, void *query) +{ + query_t *q = query; + + q->style.show_query = true; + + return KNOT_EOK; +} + +static int opt_noqr(const char *arg, void *query) +{ + query_t *q = query; + + q->style.show_query = false; + + return KNOT_EOK; +} + +static int opt_header(const char *arg, void *query) +{ + query_t *q = query; + + q->style.show_header = true; + + return KNOT_EOK; +} + +static int opt_noheader(const char *arg, void *query) +{ + query_t *q = query; + + q->style.show_header = false; + + return KNOT_EOK; +} + +static int opt_comments(const char *arg, void *query) +{ + query_t *q = query; + + q->style.show_section = true; + + return KNOT_EOK; +} + +static int opt_nocomments(const char *arg, void *query) +{ + query_t *q = query; + + q->style.show_section = false; + + return KNOT_EOK; +} + +static int opt_opt(const char *arg, void *query) +{ + query_t *q = query; + + q->style.show_edns = true; + + return KNOT_EOK; +} + +static int opt_noopt(const char *arg, void *query) +{ + query_t *q = query; + + q->style.show_edns = false; + + return KNOT_EOK; +} + +static int opt_question(const char *arg, void *query) +{ + query_t *q = query; + + q->style.show_question = true; + + return KNOT_EOK; +} + +static int opt_noquestion(const char *arg, void *query) +{ + query_t *q = query; + + q->style.show_question = false; + + return KNOT_EOK; +} + +static int opt_answer(const char *arg, void *query) +{ + query_t *q = query; + + q->style.show_answer = true; + + return KNOT_EOK; +} + +static int opt_noanswer(const char *arg, void *query) +{ + query_t *q = query; + + q->style.show_answer = false; + + return KNOT_EOK; +} + +static int opt_authority(const char *arg, void *query) +{ + query_t *q = query; + + q->style.show_authority = true; + + return KNOT_EOK; +} + +static int opt_noauthority(const char *arg, void *query) +{ + query_t *q = query; + + q->style.show_authority = false; + + return KNOT_EOK; +} + +static int opt_additional(const char *arg, void *query) +{ + query_t *q = query; + + q->style.show_additional = true; + + return KNOT_EOK; +} + +static int opt_noadditional(const char *arg, void *query) +{ + query_t *q = query; + + q->style.show_additional = false; + q->style.show_edns = false; + q->style.show_tsig = false; + + return KNOT_EOK; +} + +static int opt_tsig(const char *arg, void *query) +{ + query_t *q = query; + + q->style.show_tsig = true; + + return KNOT_EOK; +} + +static int opt_notsig(const char *arg, void *query) +{ + query_t *q = query; + + q->style.show_tsig = false; + + return KNOT_EOK; +} + +static int opt_stats(const char *arg, void *query) +{ + query_t *q = query; + + q->style.show_footer = true; + + return KNOT_EOK; +} + +static int opt_nostats(const char *arg, void *query) +{ + query_t *q = query; + + q->style.show_footer = false; + + return KNOT_EOK; +} + +static int opt_class(const char *arg, void *query) +{ + query_t *q = query; + + q->style.style.show_class = true; + + return KNOT_EOK; +} + +static int opt_noclass(const char *arg, void *query) +{ + query_t *q = query; + + q->style.style.show_class = false; + + return KNOT_EOK; +} + +static int opt_ttl(const char *arg, void *query) +{ + query_t *q = query; + + q->style.style.show_ttl = true; + + return KNOT_EOK; +} + +static int opt_nottl(const char *arg, void *query) +{ + query_t *q = query; + + q->style.style.show_ttl = false; + + return KNOT_EOK; +} + +static int opt_ignore(const char *arg, void *query) +{ + query_t *q = query; + + q->ignore_tc = true; + + return KNOT_EOK; +} + +static int opt_noignore(const char *arg, void *query) +{ + query_t *q = query; + + q->ignore_tc = false; + + return KNOT_EOK; +} + +static int opt_crypto(const char *arg, void *query) +{ + query_t *q = query; + + q->style.style.hide_crypto = false; + + return KNOT_EOK; +} + +static int opt_nocrypto(const char *arg, void *query) +{ + query_t *q = query; + + q->style.style.hide_crypto = true; + + return KNOT_EOK; +} + +static int opt_tcp(const char *arg, void *query) +{ + query_t *q = query; + + q->protocol = PROTO_TCP; + + return KNOT_EOK; +} + +static int opt_notcp(const char *arg, void *query) +{ + query_t *q = query; + + q->protocol = PROTO_UDP; + return opt_ignore(arg, query); +} + +static int opt_fastopen(const char *arg, void *query) +{ + query_t *q = query; + + q->fastopen = true; + + return KNOT_EOK; +} + +static int opt_nofastopen(const char *arg, void *query) +{ + query_t *q = query; + + q->fastopen = false; + + return opt_ignore(arg, query); +} + +static int opt_tls(const char *arg, void *query) +{ + query_t *q = query; + + q->tls.enable = true; + return opt_tcp(arg, query); +} + +static int opt_notls(const char *arg, void *query) +{ + query_t *q = query; + + tls_params_clean(&q->tls); + tls_params_init(&q->tls); + + return KNOT_EOK; +} + +static int opt_tls_ca(const char *arg, void *query) +{ + query_t *q = query; + + if (arg == NULL) { + q->tls.system_ca = true; + return opt_tls(arg, query); + } else { + if (ptrlist_add(&q->tls.ca_files, strdup(arg), NULL) == NULL) { + return KNOT_ENOMEM; + } + return opt_tls(arg, query); + } +} + +static int opt_notls_ca(const char *arg, void *query) +{ + query_t *q = query; + + q->tls.system_ca = false; + + ptrnode_t *node = NULL, *nxt = NULL; + WALK_LIST_DELSAFE(node, nxt, q->tls.ca_files) { + free(node->d); + } + ptrlist_free(&q->tls.ca_files, NULL); + + return KNOT_EOK; +} + +static int opt_tls_pin(const char *arg, void *query) +{ + query_t *q = query; + + uint8_t pin[64] = { 0 }; + + int ret = base64_decode((const uint8_t *)arg, strlen(arg), pin, sizeof(pin)); + if (ret < 0) { + ERR("invalid +tls-pin=%s\n", arg); + return ret; + } else if (ret != CERT_PIN_LEN) { // Check for 256-bit value. + ERR("invalid sha256 hash length +tls-pin=%s\n", arg); + return KNOT_EINVAL; + } + + uint8_t *item = malloc(1 + ret); // 1 ~ leading data length. + if (item == NULL) { + return KNOT_ENOMEM; + } + item[0] = ret; + memcpy(&item[1], pin, ret); + + if (ptrlist_add(&q->tls.pins, item, NULL) == NULL) { + return KNOT_ENOMEM; + } + + return opt_tls(arg, query); +} + +static int opt_notls_pin(const char *arg, void *query) +{ + query_t *q = query; + + ptrnode_t *node = NULL, *nxt = NULL; + WALK_LIST_DELSAFE(node, nxt, q->tls.pins) { + free(node->d); + } + ptrlist_free(&q->tls.pins, NULL); + + return KNOT_EOK; +} + +static int opt_tls_hostname(const char *arg, void *query) +{ + query_t *q = query; + + free(q->tls.hostname); + q->tls.hostname = strdup(arg); + + return opt_tls(arg, query); +} + +static int opt_notls_hostname(const char *arg, void *query) +{ + query_t *q = query; + + free(q->tls.hostname); + q->tls.hostname = NULL; + + return KNOT_EOK; +} + +static int opt_tls_sni(const char *arg, void *query) +{ + query_t *q = query; + + free(q->tls.sni); + q->tls.sni = strdup(arg); + + return opt_tls(arg, query); +} + +static int opt_notls_sni(const char *arg, void *query) +{ + query_t *q = query; + + free(q->tls.sni); + q->tls.sni = NULL; + + return KNOT_EOK; +} + +static int opt_nsid(const char *arg, void *query) +{ + query_t *q = query; + + q->nsid = true; + + return KNOT_EOK; +} + +static int opt_nonsid(const char *arg, void *query) +{ + query_t *q = query; + + q->nsid = false; + + return KNOT_EOK; +} + +static int opt_bufsize(const char *arg, void *query) +{ + query_t *q = query; + + uint16_t num = 0; + if (str_to_u16(arg, &num) != KNOT_EOK) { + ERR("invalid +bufsize=%s\n", arg); + return KNOT_EINVAL; + } + + // Disable EDNS if zero bufsize. + if (num == 0) { + q->udp_size = -1; + } else if (num < KNOT_WIRE_HEADER_SIZE) { + q->udp_size = KNOT_WIRE_HEADER_SIZE; + } else { + q->udp_size = num; + } + + return KNOT_EOK; +} + +static int opt_nobufsize(const char *arg, void *query) +{ + query_t *q = query; + + q->udp_size = -1; + + return KNOT_EOK; +} + +static int opt_cookie(const char *arg, void *query) +{ + query_t *q = query; + + if (arg != NULL) { + uint8_t *input = NULL; + size_t input_len; + + int ret = hex_decode(arg, &input, &input_len); + if (ret != KNOT_EOK) { + ERR("invalid +cookie=%s\n", arg); + return KNOT_EINVAL; + } + + if (input_len < KNOT_EDNS_COOKIE_CLNT_SIZE) { + ERR("too short client +cookie=%s\n", arg); + free(input); + return KNOT_EINVAL; + } + q->cc.len = KNOT_EDNS_COOKIE_CLNT_SIZE; + memcpy(q->cc.data, input, q->cc.len); + + input_len -= q->cc.len; + if (input_len > 0) { + if (input_len < KNOT_EDNS_COOKIE_SRVR_MIN_SIZE) { + ERR("too short server +cookie=%s\n", arg); + free(input); + return KNOT_EINVAL; + } + if (input_len > KNOT_EDNS_COOKIE_SRVR_MAX_SIZE) { + ERR("too long server +cookie=%s\n", arg); + free(input); + return KNOT_EINVAL; + } + q->sc.len = input_len; + memcpy(q->sc.data, input + q->cc.len, q->sc.len); + } + + free(input); + } else { + q->cc.len = KNOT_EDNS_COOKIE_CLNT_SIZE; + + int ret = dnssec_random_buffer(q->cc.data, q->cc.len); + if (ret != DNSSEC_EOK) { + return knot_error_from_libdnssec(ret); + } + } + + return KNOT_EOK; +} + +static int opt_nocookie(const char *arg, void *query) +{ + query_t *q = query; + q->cc.len = 0; + q->sc.len = 0; + return KNOT_EOK; +} + +static int opt_badcookie(const char *arg, void *query) +{ + query_t *q = query; + q->badcookie = true; + return KNOT_EOK; +} + +static int opt_nobadcookie(const char *arg, void *query) +{ + query_t *q = query; + q->badcookie = false; + return KNOT_EOK; +} + +static int opt_padding(const char *arg, void *query) +{ + query_t *q = query; + + if (arg == NULL) { + q->padding = -2; + return KNOT_EOK; + } else { + uint16_t num = 0; + if (str_to_u16(arg, &num) != KNOT_EOK) { + ERR("invalid +padding=%s\n", arg); + return KNOT_EINVAL; + } + + q->padding = num; + return KNOT_EOK; + } +} + +static int opt_nopadding(const char *arg, void *query) +{ + query_t *q = query; + + q->padding = -3; + + return KNOT_EOK; +} + +static int opt_alignment(const char *arg, void *query) +{ + query_t *q = query; + + if (arg == NULL) { + q->alignment = DEFAULT_ALIGNMENT_SIZE; + return KNOT_EOK; + } else { + uint16_t num = 0; + if (str_to_u16(arg, &num) != KNOT_EOK || num < 2) { + ERR("invalid +alignment=%s\n", arg); + return KNOT_EINVAL; + } + + q->alignment = num; + return KNOT_EOK; + } +} + +static int opt_noalignment(const char *arg, void *query) +{ + query_t *q = query; + + q->alignment = 0; + + return KNOT_EOK; +} + +static int opt_subnet(const char *arg, void *query) +{ + query_t *q = query; + + char *sep = NULL; + const size_t arg_len = strlen(arg); + const char *arg_end = arg + arg_len; + size_t addr_len = 0; + + // Separate address and network mask. + if ((sep = strchr(arg, '/')) != NULL) { + addr_len = sep - arg; + } else { + addr_len = arg_len; + } + + // Check IP address. + + struct sockaddr_storage ss = { 0 }; + struct addrinfo hints = { .ai_flags = AI_NUMERICHOST }; + struct addrinfo *ai = NULL; + + char *addr_str = strndup(arg, addr_len); + if (getaddrinfo(addr_str, NULL, &hints, &ai) != 0) { + free(addr_str); + ERR("invalid address +subnet=%s\n", arg); + return KNOT_EINVAL; + } + + memcpy(&ss, ai->ai_addr, ai->ai_addrlen); + freeaddrinfo(ai); + free(addr_str); + + if (knot_edns_client_subnet_set_addr(&q->subnet, &ss) != KNOT_EOK) { + ERR("invalid address +subnet=%s\n", arg); + return KNOT_EINVAL; + } + + // Parse network mask. + const char *mask = arg; + if (mask + addr_len < arg_end) { + mask += addr_len + 1; + uint8_t num = 0; + if (str_to_u8(mask, &num) != KNOT_EOK || num > q->subnet.source_len) { + ERR("invalid network mask +subnet=%s\n", arg); + return KNOT_EINVAL; + } + q->subnet.source_len = num; + } + + return KNOT_EOK; +} + +static int opt_nosubnet(const char *arg, void *query) +{ + query_t *q = query; + + q->subnet.family = AF_UNSPEC; + + return KNOT_EOK; +} + +static int opt_edns(const char *arg, void *query) +{ + query_t *q = query; + + if (arg == NULL) { + q->edns = 0; + return KNOT_EOK; + } else { + uint8_t num = 0; + if (str_to_u8(arg, &num) != KNOT_EOK) { + ERR("invalid +edns=%s\n", arg); + return KNOT_EINVAL; + } + + q->edns = num; + return KNOT_EOK; + } +} + +static int opt_noedns(const char *arg, void *query) +{ + query_t *q = query; + + q->edns = -1; + opt_nodoflag(arg, query); + opt_nonsid(arg, query); + opt_nobufsize(arg, query); + opt_nocookie(arg, query); + opt_nopadding(arg, query); + opt_noalignment(arg, query); + opt_nosubnet(arg, query); + + return KNOT_EOK; +} + +static int opt_timeout(const char *arg, void *query) +{ + query_t *q = query; + + if (params_parse_wait(arg, &q->wait) != KNOT_EOK) { + ERR("invalid +timeout=%s\n", arg); + return KNOT_EINVAL; + } + + return KNOT_EOK; +} + +static int opt_notimeout(const char *arg, void *query) +{ + query_t *q = query; + + q->wait = DEFAULT_TIMEOUT_DIG; + + return KNOT_EOK; +} + +static int opt_retry(const char *arg, void *query) +{ + query_t *q = query; + + if (str_to_u32(arg, &q->retries) != KNOT_EOK) { + ERR("invalid +retry=%s\n", arg); + return KNOT_EINVAL; + } + + return KNOT_EOK; +} + +static int opt_noretry(const char *arg, void *query) +{ + query_t *q = query; + + q->retries = DEFAULT_RETRIES_DIG; + + return KNOT_EOK; +} + +static int parse_ednsopt(const char *arg, ednsopt_t **opt_ptr) +{ + errno = 0; + char *end = NULL; + unsigned long code = strtoul(arg, &end, 10); + if (errno != 0 || arg == end || code > UINT16_MAX) { + return KNOT_EINVAL; + } + + size_t length = 0; + uint8_t *data = NULL; + if (end[0] == ':') { + if (end[1] != '\0') { + int ret = hex_decode(end + 1, &data, &length); + if (ret != KNOT_EOK) { + return ret; + } + if (length > UINT16_MAX) { + free(data); + return KNOT_ERANGE; + } + } + } else if (end[0] != '\0') { + return KNOT_EINVAL; + } + + ednsopt_t *opt = ednsopt_create(code, length, data); + if (opt == NULL) { + free(data); + return KNOT_ENOMEM; + } + + *opt_ptr = opt; + return KNOT_EOK; +} + +static int opt_ednsopt(const char *arg, void *query) +{ + query_t *q = query; + + ednsopt_t *opt = NULL; + int ret = parse_ednsopt(arg, &opt); + if (ret != KNOT_EOK) { + ERR("invalid +ednsopt=%s\n", arg); + return KNOT_EINVAL; + } + + add_tail(&q->edns_opts, &opt->n); + + return KNOT_EOK; +} + +static int opt_noednsopt(const char *arg, void *query) +{ + query_t *q = query; + + ednsopt_list_deinit(&q->edns_opts); + + return KNOT_EOK; +} + +static int opt_noidn(const char *arg, void *query) +{ + query_t *q = query; + + q->idn = false; + q->style.style.ascii_to_idn = NULL; + + return KNOT_EOK; +} + +static const param_t kdig_opts2[] = { + { "multiline", ARG_NONE, opt_multiline }, + { "nomultiline", ARG_NONE, opt_nomultiline }, + + { "short", ARG_NONE, opt_short }, + { "noshort", ARG_NONE, opt_noshort }, + + { "generic", ARG_NONE, opt_generic }, + { "nogeneric", ARG_NONE, opt_nogeneric }, + + { "aaflag", ARG_NONE, opt_aaflag }, + { "noaaflag", ARG_NONE, opt_noaaflag }, + + { "tcflag", ARG_NONE, opt_tcflag }, + { "notcflag", ARG_NONE, opt_notcflag }, + + { "rdflag", ARG_NONE, opt_rdflag }, + { "nordflag", ARG_NONE, opt_nordflag }, + + { "recurse", ARG_NONE, opt_rdflag }, + { "norecurse", ARG_NONE, opt_nordflag }, + + { "raflag", ARG_NONE, opt_raflag }, + { "noraflag", ARG_NONE, opt_noraflag }, + + { "zflag", ARG_NONE, opt_zflag }, + { "nozflag", ARG_NONE, opt_nozflag }, + + { "adflag", ARG_NONE, opt_adflag }, + { "noadflag", ARG_NONE, opt_noadflag }, + + { "cdflag", ARG_NONE, opt_cdflag }, + { "nocdflag", ARG_NONE, opt_nocdflag }, + + { "dnssec", ARG_NONE, opt_doflag }, + { "nodnssec", ARG_NONE, opt_nodoflag }, + + { "all", ARG_NONE, opt_all }, + { "noall", ARG_NONE, opt_noall }, + + { "qr", ARG_NONE, opt_qr }, + { "noqr", ARG_NONE, opt_noqr }, + + { "header", ARG_NONE, opt_header }, + { "noheader", ARG_NONE, opt_noheader }, + + { "comments", ARG_NONE, opt_comments }, + { "nocomments", ARG_NONE, opt_nocomments }, + + { "opt", ARG_NONE, opt_opt }, + { "opt", ARG_NONE, opt_opt }, + { "noopt", ARG_NONE, opt_noopt }, + + { "question", ARG_NONE, opt_question }, + { "noquestion", ARG_NONE, opt_noquestion }, + + { "answer", ARG_NONE, opt_answer }, + { "noanswer", ARG_NONE, opt_noanswer }, + + { "authority", ARG_NONE, opt_authority }, + { "noauthority", ARG_NONE, opt_noauthority }, + + { "additional", ARG_NONE, opt_additional }, + { "noadditional", ARG_NONE, opt_noadditional }, + + { "tsig", ARG_NONE, opt_tsig }, + { "notsig", ARG_NONE, opt_notsig }, + + { "stats", ARG_NONE, opt_stats }, + { "nostats", ARG_NONE, opt_nostats }, + + { "class", ARG_NONE, opt_class }, + { "noclass", ARG_NONE, opt_noclass }, + + { "ttl", ARG_NONE, opt_ttl }, + { "nottl", ARG_NONE, opt_nottl }, + + { "crypto", ARG_NONE, opt_crypto }, + { "nocrypto", ARG_NONE, opt_nocrypto }, + + { "tcp", ARG_NONE, opt_tcp }, + { "notcp", ARG_NONE, opt_notcp }, + + { "fastopen", ARG_NONE, opt_fastopen }, + { "nofastopen", ARG_NONE, opt_nofastopen }, + + { "ignore", ARG_NONE, opt_ignore }, + { "noignore", ARG_NONE, opt_noignore }, + + { "tls", ARG_NONE, opt_tls }, + { "notls", ARG_NONE, opt_notls }, + + { "tls-ca", ARG_OPTIONAL, opt_tls_ca }, + { "notls-ca", ARG_NONE, opt_notls_ca }, + + { "tls-pin", ARG_REQUIRED, opt_tls_pin }, + { "notls-pin", ARG_NONE, opt_notls_pin }, + + { "tls-hostname", ARG_REQUIRED, opt_tls_hostname }, + { "notls-hostname", ARG_NONE, opt_notls_hostname }, + + { "tls-sni", ARG_REQUIRED, opt_tls_sni }, + { "notls-sni", ARG_NONE, opt_notls_sni }, + + { "nsid", ARG_NONE, opt_nsid }, + { "nonsid", ARG_NONE, opt_nonsid }, + + { "bufsize", ARG_REQUIRED, opt_bufsize }, + { "nobufsize", ARG_NONE, opt_nobufsize }, + + { "padding", ARG_OPTIONAL, opt_padding }, + { "nopadding", ARG_NONE, opt_nopadding }, + + { "alignment", ARG_OPTIONAL, opt_alignment }, + { "noalignment", ARG_NONE, opt_noalignment }, + + { "subnet", ARG_REQUIRED, opt_subnet }, + { "nosubnet", ARG_NONE, opt_nosubnet }, + + // Obsolete aliases. + { "client", ARG_REQUIRED, opt_subnet }, + { "noclient", ARG_NONE, opt_nosubnet }, + + { "edns", ARG_OPTIONAL, opt_edns }, + { "noedns", ARG_NONE, opt_noedns }, + + { "timeout", ARG_REQUIRED, opt_timeout }, + { "notimeout", ARG_NONE, opt_notimeout }, + + { "retry", ARG_REQUIRED, opt_retry }, + { "noretry", ARG_NONE, opt_noretry }, + + { "cookie", ARG_OPTIONAL, opt_cookie }, + { "nocookie", ARG_NONE, opt_nocookie }, + + { "badcookie", ARG_NONE, opt_badcookie }, + { "nobadcookie", ARG_NONE, opt_nobadcookie }, + + { "ednsopt", ARG_REQUIRED, opt_ednsopt }, + { "noednsopt", ARG_NONE, opt_noednsopt }, + + /* "idn" doesn't work since it must be called before query creation. */ + { "noidn", ARG_NONE, opt_noidn }, + + { NULL } +}; + +query_t *query_create(const char *owner, const query_t *conf) +{ + // Create output structure. + query_t *query = calloc(1, sizeof(query_t)); + + if (query == NULL) { + DBG_NULL; + return NULL; + } + + // Initialization with defaults or with reference query. + if (conf == NULL) { + query->conf = NULL; + query->local = NULL; + query->operation = OPERATION_QUERY; + query->ip = IP_ALL; + query->protocol = PROTO_ALL; + query->fastopen = false; + query->port = strdup(""); + query->udp_size = -1; + query->retries = DEFAULT_RETRIES_DIG; + query->wait = DEFAULT_TIMEOUT_DIG; + query->ignore_tc = false; + query->class_num = -1; + query->type_num = -1; + query->serial = -1; + query->notify = false; + query->flags = DEFAULT_FLAGS_DIG; + query->style = DEFAULT_STYLE_DIG; + query->idn = true; + query->nsid = false; + query->edns = -1; + query->cc.len = 0; + query->sc.len = 0; + query->badcookie = true; + query->padding = -1; + query->alignment = 0; + tls_params_init(&query->tls); + //query->tsig_key + query->subnet.family = AF_UNSPEC; + ednsopt_list_init(&query->edns_opts); +#if USE_DNSTAP + query->dt_reader = NULL; + query->dt_writer = NULL; +#endif // USE_DNSTAP + } else { + *query = *conf; + query->conf = conf; + if (conf->local != NULL) { + query->local = srv_info_create(conf->local->name, + conf->local->service); + if (query->local == NULL) { + query_free(query); + return NULL; + } + } else { + query->local = NULL; + } + query->port = strdup(conf->port); + tls_params_copy(&query->tls, &conf->tls); + if (conf->tsig_key.name != NULL) { + int ret = knot_tsig_key_copy(&query->tsig_key, + &conf->tsig_key); + if (ret != KNOT_EOK) { + query_free(query); + return NULL; + } + } + + int ret = ednsopt_list_dup(&query->edns_opts, &conf->edns_opts); + if (ret != KNOT_EOK) { + query_free(query); + return NULL; + } + +#if USE_DNSTAP + query->dt_reader = conf->dt_reader; + query->dt_writer = conf->dt_writer; +#endif // USE_DNSTAP + } + + // Initialize list of servers. + init_list(&query->servers); + + // Set the query owner if any. + if (owner != NULL) { + if ((query->owner = strdup(owner)) == NULL) { + query_free(query); + return NULL; + } + } + + // Check dynamic allocation. + if (query->port == NULL) { + query_free(query); + return NULL; + } + + return query; +} + +void query_free(query_t *query) +{ + node_t *n = NULL, *nxt = NULL; + + if (query == NULL) { + DBG_NULL; + return; + } + + // Cleanup servers. + WALK_LIST_DELSAFE(n, nxt, query->servers) { + srv_info_free((srv_info_t *)n); + } + + // Cleanup local address. + if (query->local != NULL) { + srv_info_free(query->local); + } + + tls_params_clean(&query->tls); + + // Cleanup signing key. + knot_tsig_key_deinit(&query->tsig_key); + + // Cleanup EDNS options. + ednsopt_list_deinit(&query->edns_opts); + +#if USE_DNSTAP + if (query->dt_reader != NULL) { + dt_reader_free(query->dt_reader); + } + if (query->dt_writer != NULL) { + // Global writer can be shared! + if (query->conf == NULL || + query->conf->dt_writer != query->dt_writer) { + dt_writer_free(query->dt_writer); + } + } +#endif // USE_DNSTAP + + free(query->owner); + free(query->port); + free(query); +} + +ednsopt_t *ednsopt_create(uint16_t code, uint16_t length, uint8_t *data) +{ + ednsopt_t *opt = calloc(1, sizeof(*opt)); + if (opt == NULL) { + return NULL; + } + + opt->code = code; + opt->length = length; + opt->data = data; + + return opt; +} + +ednsopt_t *ednsopt_dup(const ednsopt_t *opt) +{ + ednsopt_t *dup = calloc(1, sizeof(*opt)); + if (dup == NULL) { + return NULL; + } + + dup->code = opt->code; + dup->length = opt->length; + dup->data = memdup(opt->data, opt->length); + if (dup->data == NULL) { + free(dup); + return NULL; + } + + return dup; +} + +void ednsopt_free(ednsopt_t *opt) +{ + if (opt == NULL) { + return; + } + + free(opt->data); + free(opt); +} + +void ednsopt_list_init(list_t *list) +{ + init_list(list); +} + +void ednsopt_list_deinit(list_t *list) +{ + node_t *n, *next; + WALK_LIST_DELSAFE(n, next, *list) { + ednsopt_t *opt = (ednsopt_t *)n; + ednsopt_free(opt); + } + + init_list(list); +} + +int ednsopt_list_dup(list_t *dest, const list_t *src) +{ + list_t backup = *dest; + init_list(dest); + + node_t *n = NULL; + WALK_LIST(n, *src) { + ednsopt_t *opt = (ednsopt_t *)n; + ednsopt_t *dup = ednsopt_dup(opt); + if (dup == NULL) { + ednsopt_list_deinit(dest); + *dest = backup; + return KNOT_ENOMEM; + } + + add_tail(dest, &dup->n); + } + + return KNOT_EOK; +} + +bool ednsopt_list_empty(const list_t *list) +{ + return EMPTY_LIST(*list); +} + +int kdig_init(kdig_params_t *params) +{ + if (params == NULL) { + DBG_NULL; + return KNOT_EINVAL; + } + + memset(params, 0, sizeof(*params)); + + params->stop = false; + + // Initialize list of queries. + init_list(¶ms->queries); + + // Create config query. + if ((params->config = query_create(NULL, NULL)) == NULL) { + return KNOT_ENOMEM; + } + + return KNOT_EOK; +} + +void kdig_clean(kdig_params_t *params) +{ + node_t *n = NULL, *nxt = NULL; + + if (params == NULL) { + DBG_NULL; + return; + } + + // Clean up queries. + WALK_LIST_DELSAFE(n, nxt, params->queries) { + query_free((query_t *)n); + } + + // Clean up config. + query_free(params->config); + + // Clean up the structure. + memset(params, 0, sizeof(*params)); +} + +static int parse_class(const char *value, query_t *query) +{ + uint16_t rclass; + + if (params_parse_class(value, &rclass) != KNOT_EOK) { + return KNOT_EINVAL; + } + + query->class_num = rclass; + + return KNOT_EOK; +} + +static int parse_keyfile(const char *value, query_t *query) +{ + knot_tsig_key_deinit(&query->tsig_key); + + if (knot_tsig_key_init_file(&query->tsig_key, value) != KNOT_EOK) { + return KNOT_EINVAL; + } + + return KNOT_EOK; +} + +static int parse_local(const char *value, query_t *query) +{ + srv_info_t *local = parse_nameserver(value, "0"); + if (local == NULL) { + return KNOT_EINVAL; + } + + if (query->local != NULL) { + srv_info_free(query->local); + } + + query->local = local; + + return KNOT_EOK; +} + +static int parse_name(const char *value, list_t *queries, const query_t *conf) +{ + query_t *query = NULL; + char *ascii_name = (char *)value; + char *fqd_name = NULL; + + if (value != NULL) { + if (conf->idn) { + ascii_name = name_from_idn(value); + if (ascii_name == NULL) { + return KNOT_EINVAL; + } + } + + // If name is not FQDN, append trailing dot. + fqd_name = get_fqd_name(ascii_name); + + if (conf->idn) { + free(ascii_name); + } + } + + // Create new query. + query = query_create(fqd_name, conf); + + free(fqd_name); + + if (query == NULL) { + return KNOT_ENOMEM; + } + + // Add new query to the queries. + add_tail(queries, (node_t *)query); + + return KNOT_EOK; +} + +static int parse_port(const char *value, query_t *query) +{ + char **port; + + // Set current server port (last or query default). + if (list_size(&query->servers) > 0) { + srv_info_t *server = TAIL(query->servers); + port = &(server->service); + } else { + port = &(query->port); + } + + char *new_port = strdup(value); + + if (new_port == NULL) { + return KNOT_ENOMEM; + } + + // Deallocate old string. + free(*port); + + *port = new_port; + + return KNOT_EOK; +} + +static int parse_reverse(const char *value, list_t *queries, const query_t *conf) +{ + query_t *query = NULL; + + // Create reverse name. + char *reverse = get_reverse_name(value); + + if (reverse == NULL) { + return KNOT_EINVAL; + } + + // Create reverse query for given address. + query = query_create(reverse, conf); + + free(reverse); + + if (query == NULL) { + return KNOT_ENOMEM; + } + + // Set type for reverse query. + query->type_num = KNOT_RRTYPE_PTR; + + // Add new query to the queries. + add_tail(queries, (node_t *)query); + + return KNOT_EOK; +} + +static int parse_server(const char *value, kdig_params_t *params) +{ + query_t *query; + + // Set current query (last or config). + if (list_size(¶ms->queries) > 0) { + query = TAIL(params->queries); + } else { + query = params->config; + } + + if (params_parse_server(value, &query->servers, query->port) != KNOT_EOK) { + ERR("invalid server @%s\n", value); + return KNOT_EINVAL; + } + + return KNOT_EOK; +} + +static int parse_tsig(const char *value, query_t *query) +{ + knot_tsig_key_deinit(&query->tsig_key); + + if (knot_tsig_key_init_str(&query->tsig_key, value) != KNOT_EOK) { + return KNOT_EINVAL; + } + + return KNOT_EOK; +} + +static int parse_type(const char *value, query_t *query) +{ + uint16_t rtype; + int64_t serial; + bool notify; + + if (params_parse_type(value, &rtype, &serial, ¬ify) != KNOT_EOK) { + return KNOT_EINVAL; + } + + query->type_num = rtype; + query->serial = serial; + query->notify = notify; + + // If NOTIFY, reset default RD flag. + if (query->notify) { + query->flags.rd_flag = false; + } + + return KNOT_EOK; +} + +#if USE_DNSTAP +static int parse_dnstap_output(const char *value, query_t *query) +{ + if (query->dt_writer != NULL) { + if (query->conf == NULL || + query->conf->dt_writer != query->dt_writer) { + dt_writer_free(query->dt_writer); + } + } + + query->dt_writer = dt_writer_create(value, "kdig " PACKAGE_VERSION); + if (query->dt_writer == NULL) { + return KNOT_EINVAL; + } + + return KNOT_EOK; +} + +static int parse_dnstap_input(const char *value, query_t *query) +{ + // Just in case, shouldn't happen. + if (query->dt_reader != NULL) { + dt_reader_free(query->dt_reader); + } + + query->dt_reader = dt_reader_create(value); + if (query->dt_reader == NULL) { + return KNOT_EINVAL; + } + + return KNOT_EOK; +} +#endif // USE_DNSTAP + +static void complete_servers(query_t *query, const query_t *conf) +{ + node_t *n = NULL; + char *def_port; + + // Decide which default port use. + if (strlen(query->port) > 0) { + def_port = query->port; + } else if (strlen(conf->port) > 0) { + def_port = conf->port; + } else if (query->tls.enable) { + def_port = DEFAULT_DNS_TLS_PORT; + } else { + def_port = DEFAULT_DNS_PORT; + } + + // Complete specified nameservers if any. + if (list_size(&query->servers) > 0) { + WALK_LIST(n, query->servers) { + srv_info_t *s = (srv_info_t *)n; + + // If the port isn't specified yet use the default one. + if (strlen(s->service) == 0) { + free(s->service); + s->service = strdup(def_port); + if (s->service == NULL) { + WARN("can't set port %s\n", def_port); + return; + } + } + + // Use server name as hostname for TLS if necessary. + if (query->tls.enable && query->tls.hostname == NULL && + (query->tls.system_ca || !EMPTY_LIST(query->tls.ca_files))) { + query->tls.hostname = strdup(s->name); + } + } + // Use servers from config if any. + } else if (list_size(&conf->servers) > 0) { + WALK_LIST(n, conf->servers) { + srv_info_t *s = (srv_info_t *)n; + char *port = def_port; + + // If the port is already specified, use it. + if (strlen(s->service) > 0) { + port = s->service; + } + + srv_info_t *server = srv_info_create(s->name, port); + if (server == NULL) { + WARN("can't set nameserver %s port %s\n", + s->name, s->service); + return; + } + add_tail(&query->servers, (node_t *)server); + + // Use server name as hostname for TLS if necessary. + if (query->tls.enable && query->tls.hostname == NULL && + (query->tls.system_ca || !EMPTY_LIST(query->tls.ca_files))) { + query->tls.hostname = strdup(s->name); + } + } + // Use system specific. + } else { + get_nameservers(&query->servers, def_port); + } +} + +void complete_queries(list_t *queries, const query_t *conf) +{ + node_t *n = NULL; + + if (queries == NULL || conf == NULL) { + DBG_NULL; + return; + } + + // If there is no query, add default query: NS to ".". + if (list_size(queries) == 0) { + query_t *q = query_create(".", conf); + if (q == NULL) { + WARN("can't create query . NS IN\n"); + return; + } + q->class_num = KNOT_CLASS_IN; + q->type_num = KNOT_RRTYPE_NS; + add_tail(queries, (node_t *)q); + } + + WALK_LIST(n, *queries) { + query_t *q = (query_t *)n; + + // Fill class number if missing. + if (q->class_num < 0) { + if (conf->class_num >= 0) { + q->class_num = conf->class_num; + } else { + q->class_num = KNOT_CLASS_IN; + } + } + + // Fill type number if missing. + if (q->type_num < 0) { + if (conf->type_num >= 0) { + q->type_num = conf->type_num; + q->serial = conf->serial; + } else { + q->type_num = KNOT_RRTYPE_A; + } + } + + // Set zone transfer if any. + if (q->type_num == KNOT_RRTYPE_AXFR || + q->type_num == KNOT_RRTYPE_IXFR) { + q->operation = OPERATION_XFR; + } + + // No retries for TCP. + if (q->protocol == PROTO_TCP) { + q->retries = 0; + } + + // Complete nameservers list. + complete_servers(q, conf); + } +} + +static void print_help(void) +{ + printf("Usage: %s [-4] [-6] [-d] [-b address] [-c class] [-p port]\n" + " [-q name] [-t type] [-x address] [-k keyfile]\n" + " [-y [algo:]keyname:key] [-E tapfile] [-G tapfile]\n" + " name [type] [class] [@server]\n" + "\n" + " +[no]multiline Wrap long records to more lines.\n" + " +[no]short Show record data only.\n" + " +[no]generic Use generic representation format.\n" + " +[no]aaflag Set AA flag.\n" + " +[no]tcflag Set TC flag.\n" + " +[no]rdflag Set RD flag.\n" + " +[no]recurse Same as +[no]rdflag\n" + " +[no]raflag Set RA flag.\n" + " +[no]zflag Set zero flag bit.\n" + " +[no]adflag Set AD flag.\n" + " +[no]cdflag Set CD flag.\n" + " +[no]dnssec Set DO flag.\n" + " +[no]all Show all packet sections.\n" + " +[no]qr Show query packet.\n" + " +[no]header Show packet header.\n" + " +[no]comments Show commented section names.\n" + " +[no]opt Show EDNS pseudosection.\n" + " +[no]question Show question section.\n" + " +[no]answer Show answer section.\n" + " +[no]authority Show authority section.\n" + " +[no]additional Show additional section.\n" + " +[no]tsig Show TSIG pseudosection.\n" + " +[no]stats Show trailing packet statistics.\n" + " +[no]class Show DNS class.\n" + " +[no]ttl Show TTL value.\n" + " +[no]crypto Show binary parts of RRSIGs and DNSKEYs.\n" + " +[no]tcp Use TCP protocol.\n" + " +[no]fastopen Use TCP Fast Open.\n" + " +[no]ignore Don't use TCP automatically if truncated.\n" + " +[no]tls Use TLS with Opportunistic privacy profile.\n" + " +[no]tls-ca[=FILE] Use TLS with Out-Of-Band privacy profile.\n" + " +[no]tls-pin=BASE64 Use TLS with pinned certificate.\n" + " +[no]tls-hostname=STR Use TLS with remote server hostname.\n" + " +[no]tls-sni=STR Use TLS with Server Name Indication.\n" + " +[no]nsid Request NSID.\n" + " +[no]bufsize=B Set EDNS buffer size.\n" + " +[no]padding[=N] Pad with EDNS(0) (default or specify size).\n" + " +[no]alignment[=N] Pad with EDNS(0) to blocksize (%u or specify size).\n" + " +[no]subnet=SUBN Set EDNS(0) client subnet addr/prefix.\n" + " +[no]edns[=N] Use EDNS(=version).\n" + " +[no]timeout=T Set wait for reply interval in seconds.\n" + " +[no]retry=N Set number of retries.\n" + " +[no]cookie=HEX Attach EDNS(0) cookie to the query.\n" + " +[no]badcookie Repeat a query with the correct cookie.\n" + " +[no]ednsopt=CODE[:HEX] Set custom EDNS option.\n" + " +noidn Disable IDN transformation.\n" + "\n" + " -h, --help Print the program help.\n" + " -V, --version Print the program version.\n", + PROGRAM_NAME, DEFAULT_ALIGNMENT_SIZE); +} + +static int parse_opt1(const char *opt, const char *value, kdig_params_t *params, + int *index) +{ + const char *val = value; + size_t len = strlen(opt); + int add = 1; + query_t *query; + + // Set current query (last or config). + if (list_size(¶ms->queries) > 0) { + query = TAIL(params->queries); + } else { + query = params->config; + } + + // If there is no space between option and argument. + if (len > 1) { + val = opt + 1; + add = 0; + } + + switch (opt[0]) { + case '4': + if (len > 1) { + ERR("invalid option -%s\n", opt); + return KNOT_ENOTSUP; + } + + query->ip = IP_4; + break; + case '6': + if (len > 1) { + ERR("invalid option -%s\n", opt); + return KNOT_ENOTSUP; + } + + query->ip = IP_6; + break; + case 'b': + if (val == NULL) { + ERR("missing address\n"); + return KNOT_EINVAL; + } + + if (parse_local(val, query) != KNOT_EOK) { + ERR("bad address %s\n", val); + return KNOT_EINVAL; + } + *index += add; + break; + case 'd': + msg_enable_debug(1); + break; + case 'h': + if (len > 1) { + ERR("invalid option -%s\n", opt); + return KNOT_ENOTSUP; + } + + print_help(); + params->stop = true; + break; + case 'c': + if (val == NULL) { + ERR("missing class\n"); + return KNOT_EINVAL; + } + + if (parse_class(val, query) != KNOT_EOK) { + ERR("bad class %s\n", val); + return KNOT_EINVAL; + } + *index += add; + break; + case 'k': + if (val == NULL) { + ERR("missing filename\n"); + return KNOT_EINVAL; + } + + if (parse_keyfile(val, query) != KNOT_EOK) { + ERR("bad keyfile %s\n", value); + return KNOT_EINVAL; + } + *index += add; + break; + case 'p': + if (val == NULL) { + ERR("missing port\n"); + return KNOT_EINVAL; + } + + if (parse_port(val, query) != KNOT_EOK) { + ERR("bad port %s\n", value); + return KNOT_EINVAL; + } + *index += add; + break; + case 'q': + // Allow empty QNAME. + if (parse_name(val, ¶ms->queries, params->config) + != KNOT_EOK) { + ERR("bad query name %s\n", val); + return KNOT_EINVAL; + } + *index += add; + break; + case 't': + if (val == NULL) { + ERR("missing type\n"); + return KNOT_EINVAL; + } + + if (parse_type(val, query) != KNOT_EOK) { + ERR("bad type %s\n", val); + return KNOT_EINVAL; + } + *index += add; + break; + case 'V': + if (len > 1) { + ERR("invalid option -%s\n", opt); + return KNOT_ENOTSUP; + } + + print_version(PROGRAM_NAME); + params->stop = true; + break; + case 'x': + if (val == NULL) { + ERR("missing address\n"); + return KNOT_EINVAL; + } + + if (parse_reverse(val, ¶ms->queries, params->config) + != KNOT_EOK) { + ERR("bad reverse name %s\n", val); + return KNOT_EINVAL; + } + *index += add; + break; + case 'y': + if (val == NULL) { + ERR("missing key\n"); + return KNOT_EINVAL; + } + + if (parse_tsig(val, query) != KNOT_EOK) { + ERR("bad key %s\n", value); + return KNOT_EINVAL; + } + *index += add; + break; + case 'E': +#if USE_DNSTAP + if (val == NULL) { + ERR("missing filename\n"); + return KNOT_EINVAL; + } + + if (parse_dnstap_output(val, query) != KNOT_EOK) { + ERR("unable to open dnstap output file %s\n", val); + return KNOT_EINVAL; + } + *index += add; +#else + ERR("no dnstap support but -E specified\n"); + return KNOT_EINVAL; +#endif // USE_DNSTAP + break; + case 'G': +#if USE_DNSTAP + if (val == NULL) { + ERR("missing filename\n"); + return KNOT_EINVAL; + } + + query = query_create(NULL, params->config); + if (query == NULL) { + return KNOT_ENOMEM; + } + + if (parse_dnstap_input(val, query) != KNOT_EOK) { + ERR("unable to open dnstap input file %s\n", val); + query_free(query); + return KNOT_EINVAL; + } + + query->operation = OPERATION_LIST_DNSTAP; + add_tail(¶ms->queries, (node_t *)query); + + *index += add; +#else + ERR("no dnstap support but -G specified\n"); + return KNOT_EINVAL; +#endif // USE_DNSTAP + break; + case '-': + if (strcmp(opt, "-help") == 0) { + print_help(); + params->stop = true; + } else if (strcmp(opt, "-version") == 0) { + print_version(PROGRAM_NAME); + params->stop = true; + } else { + ERR("invalid option: -%s\n", opt); + return KNOT_ENOTSUP; + } + break; + default: + ERR("invalid option: -%s\n", opt); + return KNOT_ENOTSUP; + } + + return KNOT_EOK; +} + +static int parse_opt2(const char *value, kdig_params_t *params) +{ + query_t *query; + + // Set current query (last or config). + if (list_size(¶ms->queries) > 0) { + query = TAIL(params->queries); + } else { + query = params->config; + } + + // Get option name. + const char *arg_sep = "="; + size_t opt_len = strcspn(value, arg_sep); + if (opt_len < 1) { + ERR("invalid option: +%s\n", value); + return KNOT_ENOTSUP; + } + + // Get option argument if any. + const char *arg = NULL; + const char *rest = value + opt_len; + if (strlen(rest) > 0) { + arg = rest + strspn(rest, arg_sep); + } + + // Check if the given option is supported. + bool unique; + int ret = best_param(value, opt_len, kdig_opts2, &unique); + if (ret < 0) { + ERR("invalid option: +%s\n", value); + return KNOT_ENOTSUP; + } else if (!unique) { + ERR("ambiguous option: +%s\n", value); + return KNOT_ENOTSUP; + } + + // Check argument presence. + switch (kdig_opts2[ret].arg) { + case ARG_NONE: + if (arg != NULL && *arg != '\0') { + WARN("superfluous option argument: +%s\n", value); + } + break; + case ARG_REQUIRED: + if (arg == NULL) { + ERR("missing argument: +%s\n", value); + return KNOT_EFEWDATA; + } + // FALLTHROUGH + case ARG_OPTIONAL: + if (arg != NULL && *arg == '\0') { + ERR("empty argument: +%s\n", value); + return KNOT_EFEWDATA; + } + break; + } + + // Call option handler. + return kdig_opts2[ret].handler(arg, query); +} + +static int parse_token(const char *value, kdig_params_t *params) +{ + query_t *query; + + // Set current query (last or config). + if (list_size(¶ms->queries) > 0) { + query = TAIL(params->queries); + } else { + query = params->config; + } + + // Try to guess the meaning of the token. + if (strlen(value) == 0) { + ERR("invalid empty parameter\n"); + } else if (parse_type(value, query) == KNOT_EOK) { + return KNOT_EOK; + } else if (parse_class(value, query) == KNOT_EOK) { + return KNOT_EOK; + } else if (parse_name(value, ¶ms->queries, params->config) == KNOT_EOK) { + return KNOT_EOK; + } else { + ERR("invalid parameter: %s\n", value); + } + + return KNOT_EINVAL; +} + +int kdig_parse(kdig_params_t *params, int argc, char *argv[]) +{ + if (params == NULL || argv == NULL) { + DBG_NULL; + return KNOT_EINVAL; + } + + // Initialize parameters. + if (kdig_init(params) != KNOT_EOK) { + return KNOT_ERROR; + } + +#ifdef LIBIDN + // Set up localization. + if (setlocale(LC_CTYPE, "") == NULL) { + WARN("can't setlocale, disabling IDN\n"); + params->config->idn = false; + params->config->style.style.ascii_to_idn = NULL; + } +#endif + + // Command line parameters processing. + for (int i = 1; i < argc; i++) { + int ret = KNOT_ERROR; + + // Process parameter. + switch (argv[i][0]) { + case '@': + ret = parse_server(argv[i] + 1, params); + break; + case '-': + ret = parse_opt1(argv[i] + 1, argv[i + 1], params, &i); + break; + case '+': + ret = parse_opt2(argv[i] + 1, params); + break; + default: + ret = parse_token(argv[i], params); + break; + } + + // Check return. + switch (ret) { + case KNOT_EOK: + if (params->stop) { + return KNOT_EOK; + } + break; + case KNOT_ENOTSUP: + print_help(); + default: // Fall through. + return ret; + } + } + + // Complete missing data in queries based on defaults. + complete_queries(¶ms->queries, params->config); + + return KNOT_EOK; +} diff --git a/src/utils/kdig/kdig_params.h b/src/utils/kdig/kdig_params.h new file mode 100644 index 0000000..17907ab --- /dev/null +++ b/src/utils/kdig/kdig_params.h @@ -0,0 +1,176 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <stdbool.h> + +#include "utils/common/params.h" +#include "utils/common/exec.h" +#include "utils/common/sign.h" +#include "libknot/libknot.h" +#include "contrib/sockaddr.h" + +#if USE_DNSTAP +# include "contrib/dnstap/reader.h" +# include "contrib/dnstap/writer.h" +#endif // USE_DNSTAP + +/*! \brief Operation mode of kdig. */ +typedef enum { + /*!< Standard 1-message query/reply. */ + OPERATION_QUERY, + /*!< Zone transfer (AXFR or IXFR). */ + OPERATION_XFR, + /*!< Dump dnstap file. */ + OPERATION_LIST_DNSTAP, + /*!< Query for NS and all authoritative SOA records. */ + OPERATION_LIST_SOA +} operation_t; + +/*! \brief DNS header and EDNS flags. */ +typedef struct { + /*!< Authoritative answer flag. */ + bool aa_flag; + /*!< Truncated flag. */ + bool tc_flag; + /*!< Recursion desired flag. */ + bool rd_flag; + /*!< Recursion available flag. */ + bool ra_flag; + /*!< Z flag. */ + bool z_flag; + /*!< Authenticated data flag. */ + bool ad_flag; + /*!< Checking disabled flag. */ + bool cd_flag; + /*!< DNSSEC OK flag. */ + bool do_flag; +} flags_t; + +/*! \brief Basic parameters for DNS query. */ +typedef struct query query_t; // Forward declaration due to configuration. +struct query { + /*!< List node (for list container). */ + node_t n; + /*!< Reference to global config. */ + const query_t *conf; + /*!< Name to query on. */ + char *owner; + /*!< List of nameservers to query to. */ + list_t servers; + /*!< Local interface (optional). */ + srv_info_t *local; + /*!< Operation mode. */ + operation_t operation; + /*!< Version of ip protocol to use. */ + ip_t ip; + /*!< Protocol type (TCP, UDP) to use. */ + protocol_t protocol; + /*!< Use TCP Fast Open. */ + bool fastopen; + /*!< Port/service to connect to. */ + char *port; + /*!< UDP buffer size (16unsigned + -1 uninitialized). */ + int32_t udp_size; + /*!< Number of UDP retries. */ + uint32_t retries; + /*!< Wait for network response in seconds (-1 means forever). */ + int32_t wait; + /*!< Ignore truncated response. */ + bool ignore_tc; + /*!< Class number (16unsigned + -1 uninitialized). */ + int32_t class_num; + /*!< Type number (16unsigned + -1 uninitialized). */ + int32_t type_num; + /*!< SOA serial for IXFR and NOTIFY (32unsigned + -1 uninitialized). */ + int64_t serial; + /*!< NOTIFY query. */ + bool notify; + /*!< Header flags. */ + flags_t flags; + /*!< Output settings. */ + style_t style; + /*!< IDN conversion. */ + bool idn; + /*!< Query for NSID. */ + bool nsid; + /*!< EDNS version (8unsigned + -1 uninitialized). */ + int16_t edns; + /*!< EDNS client cookie. */ + knot_edns_cookie_t cc; + /*!< EDNS server cookie. */ + knot_edns_cookie_t sc; + /*!< Repeat query after BADCOOKIE. */ + bool badcookie; + /*!< EDNS0 padding (16unsigned + -1 ~ uninitialized, -2 ~ default, -3 ~ none). */ + int32_t padding; + /*!< Query alignment with EDNS0 padding (0 ~ uninitialized). */ + uint16_t alignment; + /*!< TLS parameters. */ + tls_params_t tls; + /*!< Transaction signature. */ + knot_tsig_key_t tsig_key; + /*!< EDNS client subnet. */ + knot_edns_client_subnet_t subnet; + /*!< Lits of custom EDNS options. */ + list_t edns_opts; +#if USE_DNSTAP + /*!< Context for dnstap reader input. */ + dt_reader_t *dt_reader; + /*!< Context for dnstap writer output. */ + dt_writer_t *dt_writer; +#endif // USE_DNSTAP +}; + +/*! \brief EDNS option data. */ +typedef struct { + /*! List node (for list container). */ + node_t n; + /*!< OPTION-CODE field. */ + uint16_t code; + /*!< OPTION-LENGTH field. */ + uint16_t length; + /*!< OPTION-DATA field. */ + uint8_t *data; +} ednsopt_t; + +/*! \brief Settings for kdig. */ +typedef struct { + /*!< Stop processing - just print help, version,... */ + bool stop; + /*!< List of DNS queries to process. */ + list_t queries; + /*!< Default settings for queries. */ + query_t *config; +} kdig_params_t; + +query_t *query_create(const char *owner, const query_t *config); +void query_free(query_t *query); +void complete_queries(list_t *queries, const query_t *conf); + +ednsopt_t *ednsopt_create(uint16_t code, uint16_t length, uint8_t *data); +ednsopt_t *ednsopt_dup(const ednsopt_t *opt); +void ednsopt_free(ednsopt_t *opt); + +void ednsopt_list_init(list_t *list); +void ednsopt_list_deinit(list_t *list); +int ednsopt_list_dup(list_t *dst, const list_t *src); +bool ednsopt_list_empty(const list_t *list); + +int kdig_init(kdig_params_t *params); +int kdig_parse(kdig_params_t *params, int argc, char *argv[]); +void kdig_clean(kdig_params_t *params); diff --git a/src/utils/keymgr/bind_privkey.c b/src/utils/keymgr/bind_privkey.c new file mode 100644 index 0000000..7e140e4 --- /dev/null +++ b/src/utils/keymgr/bind_privkey.c @@ -0,0 +1,352 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <string.h> + +#include "contrib/ctype.h" +#include "contrib/strtonum.h" +#include "libdnssec/binary.h" +#include "libdnssec/error.h" +#include "libdnssec/shared/pem.h" +#include "libdnssec/shared/shared.h" +#include "utils/keymgr/bind_privkey.h" + +/* -- private key params conversion ---------------------------------------- */ + +/*! + * Private key attribute conversion. + */ +typedef struct param_t { + char *name; + size_t offset; + int (*parse_cb)(char *string, void *data); + void (*free_cb)(void *data); +} param_t; + +static int parse_algorithm(char *string, void *_algorithm); +static int parse_binary(char *string, void *_binary); +static int parse_time(char *string, void *_time); + +static void binary_free(void *_binary) +{ + dnssec_binary_t *binary = _binary; + dnssec_binary_free(binary); +} + +/*! + * Know attributes in private key file. + */ +const param_t PRIVKEY_CONVERSION_TABLE[] = { + #define o(field) offsetof(bind_privkey_t, field) + { "Algorithm", o(algorithm), parse_algorithm, NULL }, + { "Modulus", o(modulus), parse_binary, binary_free }, + { "PublicExponent", o(public_exponent), parse_binary, binary_free }, + { "PrivateExponent", o(private_exponent), parse_binary, binary_free }, + { "Prime1", o(prime_one), parse_binary, binary_free }, + { "Prime2", o(prime_two), parse_binary, binary_free }, + { "Exponent1", o(exponent_one), parse_binary, binary_free }, + { "Exponent2", o(exponent_two), parse_binary, binary_free }, + { "Coefficient", o(coefficient), parse_binary, binary_free }, + { "PrivateKey", o(private_key), parse_binary, binary_free }, + { "Created", o(time_created), parse_time, NULL }, + { "Publish", o(time_publish), parse_time, NULL }, + { "Activate", o(time_activate), parse_time, NULL }, + { "Revoke", o(time_revoke), parse_time, NULL }, + { "Inactive", o(time_inactive), parse_time, NULL }, + { "Delete", o(time_delete), parse_time, NULL }, + { NULL } + #undef o +}; + +/* -- attribute parsing ---------------------------------------------------- */ + +/*! + * Parse key algorithm field. + * + * Example: 7 (NSEC3RSASHA1) + * + * Only the numeric value is decoded, the rest of the value is ignored. + */ +static int parse_algorithm(char *string, void *_algorithm) +{ + char *end = string; + while (*end != '\0' && !is_space(*end)) { + end += 1; + } + *end = '\0'; + + uint8_t *algorithm = _algorithm; + int r = str_to_u8(string, algorithm); + + return (r == KNOT_EOK ? DNSSEC_EOK : DNSSEC_INVALID_KEY_ALGORITHM); +} + +/*! + * Parse binary data encoded in Base64. + * + * Example: AQAB + */ +static int parse_binary(char *string, void *_binary) +{ + dnssec_binary_t base64 = { + .data = (uint8_t *)string, + .size = strlen(string) + }; + + dnssec_binary_t *binary = _binary; + return dnssec_binary_from_base64(&base64, binary); +} + +#define LEGACY_DATE_FORMAT "%Y%m%d%H%M%S" + +/*! + * Parse timestamp in a format in \ref LEGACY_DATE_FORMAT. + * + * Example: 20140415151855 + */ +static int parse_time(char *string, void *_time) +{ + struct tm tm = { 0 }; + + char *end = strptime(string, LEGACY_DATE_FORMAT, &tm); + if (end == NULL || *end != '\0') { + return DNSSEC_MALFORMED_DATA; + } + + time_t *time = _time; + *time = timegm(&tm); + + return DNSSEC_EOK; +} + +/* -- key parsing ---------------------------------------------------------- */ + +/*! + * Strip string value of left and right whitespaces. + * + * \param[in,out] value Start of the string. + * \param[in,out] length Length of the string. + * + */ +static void strip(char **value, size_t *length) +{ + // strip from left + while (*length > 0 && is_space(**value)) { + *value += 1; + *length -= 1; + } + // strip from right + while (*length > 0 && is_space((*value)[*length - 1])) { + *length -= 1; + } +} + +/*! + * Parse one line of the private key file. + */ +static int parse_line(bind_privkey_t *params, char *line, size_t length) +{ + assert(params); + assert(line); + + char *separator = memchr(line, ':', length); + if (!separator) { + return DNSSEC_MALFORMED_DATA; + } + + char *key = line; + size_t key_length = separator - key; + strip(&key, &key_length); + + char *value = separator + 1; + size_t value_length = (line + length) - value; + strip(&value, &value_length); + + if (key_length == 0 || value_length == 0) { + return DNSSEC_MALFORMED_DATA; + } + + key[key_length] = '\0'; + value[value_length] = '\0'; + + for (const param_t *p = PRIVKEY_CONVERSION_TABLE; p->name != NULL; p++) { + size_t name_length = strlen(p->name); + if (name_length != key_length) { + continue; + } + + if (strcasecmp(p->name, key) != 0) { + continue; + } + + return p->parse_cb(value, (void *)params + p->offset); + } + + // ignore unknown attributes + + return DNSSEC_EOK; +} + +int bind_privkey_parse(const char *filename, bind_privkey_t *params_ptr) +{ + _cleanup_fclose_ FILE *file = fopen(filename, "r"); + if (!file) { + return DNSSEC_NOT_FOUND; + } + + bind_privkey_t params = { 0 }; + + _cleanup_free_ char *line = NULL; + size_t size = 0; + ssize_t read = 0; + while ((read = getline(&line, &size, file)) != -1) { + int r = parse_line(¶ms, line, read); + if (r != DNSSEC_EOK) { + bind_privkey_free(¶ms); + return r; + } + } + + *params_ptr = params; + + return DNSSEC_EOK; +} + +/* -- freeing -------------------------------------------------------------- */ + +/*! + * Free private key parameters. + */ +void bind_privkey_free(bind_privkey_t *params) +{ + if (!params) { + return; + } + + for (const param_t *p = PRIVKEY_CONVERSION_TABLE; p->name != NULL; p++) { + if (p->free_cb) { + p->free_cb((void *)params + p->offset); + } + } + + clear_struct(params); +} + +/* -- export to PEM -------------------------------------------------------- */ + +static int rsa_params_to_pem(const bind_privkey_t *params, dnssec_binary_t *pem) +{ + _cleanup_x509_privkey_ gnutls_x509_privkey_t key = NULL; + int result = gnutls_x509_privkey_init(&key); + if (result != GNUTLS_E_SUCCESS) { + return DNSSEC_ENOMEM; + } + + gnutls_datum_t m = binary_to_datum(¶ms->modulus); + gnutls_datum_t e = binary_to_datum(¶ms->public_exponent); + gnutls_datum_t d = binary_to_datum(¶ms->private_exponent); + gnutls_datum_t p = binary_to_datum(¶ms->prime_one); + gnutls_datum_t q = binary_to_datum(¶ms->prime_two); + gnutls_datum_t u = binary_to_datum(¶ms->coefficient); + + result = gnutls_x509_privkey_import_rsa_raw(key, &m, &e, &d, &p, &q, &u); + if (result != GNUTLS_E_SUCCESS) { + return DNSSEC_KEY_IMPORT_ERROR; + } + + return pem_from_x509(key, pem); +} + +/*! + * \see lib/key/convert.h + */ +static gnutls_ecc_curve_t choose_ecdsa_curve(size_t pubkey_size) +{ + switch (pubkey_size) { + case 64: return GNUTLS_ECC_CURVE_SECP256R1; + case 96: return GNUTLS_ECC_CURVE_SECP384R1; + default: return GNUTLS_ECC_CURVE_INVALID; + } +} + +static void ecdsa_extract_public_params(dnssec_key_t *key, gnutls_ecc_curve_t *curve, + gnutls_datum_t *x, gnutls_datum_t *y) +{ + dnssec_binary_t pubkey = { 0 }; + dnssec_key_get_pubkey(key, &pubkey); + + *curve = choose_ecdsa_curve(pubkey.size); + + size_t param_size = pubkey.size / 2; + x->data = pubkey.data; + x->size = param_size; + y->data = pubkey.data + param_size; + y->size = param_size; +} + +static int ecdsa_params_to_pem(dnssec_key_t *dnskey, const bind_privkey_t *params, + dnssec_binary_t *pem) +{ + _cleanup_x509_privkey_ gnutls_x509_privkey_t key = NULL; + int result = gnutls_x509_privkey_init(&key); + if (result != GNUTLS_E_SUCCESS) { + return DNSSEC_ENOMEM; + } + + gnutls_ecc_curve_t curve = 0; + gnutls_datum_t x = { 0 }; + gnutls_datum_t y = { 0 }; + ecdsa_extract_public_params(dnskey, &curve, &x, &y); + + gnutls_datum_t k = binary_to_datum(¶ms->private_key); + + result = gnutls_x509_privkey_import_ecc_raw(key, curve, &x, &y, &k); + if (result != DNSSEC_EOK) { + return DNSSEC_KEY_IMPORT_ERROR; + } + + gnutls_x509_privkey_fix(key); + + return pem_from_x509(key, pem); +} + +int bind_privkey_to_pem(dnssec_key_t *key, bind_privkey_t *params, dnssec_binary_t *pem) +{ + dnssec_key_algorithm_t algorithm = dnssec_key_get_algorithm(key); + switch (algorithm) { + case DNSSEC_KEY_ALGORITHM_RSA_SHA1: + case DNSSEC_KEY_ALGORITHM_RSA_SHA1_NSEC3: + case DNSSEC_KEY_ALGORITHM_RSA_SHA256: + case DNSSEC_KEY_ALGORITHM_RSA_SHA512: + return rsa_params_to_pem(params, pem); + case DNSSEC_KEY_ALGORITHM_ECDSA_P256_SHA256: + case DNSSEC_KEY_ALGORITHM_ECDSA_P384_SHA384: + return ecdsa_params_to_pem(key, params, pem); + default: + return DNSSEC_INVALID_KEY_ALGORITHM; + } +} + +void bind_privkey_to_timing(bind_privkey_t *params, knot_kasp_key_timing_t *timing) +{ + // unsupported: time_created, time_revoke + + timing->publish = (knot_time_t)params->time_publish; + timing->ready = 0; + timing->active = (knot_time_t)params->time_activate; + timing->retire = (knot_time_t)params->time_inactive; + timing->remove = (knot_time_t)params->time_delete; +} diff --git a/src/utils/keymgr/bind_privkey.h b/src/utils/keymgr/bind_privkey.h new file mode 100644 index 0000000..6c58e1d --- /dev/null +++ b/src/utils/keymgr/bind_privkey.h @@ -0,0 +1,72 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include <stdint.h> +#include <time.h> + +#include "libdnssec/binary.h" +#include "knot/dnssec/kasp/policy.h" + +/*! + * Legacy private key parameters. + */ +typedef struct { + // key information + uint8_t algorithm; + + // RSA + dnssec_binary_t modulus; + dnssec_binary_t public_exponent; + dnssec_binary_t private_exponent; + dnssec_binary_t prime_one; + dnssec_binary_t prime_two; + dnssec_binary_t exponent_one; + dnssec_binary_t exponent_two; + dnssec_binary_t coefficient; + + // ECDSA + dnssec_binary_t private_key; + + // key lifetime + time_t time_created; + time_t time_publish; + time_t time_activate; + time_t time_revoke; + time_t time_inactive; + time_t time_delete; +} bind_privkey_t; + +/*! + * Extract parameters from legacy private key file. + */ +int bind_privkey_parse(const char *filename, bind_privkey_t *params); + +/*! + * Free private key parameters. + */ +void bind_privkey_free(bind_privkey_t *params); + +/*! + * Generate PEM from pub&priv key. + */ +int bind_privkey_to_pem(dnssec_key_t *key, bind_privkey_t *params, dnssec_binary_t *pem); + +/*! + * Extract timing info. + */ +void bind_privkey_to_timing(bind_privkey_t *params, knot_kasp_key_timing_t *timing); diff --git a/src/utils/keymgr/functions.c b/src/utils/keymgr/functions.c new file mode 100644 index 0000000..3672928 --- /dev/null +++ b/src/utils/keymgr/functions.c @@ -0,0 +1,879 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <limits.h> +#include <string.h> +#include <strings.h> +#include <time.h> +#include <fcntl.h> + +#include "utils/keymgr/functions.h" +#include "utils/keymgr/bind_privkey.h" +#include "contrib/base64.h" +#include "contrib/ctype.h" +#include "contrib/tolower.h" +#include "contrib/wire_ctx.h" +#include "libdnssec/error.h" +#include "libdnssec/shared/hex.h" +#include "libdnssec/shared/shared.h" +#include "knot/dnssec/kasp/policy.h" +#include "knot/dnssec/zone-keys.h" +#include "libzscanner/scanner.h" + +static bool is_timestamp(char *arg, knot_kasp_key_timing_t *timing) +{ + knot_time_t *dst = NULL; + + if (strncasecmp(arg, "created=", 8) == 0) { + dst = &timing->created; + } else if (strncasecmp(arg, "publish=", 8) == 0) { + dst = &timing->publish; + } else if (strncasecmp(arg, "ready=", 6) == 0) { + dst = &timing->ready; + } else if (strncasecmp(arg, "active=", 7) == 0) { + dst = &timing->active; + } else if (strncasecmp(arg, "retire=", 7) == 0) { + dst = &timing->retire; + } else if (strncasecmp(arg, "remove=", 7) == 0) { + dst = &timing->remove; + } else if (strncasecmp(arg, "pre_active=", 11) == 0) { + dst = &timing->pre_active; + } else if (strncasecmp(arg, "post_active=", 12) == 0) { + dst = &timing->post_active; + } else if (strncasecmp(arg, "retire_active=", 14) == 0) { + dst = &timing->retire_active; + } else { + return false; + } + + knot_time_t stamp; + int ret = knot_time_parse("YMDhms|'now'+-#u|'t'+-#u|+-#u|'t'+-#|+-#|#", + strchr(arg, '=') + 1, &stamp); + if (ret < 0) { + printf("Invalid timestamp: %s\n", arg); + return true; + } + + *dst = stamp; + + return true; +} + +static bool str2bool(const char *s) +{ + switch (knot_tolower(s[0])) { + case '1': + case 'y': + case 't': + return true; + default: + return false; + } +} + +static void bitmap_set(kdnssec_generate_flags_t *bitmap, int flag, bool onoff) +{ + if (onoff) { + *bitmap |= flag; + } else { + *bitmap &= ~flag; + } +} + +static bool genkeyargs(int argc, char *argv[], bool just_timing, + kdnssec_generate_flags_t *flags, dnssec_key_algorithm_t *algorithm, + uint16_t *keysize, knot_kasp_key_timing_t *timing, + const char **addtopolicy) +{ + // generate algorithms field + char *algnames[256] = { 0 }; + algnames[DNSSEC_KEY_ALGORITHM_RSA_SHA1] = "rsasha1"; + algnames[DNSSEC_KEY_ALGORITHM_RSA_SHA1_NSEC3] = "rsasha1nsec3sha1"; + algnames[DNSSEC_KEY_ALGORITHM_RSA_SHA256] = "rsasha256"; + algnames[DNSSEC_KEY_ALGORITHM_RSA_SHA512] = "rsasha512"; + algnames[DNSSEC_KEY_ALGORITHM_ECDSA_P256_SHA256] = "ecdsap256sha256"; + algnames[DNSSEC_KEY_ALGORITHM_ECDSA_P384_SHA384] = "ecdsap384sha384"; + algnames[DNSSEC_KEY_ALGORITHM_ED25519] = "ed25519"; + + // parse args + for (int i = 0; i < argc; i++) { + if (!just_timing && strncasecmp(argv[i], "algorithm=", 10) == 0) { + if (is_digit(argv[i][10]) && atol(argv[i] + 10) < 256) { + *algorithm = atol(argv[i] + 10); + continue; + } + int al; + for (al = 0; al < 256; al++) { + if (algnames[al] != NULL && + strcasecmp(argv[i] + 10, algnames[al]) == 0) { + *algorithm = al; + break; + } + } + if (al == 256) { + printf("Unknown algorithm: %s\n", argv[i] + 10); + return false; + } + } else if (strncasecmp(argv[i], "ksk=", 4) == 0) { + bitmap_set(flags, DNSKEY_GENERATE_KSK, str2bool(argv[i] + 4)); + } else if (strncasecmp(argv[i], "zsk=", 4) == 0) { + bitmap_set(flags, DNSKEY_GENERATE_ZSK, str2bool(argv[i] + 4)); + } else if (!just_timing && strncasecmp(argv[i], "sep=", 4) == 0) { + bitmap_set(flags, DNSKEY_GENERATE_SEP_SPEC, true); + bitmap_set(flags, DNSKEY_GENERATE_SEP_ON, str2bool(argv[i] + 4)); + } else if (!just_timing && strncasecmp(argv[i], "size=", 5) == 0) { + *keysize = atol(argv[i] + 5); + } else if (!just_timing && strncasecmp(argv[i], "addtopolicy=", 12) == 0) { + *addtopolicy = argv[i] + 12; + } else if (!is_timestamp(argv[i], timing)) { + printf("Invalid parameter: %s\n", argv[i]); + return false; + } + } + + return true; +} + +static bool _check_lower(knot_time_t a, knot_time_t b, + const char *a_name, const char *b_name) +{ + if (knot_time_cmp(a, b) > 0) { + fprintf(stderr, "Semantic error: expected '%s' before '%s'.\n", a_name, b_name); + return false; + } + return true; +} + +#define check_lower(t, a, b) if (!_check_lower(t->a, t->b, #a, #b)) return KNOT_ESEMCHECK + +static int check_timers(const knot_kasp_key_timing_t *t) +{ + check_lower(t, publish, active); + check_lower(t, active, retire_active); + check_lower(t, active, retire); + check_lower(t, active, post_active); + if (t->post_active == 0) { + check_lower(t, retire, remove); + } + return KNOT_EOK; +} + +#undef check_lower + +// modifies ctx->policy options, so don't do anything afterwards ! +int keymgr_generate_key(kdnssec_ctx_t *ctx, int argc, char *argv[]) +{ + knot_time_t now = knot_time(), infty = 0; + knot_kasp_key_timing_t gen_timing = { now, infty, now, infty, now, infty, infty, infty, infty }; + kdnssec_generate_flags_t flags = 0; + uint16_t keysize = 0; + const char *addtopolicy = NULL; + if (!genkeyargs(argc, argv, false, &flags, &ctx->policy->algorithm, + &keysize, &gen_timing, &addtopolicy)) { + return KNOT_EINVAL; + } + + int ret = check_timers(&gen_timing); + if (ret != KNOT_EOK) { + return ret; + } + + if ((flags & DNSKEY_GENERATE_KSK) && gen_timing.ready == infty) { + gen_timing.ready = gen_timing.active; + } + + if (keysize > 0) { + if ((flags & DNSKEY_GENERATE_KSK)) { + ctx->policy->ksk_size = keysize; + } else { + ctx->policy->zsk_size = keysize; + } + } + + for (size_t i = 0; i < ctx->zone->num_keys; i++) { + knot_kasp_key_t *kasp_key = &ctx->zone->keys[i]; + if ((kasp_key->is_ksk && (flags & DNSKEY_GENERATE_KSK)) && + dnssec_key_get_algorithm(kasp_key->key) != ctx->policy->algorithm) { + printf("warning: creating key with different algorithm than " + "configured in the policy\n"); + break; + } + } + + knot_kasp_key_t *key = NULL; + ret = kdnssec_generate_key(ctx, flags, &key); + if (ret != KNOT_EOK) { + return ret; + } + + key->timing = gen_timing; + + if (addtopolicy != NULL) { + char *last_policy_last = NULL; + + knot_dname_t *unused = NULL; + ret = kasp_db_get_policy_last(*ctx->kasp_db, addtopolicy, &unused, + &last_policy_last); + knot_dname_free(unused, NULL); + if (ret != KNOT_EOK && ret != KNOT_ENOENT) { + return ret; + } + + ret = kasp_db_set_policy_last(*ctx->kasp_db, addtopolicy, last_policy_last, + ctx->zone->dname, key->id); + free(last_policy_last); + if (ret != KNOT_EOK) { + return ret; + } + } + + ret = kdnssec_ctx_commit(ctx); + + if (ret == KNOT_EOK) { + printf("%s\n", key->id); + } + + return ret; +} + +static void parse_record(zs_scanner_t *scanner) +{ + dnssec_key_t *key = scanner->process.data; + + if (dnssec_key_get_dname(key) != NULL || + scanner->r_type != KNOT_RRTYPE_DNSKEY) { + scanner->state = ZS_STATE_STOP; + return; + } + + dnssec_binary_t rdata = { + .data = scanner->r_data, + .size = scanner->r_data_length + }; + dnssec_key_set_dname(key, scanner->dname); + dnssec_key_set_rdata(key, &rdata); +} + +int bind_pubkey_parse(const char *filename, dnssec_key_t **key_ptr) +{ + dnssec_key_t *key = NULL; + int result = dnssec_key_new(&key); + if (result != DNSSEC_EOK) { + return KNOT_ENOMEM; + } + + uint16_t cls = KNOT_CLASS_IN; + uint32_t ttl = 0; + zs_scanner_t *scanner = malloc(sizeof(zs_scanner_t)); + if (scanner == NULL) { + dnssec_key_free(key); + return KNOT_ENOMEM; + } + + if (zs_init(scanner, ".", cls, ttl) != 0 || + zs_set_input_file(scanner, filename) != 0 || + zs_set_processing(scanner, parse_record, NULL, key) != 0 || + zs_parse_all(scanner) != 0) { + zs_deinit(scanner); + free(scanner); + dnssec_key_free(key); + return KNOT_ENOENT; + } + zs_deinit(scanner); + free(scanner); + + if (dnssec_key_get_dname(key) == NULL) { + dnssec_key_free(key); + return KNOT_INVALID_PUBLIC_KEY; + } + + *key_ptr = key; + return KNOT_EOK; +} + +static char *genname(const char *orig, const char *wantsuff, const char *altsuff) +{ + char *res; + if (orig == NULL || wantsuff == NULL || altsuff == NULL || + (res = malloc(strlen(orig) + strlen(wantsuff) + 1)) == NULL) { + return NULL; + } + strcpy(res, orig); + char *dot = strrchr(res, '.'); + if (dot != NULL && strcmp(dot, wantsuff) == 0) { + ; + } else if (dot != NULL && strcmp(dot, altsuff) == 0) { + strcpy(dot, wantsuff); + } else { + strcat(res, wantsuff); + } + return res; +} + +int keymgr_import_bind(kdnssec_ctx_t *ctx, const char *import_file, bool pub_only) +{ + if (ctx == NULL || import_file == NULL) { + return KNOT_EINVAL; + } + + knot_kasp_key_timing_t timing = { 0 }; + dnssec_key_t *key = NULL; + char *keyid = NULL; + + char *pubname = genname(import_file, ".key", ".private"); + if (pubname == NULL) { + return KNOT_EINVAL; + } + + int ret = bind_pubkey_parse(pubname, &key); + free(pubname); + if (ret != KNOT_EOK) { + goto fail; + } + + if (!pub_only) { + bind_privkey_t bpriv = { 0 }; + + char *privname = genname(import_file, ".private", ".key"); + if (privname == NULL) { + goto fail; + } + + ret = bind_privkey_parse(privname, &bpriv); + free(privname); + if (ret != DNSSEC_EOK) { + goto fail; + } + + dnssec_binary_t pem = { 0 }; + ret = bind_privkey_to_pem(key, &bpriv, &pem); + if (ret != DNSSEC_EOK) { + bind_privkey_free(&bpriv); + goto fail; + } + + bind_privkey_to_timing(&bpriv, &timing); // time created remains always zero + + bind_privkey_free(&bpriv); + + ret = dnssec_keystore_import(ctx->keystore, &pem, &keyid); + dnssec_binary_free(&pem); + if (ret != DNSSEC_EOK) { + goto fail; + } + } else { + timing.publish = ctx->now; + + ret = dnssec_key_get_keyid(key, &keyid); + if (ret != DNSSEC_EOK) { + goto fail; + } + } + + // allocate kasp key + knot_kasp_key_t *kkey = calloc(1, sizeof(*kkey)); + if (!kkey) { + ret = KNOT_ENOMEM; + goto fail; + } + + kkey->id = keyid; + kkey->key = key; + kkey->timing = timing; + kkey->is_pub_only = pub_only; + kkey->is_ksk = (dnssec_key_get_flags(kkey->key) == DNSKEY_FLAGS_KSK); + kkey->is_zsk = !kkey->is_ksk; + + // append to zone + ret = kasp_zone_append(ctx->zone, kkey); + free(kkey); + if (ret != KNOT_EOK) { + goto fail; + } + ret = kdnssec_ctx_commit(ctx); + if (ret == KNOT_EOK) { + printf("%s\n", keyid); + return KNOT_EOK; + } +fail: + dnssec_key_free(key); + free(keyid); + return knot_error_from_libdnssec(ret); +} + +static int import_key(kdnssec_ctx_t *ctx, unsigned backend, const char *param, + int argc, char *argv[]) +{ + if (ctx == NULL || param == NULL) { + return KNOT_EINVAL; + } + + // parse params + knot_time_t now = knot_time(); + knot_kasp_key_timing_t timing = { .publish = now, .active = now }; + kdnssec_generate_flags_t flags = 0; + uint16_t keysize = 0; + if (!genkeyargs(argc, argv, false, &flags, &ctx->policy->algorithm, + &keysize, &timing, NULL)) { + return KNOT_EINVAL; + } + + int ret = check_timers(&timing); + if (ret != KNOT_EOK) { + return ret; + } + + normalize_generate_flags(&flags); + + dnssec_key_t *key = NULL; + char *keyid = NULL; + + if (backend == KEYSTORE_BACKEND_PEM) { + // open file + int fd = open(param, O_RDONLY, 0); + if (fd == -1) { + return knot_map_errno(); + } + + // determine size + off_t fsize = lseek(fd, 0, SEEK_END); + if (fsize == -1) { + close(fd); + return knot_map_errno(); + } + if (lseek(fd, 0, SEEK_SET) == -1) { + close(fd); + return knot_map_errno(); + } + + // alloc memory + dnssec_binary_t pem = { 0 }; + ret = dnssec_binary_alloc(&pem, fsize); + if (ret != DNSSEC_EOK) { + close(fd); + goto fail; + } + + // read pem + ssize_t read_count = read(fd, pem.data, pem.size); + close(fd); + if (read_count == -1) { + dnssec_binary_free(&pem); + ret = knot_map_errno(); + goto fail; + } + + // put pem to kesytore + ret = dnssec_keystore_import(ctx->keystore, &pem, &keyid); + dnssec_binary_free(&pem); + if (ret != DNSSEC_EOK) { + goto fail; + } + } else { + assert(backend == KEYSTORE_BACKEND_PKCS11); + keyid = strdup(param); + } + + // create dnssec key + ret = dnssec_key_new(&key); + if (ret != DNSSEC_EOK) { + goto fail; + } + ret = dnssec_key_set_dname(key, ctx->zone->dname); + if (ret != DNSSEC_EOK) { + goto fail; + } + dnssec_key_set_flags(key, dnskey_flags(flags & DNSKEY_GENERATE_SEP_ON)); + dnssec_key_set_algorithm(key, ctx->policy->algorithm); + + // fill key structure from keystore (incl. pubkey from privkey computation) + ret = dnssec_key_import_keystore(key, ctx->keystore, keyid); + if (ret != DNSSEC_EOK) { + goto fail; + } + + // allocate kasp key + knot_kasp_key_t *kkey = calloc(1, sizeof(*kkey)); + if (kkey == NULL) { + ret = KNOT_ENOMEM; + goto fail; + } + kkey->id = keyid; + kkey->key = key; + kkey->timing = timing; + kkey->is_ksk = (flags & DNSKEY_GENERATE_KSK); + kkey->is_zsk = (flags & DNSKEY_GENERATE_ZSK); + + // append to zone + ret = kasp_zone_append(ctx->zone, kkey); + free(kkey); + if (ret != KNOT_EOK) { + goto fail; + } + ret = kdnssec_ctx_commit(ctx); + if (ret == KNOT_EOK) { + printf("%s\n", keyid); + return KNOT_EOK; + } +fail: + dnssec_key_free(key); + free(keyid); + return knot_error_from_libdnssec(ret); +} + +int keymgr_import_pem(kdnssec_ctx_t *ctx, const char *import_file, int argc, char *argv[]) +{ + return import_key(ctx, KEYSTORE_BACKEND_PEM, import_file, argc, argv); +} + +int keymgr_import_pkcs11(kdnssec_ctx_t *ctx, const char *key_id, int argc, char *argv[]) +{ + return import_key(ctx, KEYSTORE_BACKEND_PKCS11, key_id, argc, argv); +} + +int keymgr_nsec3_salt_print(kdnssec_ctx_t *ctx) +{ + dnssec_binary_t salt_bin; + knot_time_t created; + int ret = kasp_db_load_nsec3salt(*ctx->kasp_db, ctx->zone->dname, + &salt_bin, &created); + switch (ret) { + case KNOT_EOK: + printf("Current salt: "); + if (salt_bin.size == 0) { + printf("-"); + } + for (size_t i = 0; i < salt_bin.size; i++) { + printf("%02X", (unsigned)salt_bin.data[i]); + } + printf("\n"); + free(salt_bin.data); + break; + case KNOT_ENOENT: + printf("-- no salt --\n"); + ret = KNOT_EOK; + break; + } + return ret; +} + +int keymgr_nsec3_salt_set(kdnssec_ctx_t *ctx, const char *new_salt) +{ + assert(new_salt); + + dnssec_binary_t salt_bin = { 0 }; + if (strcmp(new_salt, "-") != 0 && + hex_to_bin(new_salt, &salt_bin) != DNSSEC_EOK) { + return KNOT_EMALF; + } + if (salt_bin.size != ctx->policy->nsec3_salt_length) { + printf("Warning: specified salt doesn't match configured " + "salt length (%d).\n", + (int)ctx->policy->nsec3_salt_length); + } + int ret = kasp_db_store_nsec3salt(*ctx->kasp_db, ctx->zone->dname, + &salt_bin, knot_time()); + if (salt_bin.size > 0) { + free(salt_bin.data); + } + return ret; +} + +static void print_tsig(dnssec_tsig_algorithm_t mac, const char *name, + const dnssec_binary_t *secret) +{ + assert(name); + assert(secret); + + const char *mac_name = dnssec_tsig_algorithm_to_name(mac); + assert(mac_name); + + // client format (as a comment) + printf("# %s:%s:%.*s\n", mac_name, name, (int)secret->size, secret->data); + + // server format + printf("key:\n"); + printf(" - id: %s\n", name); + printf(" algorithm: %s\n", mac_name); + printf(" secret: %.*s\n", (int)secret->size, secret->data); +} + +int keymgr_generate_tsig(const char *tsig_name, const char *alg_name, int bits) +{ + dnssec_tsig_algorithm_t alg = dnssec_tsig_algorithm_from_name(alg_name); + if (alg == DNSSEC_TSIG_UNKNOWN) { + return KNOT_INVALID_KEY_ALGORITHM; + } + + int optimal_bits = dnssec_tsig_optimal_key_size(alg); + if (bits == 0) { + bits = optimal_bits; // TODO review + } + + // round up bits to bytes + bits = (bits + CHAR_BIT - 1) / CHAR_BIT * CHAR_BIT; + + if (bits != optimal_bits) { + printf("Notice: Optimal key size for %s is %d bits.", + dnssec_tsig_algorithm_to_name(alg), optimal_bits); + } + assert(bits % CHAR_BIT == 0); + + _cleanup_binary_ dnssec_binary_t key = { 0 }; + int r = dnssec_binary_alloc(&key, bits / CHAR_BIT); + if (r != DNSSEC_EOK) { + printf("Failed to allocate memory."); + return knot_error_from_libdnssec(r); + } + + r = gnutls_rnd(GNUTLS_RND_KEY, key.data, key.size); + if (r != 0) { + printf("Failed to generate secret the key."); + return knot_error_from_libdnssec(r); + } + + _cleanup_binary_ dnssec_binary_t key_b64 = { 0 }; + r = dnssec_binary_to_base64(&key, &key_b64); + if (r != DNSSEC_EOK) { + printf("Failed to convert the key to Base64."); + return knot_error_from_libdnssec(r); + } + + print_tsig(alg, tsig_name, &key_b64); + + return KNOT_EOK; +} + +static long is_uint32(const char *string) +{ + if (*string == '\0') { + return -1; + } + for (const char *p = string; *p != '\0'; p++) { + if (!is_digit(*p)) { + return -1; + } + } + long res = atol(string); + return (res <= UINT32_MAX ? res : -1); +} + +static bool is_hex(const char *string) +{ + for (const char *p = string; *p != '\0'; p++) { + if (!is_xdigit(*p)) { + return false; + } + } + return (*string != '\0'); +} + +int keymgr_get_key(kdnssec_ctx_t *ctx, const char *key_spec, knot_kasp_key_t **key) +{ + long spec_tag = is_uint32(key_spec), spec_len = strlen(key_spec); + if (spec_tag < 0 && !is_hex(key_spec)) { + printf("Error in key specification.\n"); + return KNOT_EINVAL; + } + + *key = NULL; + for (size_t i = 0; i < ctx->zone->num_keys; i++) { + knot_kasp_key_t *candidate = &ctx->zone->keys[i]; + if ((spec_tag >= 0 && dnssec_key_get_keytag(candidate->key) == spec_tag) || + (spec_tag < 0 && strncmp(candidate->id, key_spec, spec_len) == 0)) { + if (*key == NULL) { + *key = candidate; + } + else { + printf("Key is not specified uniquely.\n"); + return KNOT_ELIMIT; + } + } + } + if (*key == NULL) { + printf("Key not found.\n"); + return KNOT_ENOENT; + } + return KNOT_EOK; +} + +int keymgr_foreign_key_id(char *argv[], knot_dname_t **key_zone, char **key_id) +{ + *key_zone = knot_dname_from_str_alloc(argv[0]); + if (*key_zone == NULL) { + return KNOT_ENOMEM; + } + knot_dname_to_lower(*key_zone); + + kdnssec_ctx_t kctx = { 0 }; + int ret = kdnssec_ctx_init(conf(), &kctx, *key_zone, NULL); + if (ret != KNOT_EOK) { + printf("Failed to initialize zone %s (%s)\n", argv[0], knot_strerror(ret)); + free(*key_zone); + *key_zone = NULL; + return KNOT_ENOZONE; + } + knot_kasp_key_t *key; + ret = keymgr_get_key(&kctx, argv[2], &key); + if (ret == KNOT_EOK) { + *key_id = strdup(key->id); + if (*key_id == NULL) { + ret = KNOT_ENOMEM; + } + } + kdnssec_ctx_deinit(&kctx); + return ret; +} + +int keymgr_set_timing(knot_kasp_key_t *key, int argc, char *argv[]) +{ + knot_kasp_key_timing_t temp = key->timing; + kdnssec_generate_flags_t flags = ((key->is_ksk ? DNSKEY_GENERATE_KSK : 0) | (key->is_zsk ? DNSKEY_GENERATE_ZSK : 0)); + + if (genkeyargs(argc, argv, true, &flags, NULL, NULL, &temp, NULL)) { + int ret = check_timers(&temp); + if (ret != KNOT_EOK) { + return ret; + } + key->timing = temp; + key->is_ksk = (flags & DNSKEY_GENERATE_KSK); + key->is_zsk = (flags & DNSKEY_GENERATE_ZSK); + return KNOT_EOK; + } + return KNOT_EINVAL; +} + +static void print_timer(const char *name, knot_time_t t, knot_time_print_t format, + char separator) +{ + static char buff[100]; + if (knot_time_print(format, t, buff, sizeof(buff)) < 0) { + printf("%s=(error)%c", name, separator); // shall not happen + } else { + printf("%s=%s%c", name, buff, separator); + } +} + +int keymgr_list_keys(kdnssec_ctx_t *ctx, knot_time_print_t format) +{ + for (size_t i = 0; i < ctx->zone->num_keys; i++) { + knot_kasp_key_t *key = &ctx->zone->keys[i]; + printf("%s ksk=%s zsk=%s tag=%05d algorithm=%-2d size=%-4u public-only=%s ", key->id, + (key->is_ksk ? "yes" : "no "), (key->is_zsk ? "yes" : "no "), + dnssec_key_get_keytag(key->key), (int)dnssec_key_get_algorithm(key->key), + dnssec_key_get_size(key->key), (key->is_pub_only ? "yes" : "no ")); + print_timer("created", key->timing.created, format, ' '); + print_timer("pre-active", key->timing.pre_active, format, ' '); + print_timer("publish", key->timing.publish, format, ' '); + print_timer("ready", key->timing.ready, format, ' '); + print_timer("active", key->timing.active, format, ' '); + print_timer("retire-active", key->timing.retire_active, format, ' '); + print_timer("retire", key->timing.retire, format, ' '); + print_timer("post-active", key->timing.post_active, format, ' '); + print_timer("remove", key->timing.remove, format, '\n'); + } + return KNOT_EOK; +} + +static int print_ds(const knot_dname_t *dname, const dnssec_binary_t *rdata) +{ + wire_ctx_t ctx = wire_ctx_init(rdata->data, rdata->size); + if (wire_ctx_available(&ctx) < 4) { + return KNOT_EMALF; + } + + char *name = knot_dname_to_str_alloc(dname); + if (!name) { + return KNOT_ENOMEM; + } + + uint16_t keytag = wire_ctx_read_u16(&ctx); + uint8_t algorithm = wire_ctx_read_u8(&ctx); + uint8_t digest_type = wire_ctx_read_u8(&ctx); + + size_t digest_size = wire_ctx_available(&ctx); + + printf("%s DS %d %d %d ", name, keytag, algorithm, digest_type); + for (size_t i = 0; i < digest_size; i++) { + printf("%02x", ctx.position[i]); + } + printf("\n"); + + free(name); + return KNOT_EOK; +} + +static int create_and_print_ds(const knot_dname_t *zone_name, + const dnssec_key_t *key, dnssec_key_digest_t digest) +{ + _cleanup_binary_ dnssec_binary_t rdata = { 0 }; + int r = dnssec_key_create_ds(key, digest, &rdata); + if (r != DNSSEC_EOK) { + return knot_error_from_libdnssec(r); + } + + return print_ds(zone_name, &rdata); +} + +int keymgr_generate_ds(const knot_dname_t *dname, const knot_kasp_key_t *key) +{ + static const dnssec_key_digest_t digests[] = { + DNSSEC_KEY_DIGEST_SHA1, + DNSSEC_KEY_DIGEST_SHA256, + DNSSEC_KEY_DIGEST_SHA384, + 0 + }; + + int ret = KNOT_EOK; + for (int i = 0; digests[i] != 0 && ret == KNOT_EOK; i++) { + ret = create_and_print_ds(dname, key->key, digests[i]); + } + + return ret; +} + +int keymgr_generate_dnskey(const knot_dname_t *dname, const knot_kasp_key_t *key) +{ + const dnssec_key_t *dnskey = key->key; + + char *name = knot_dname_to_str_alloc(dname); + if (!name) { + return KNOT_ENOMEM; + } + + uint16_t flags = dnssec_key_get_flags(dnskey); + uint8_t algorithm = dnssec_key_get_algorithm(dnskey); + + dnssec_binary_t pubkey = { 0 }; + int ret = dnssec_key_get_pubkey(dnskey, &pubkey); + if (ret != DNSSEC_EOK) { + free(name); + return knot_error_from_libdnssec(ret); + } + + uint8_t *base64_output = NULL; + int len = base64_encode_alloc(pubkey.data, pubkey.size, &base64_output); + if (len < 0) { + free(name); + return len; + } + + printf("%s DNSKEY %u 3 %u %.*s\n", name, flags, algorithm, len, base64_output); + + free(base64_output); + free(name); + return KNOT_EOK; +} diff --git a/src/utils/keymgr/functions.h b/src/utils/keymgr/functions.h new file mode 100644 index 0000000..0dcf9cc --- /dev/null +++ b/src/utils/keymgr/functions.h @@ -0,0 +1,45 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include "knot/dnssec/context.h" + +int keymgr_generate_key(kdnssec_ctx_t *ctx, int argc, char *argv[]); + +int keymgr_import_bind(kdnssec_ctx_t *ctx, const char *import_file, bool pub_only); + +int keymgr_import_pem(kdnssec_ctx_t *ctx, const char *import_file, int argc, char *argv[]); + +int keymgr_import_pkcs11(kdnssec_ctx_t *ctx, const char *key_id, int argc, char *argv[]); + +int keymgr_nsec3_salt_print(kdnssec_ctx_t *ctx); + +int keymgr_nsec3_salt_set(kdnssec_ctx_t *ctx, const char *new_salt); + +int keymgr_generate_tsig(const char *tsig_name, const char *alg_name, int bits); + +int keymgr_get_key(kdnssec_ctx_t *ctx, const char *key_spec, knot_kasp_key_t **key); + +int keymgr_foreign_key_id(char *argv[], knot_dname_t **key_zone, char **key_id); + +int keymgr_set_timing(knot_kasp_key_t *key, int argc, char *argv[]); + +int keymgr_list_keys(kdnssec_ctx_t *ctx, knot_time_print_t format); + +int keymgr_generate_ds(const knot_dname_t *dname, const knot_kasp_key_t *key); + +int keymgr_generate_dnskey(const knot_dname_t *dname, const knot_kasp_key_t *key); diff --git a/src/utils/keymgr/main.c b/src/utils/keymgr/main.c new file mode 100644 index 0000000..0ed388f --- /dev/null +++ b/src/utils/keymgr/main.c @@ -0,0 +1,373 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <getopt.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include "contrib/string.h" +#include "knot/conf/conf.h" +#include "knot/dnssec/zone-keys.h" +#include "libknot/libknot.h" +#include "utils/common/params.h" +#include "utils/keymgr/functions.h" + +#define PROGRAM_NAME "keymgr" + +static void print_help(void) +{ + printf("Usage:\n" + " %s -h | -V\n" + " %s -t <tsig_name> [<algorithm>] [<bits>]\n" + " %s [-c | -C | -d <path>] <zone> <command> [<argument>...]\n" + "\n" + "Parameters:\n" + " -c, --config <file> Use a textual configuration file.\n" + " (default %s)\n" + " -C, --confdb <dir> Use a binary configuration database directory.\n" + " (default %s)\n" + " -d, --dir <path> Use specified KASP database path and default configuration.\n" + " -t, --tsig <name> [alg] Generate a TSIG key.\n" + " -h, --help Print the program help.\n" + " -V, --version Print the program version.\n" + "\n" + "Commands:\n" + " list List all zone's DNSSEC keys.\n" + " generate Generate new DNSSEC key.\n" + " (syntax: generate <attribute_name>=<value>...)\n" + " import-bind Import BIND-style key file pair (.key + .private).\n" + " (syntax: import-bind <key_file_name>)\n" + " import-pub Import public-only key to be published in the zone (in BIND .key format).\n" + " (syntax: import-pub <key_file_name>)\n" + " import-pem Import key in PEM format. Specify its parameters manually.\n" + " (syntax: import-pem <pem_file_path> <attribute_name>=<value>...)\n" + " import-pkcs11 Import key stored in PKCS11 storage. Specify its parameters manually.\n" + " (syntax: import-pkcs11 <key_id> <attribute_name>=<value>...)\n" + " nsec3-salt Print current NSEC3 salt. If parameter is specified, set new salt.\n" + " (syntax: nsec3salt [<new_salt>])\n" + " ds Generate DS record(s) for specified key.\n" + " (syntax: ds <key_spec>)\n" + " dnskey Generate DNSKEY record for specified key.\n" + " (syntax: dnskey <key_spec>)\n" + " share Share an existing key of another zone with the specified zone.\n" + " (syntax: share <full_key_ID>\n" + " delete Remove the specified key from zone.\n" + " (syntax: delete <key_spec>)\n" + " set Set existing key's timing attribute.\n" + " (syntax: set <key_spec> <attribute_name>=<value>...)\n" + "\n" + "Key specification:\n" + " either the key tag (number) or [a prefix of] key ID.\n" + "\n" + "Key attributes:\n" + " algorithm The key cryptographic algorithm: either name (e.g. RSASHA256) or\n" + " number.\n" + " size The key size in bits.\n" + " ksk Whether the generated/imported key shall be Key Signing Key.\n" + " created/publish/ready/active/retire/remove The timestamp of the key\n" + " lifetime event (e.g. published=+1d active=1499770874)\n", + PROGRAM_NAME, PROGRAM_NAME, PROGRAM_NAME, CONF_DEFAULT_FILE, CONF_DEFAULT_DBDIR); +} + +static int key_command(int argc, char *argv[], int optind) +{ + if (argc < optind + 2) { + printf("Zone name and/or command not specified\n"); + print_help(); + return KNOT_EINVAL; + } + argc -= optind; + argv += optind; + + knot_dname_t *zone_name = knot_dname_from_str_alloc(argv[0]); + if (zone_name == NULL) { + return KNOT_ENOMEM; + } + knot_dname_to_lower(zone_name); + + kdnssec_ctx_t kctx = { 0 }; + + conf_val_t mapsize = conf_default_get(conf(), C_MAX_KASP_DB_SIZE); + char *kasp_dir = conf_kaspdir(conf()); + int ret = kasp_db_init(kaspdb(), kasp_dir, conf_int(&mapsize)); + free(kasp_dir); + if (ret != KNOT_EOK) { + printf("Failed to initialize KASP db (%s)\n", knot_strerror(ret)); + goto main_end; + } + + ret = kdnssec_ctx_init(conf(), &kctx, zone_name, NULL); + if (ret != KNOT_EOK) { + printf("Failed to initialize KASP (%s)\n", knot_strerror(ret)); + goto main_end; + } + +#define CHECK_MISSING_ARG(msg) \ + if (argc < 3) { \ + printf("%s\n", (msg)); \ + ret = KNOT_EINVAL; \ + goto main_end; \ + } + + bool print_ok_on_succes = true; + if (strcmp(argv[1], "generate") == 0) { + ret = keymgr_generate_key(&kctx, argc - 2, argv + 2); + print_ok_on_succes = false; + } else if (strcmp(argv[1], "import-bind") == 0) { + CHECK_MISSING_ARG("BIND-style key to import not specified"); + ret = keymgr_import_bind(&kctx, argv[2], false); + } else if (strcmp(argv[1], "import-pub") == 0) { + CHECK_MISSING_ARG("BIND-style key to import not specified"); + ret = keymgr_import_bind(&kctx, argv[2], true); + } else if (strcmp(argv[1], "import-pem") == 0) { + CHECK_MISSING_ARG("PEM file to import not specified"); + ret = keymgr_import_pem(&kctx, argv[2], argc - 3, argv + 3); + } else if (strcmp(argv[1], "import-pkcs11") == 0) { + CHECK_MISSING_ARG("Key ID to import not specified"); + ret = keymgr_import_pkcs11(&kctx, argv[2], argc - 3, argv + 3); + } else if (strcmp(argv[1], "nsec3-salt") == 0) { + if (argc > 2) { + ret = keymgr_nsec3_salt_set(&kctx, argv[2]); + } else { + ret = keymgr_nsec3_salt_print(&kctx); + print_ok_on_succes = false; + } + } else if (strcmp(argv[1], "set") == 0) { + CHECK_MISSING_ARG("Key is not specified"); + knot_kasp_key_t *key2set; + ret = keymgr_get_key(&kctx, argv[2], &key2set); + if (ret == KNOT_EOK) { + ret = keymgr_set_timing(key2set, argc - 3, argv + 3); + if (ret == KNOT_EOK) { + ret = kdnssec_ctx_commit(&kctx); + } + } + } else if (strcmp(argv[1], "list") == 0) { + knot_time_print_t format = TIME_PRINT_UNIX; + if (argc > 2 && strcmp(argv[2], "human") == 0) { + format = TIME_PRINT_HUMAN_MIXED; + } else if (argc > 2 && strcmp(argv[2], "iso") == 0) { + format = TIME_PRINT_ISO8601; + } + ret = keymgr_list_keys(&kctx, format); + print_ok_on_succes = false; + } else if (strcmp(argv[1], "ds") == 0 || strcmp(argv[1], "dnskey") == 0) { + int (*generate_rr)(const knot_dname_t *, const knot_kasp_key_t *) = keymgr_generate_dnskey; + if (strcmp(argv[1], "ds") == 0) { + generate_rr = keymgr_generate_ds; + } + if (argc < 3) { + for (int i = 0; i < kctx.zone->num_keys && ret == KNOT_EOK; i++) { + if (kctx.zone->keys[i].is_ksk) { + ret = generate_rr(zone_name, &kctx.zone->keys[i]); + } + } + } else { + knot_kasp_key_t *key2rr; + ret = keymgr_get_key(&kctx, argv[2], &key2rr); + if (ret == KNOT_EOK) { + ret = generate_rr(zone_name, key2rr); + } + } + print_ok_on_succes = false; + } else if (strcmp(argv[1], "share") == 0) { + CHECK_MISSING_ARG("Key to be shared is not specified"); + knot_dname_t *other_zone = NULL; + char *key_to_share = NULL; + ret = keymgr_foreign_key_id(argv, &other_zone, &key_to_share); + if (ret == KNOT_EOK) { + ret = kasp_db_share_key(*kctx.kasp_db, other_zone, kctx.zone->dname, key_to_share); + } + free(other_zone); + free(key_to_share); + } else if (strcmp(argv[1], "delete") == 0) { + CHECK_MISSING_ARG("Key is not specified"); + knot_kasp_key_t *key2del; + ret = keymgr_get_key(&kctx, argv[2], &key2del); + if (ret == KNOT_EOK) { + ret = kdnssec_delete_key(&kctx, key2del); + } + } else { + printf("Wrong zone-key command: %s\n", argv[1]); + goto main_end; + } + +#undef CHECK_MISSING_ARG + + if (ret == KNOT_EOK) { + printf("%s", print_ok_on_succes ? "OK\n" : ""); + } else { + printf("Error (%s)\n", knot_strerror(ret)); + } + +main_end: + kdnssec_ctx_deinit(&kctx); + kasp_db_close(kaspdb()); + free(zone_name); + + return ret; +} + +static bool init_conf(const char *confdb) +{ + size_t max_conf_size = (size_t)CONF_MAPSIZE * 1024 * 1024; + + conf_flag_t flags = CONF_FNOHOSTNAME | CONF_FOPTMODULES; + if (confdb != NULL) { + flags |= CONF_FREADONLY; + } + + conf_t *new_conf = NULL; + int ret = conf_new(&new_conf, conf_schema, confdb, max_conf_size, flags); + if (ret != KNOT_EOK) { + printf("Failed opening configuration database %s (%s)\n", + (confdb == NULL ? "" : confdb), knot_strerror(ret)); + return false; + } + conf_update(new_conf, CONF_UPD_FNONE); + return true; +} + +static bool init_confile(const char *confile) +{ + int ret = conf_import(conf(), confile, true); + if (ret != KNOT_EOK) { + printf("Failed opening configuration file %s (%s)\n", + confile, knot_strerror(ret)); + return false; + } + return true; +} + +static bool init_conf_blank(const char *kasp_dir) +{ + char *confstr = sprintf_alloc("template:\n""" + " - id: default\n" + " storage: .\n" + " kasp-db: %s\n", kasp_dir); + int ret = conf_import(conf(), confstr, false); + free(confstr); + if (ret != KNOT_EOK) { + printf("Failed creating fake configuration (%s)\n", + knot_strerror(ret)); + return false; + } + return true; +} + +static void update_privileges(void) +{ + int uid, gid; + if (conf_user(conf(), &uid, &gid) != KNOT_EOK) { + return; + } + + // Just try to alter process privileges if different from configured. + int unused __attribute__((unused)); + if ((gid_t)gid != getgid()) { + unused = setregid(gid, gid); + } + if ((uid_t)uid != getuid()) { + unused = setreuid(uid, uid); + } +} + +static bool conf_initialized = false; // This is a singleton as well as conf() is. + +#define CHECK_CONF_UNINIT \ + if ((conf_initialized = !conf_initialized) == false) { \ + printf("Error: multiple arguments attempting configuration initializatioin.\n"); \ + return EXIT_FAILURE; \ + } + +int main(int argc, char *argv[]) +{ + int ret; + + struct option opts[] = { + { "config", required_argument, NULL, 'c' }, + { "confdb", required_argument, NULL, 'C' }, + { "dir", required_argument, NULL, 'd' }, + { "tsig", required_argument, NULL, 't' }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'V' }, + { NULL } + }; + + int opt = 0; + while ((opt = getopt_long(argc, argv, "hVd:c:C:t:", opts, NULL)) != -1) { + switch (opt) { + case 'h': + print_help(); + return EXIT_SUCCESS; + case 'V': + print_version(PROGRAM_NAME); + return EXIT_SUCCESS; + case 'd': + CHECK_CONF_UNINIT + if (!init_conf(NULL) || !init_conf_blank(optarg)) { + return EXIT_FAILURE; + } + break; + case 'c': + CHECK_CONF_UNINIT + if (!init_conf(NULL) || !init_confile(optarg)) { + return EXIT_FAILURE; + } + break; + case 'C': + CHECK_CONF_UNINIT + if (!init_conf(argv[2])) { + return EXIT_FAILURE; + } + break; + case 't': + ret = keymgr_generate_tsig(optarg, (argc > optind ? argv[optind] : "hmac-sha256"), + (argc > optind + 1 ? atol(argv[optind + 1]) : 0)); + if (ret != KNOT_EOK) { + printf("Failed to generate TSIG (%s)\n", knot_strerror(ret)); + } + return (ret == KNOT_EOK ? EXIT_SUCCESS : EXIT_FAILURE); + default: + print_help(); + return EXIT_FAILURE; + } + } + + if (!conf_initialized) { + struct stat st; + if (stat(CONF_DEFAULT_DBDIR, &st) == 0 && init_conf(CONF_DEFAULT_DBDIR)) { + // initialized conf from default DB location + } else if (stat(CONF_DEFAULT_FILE, &st) == 0 && + init_conf(NULL) && init_confile(CONF_DEFAULT_FILE)) { + // initialized conf from default confile + } else { + printf("Couldn't initialize configuration, please provide -c, -C, or -d option\n"); + return EXIT_FAILURE; + } + } + + update_privileges(); + + ret = key_command(argc, argv, optind); + + conf_free(conf()); + + return (ret == KNOT_EOK ? EXIT_SUCCESS : EXIT_FAILURE); +} diff --git a/src/utils/khost/khost_main.c b/src/utils/khost/khost_main.c new file mode 100644 index 0000000..fc3fe17 --- /dev/null +++ b/src/utils/khost/khost_main.c @@ -0,0 +1,43 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stdlib.h> + +#include "libdnssec/crypto.h" +#include "utils/khost/khost_params.h" +#include "utils/kdig/kdig_exec.h" +#include "libknot/libknot.h" + +int main(int argc, char *argv[]) +{ + int ret = EXIT_SUCCESS; + + kdig_params_t params; + if (khost_parse(¶ms, argc, argv) == KNOT_EOK) { + if (!params.stop) { + dnssec_crypto_init(); + if (kdig_exec(¶ms) != KNOT_EOK) { + ret = EXIT_FAILURE; + } + dnssec_crypto_cleanup(); + } + } else { + ret = EXIT_FAILURE; + } + + khost_clean(¶ms); + return ret; +} diff --git a/src/utils/khost/khost_params.c b/src/utils/khost/khost_params.c new file mode 100644 index 0000000..bd5f095 --- /dev/null +++ b/src/utils/khost/khost_params.c @@ -0,0 +1,365 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <getopt.h> +#include <locale.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "utils/khost/khost_params.h" +#include "utils/kdig/kdig_params.h" +#include "utils/common/msg.h" +#include "utils/common/params.h" +#include "utils/common/resolv.h" +#include "libknot/libknot.h" +#include "contrib/strtonum.h" +#include "contrib/ucw/lists.h" + +#define PROGRAM_NAME "khost" + +#define DEFAULT_RETRIES_HOST 1 +#define DEFAULT_TIMEOUT_HOST 2 + +static const style_t DEFAULT_STYLE_HOST = { + .format = FORMAT_HOST, + .style = { + .wrap = false, + .show_class = true, + .show_ttl = true, + .verbose = false, + .original_ttl = false, + .empty_ttl = false, + .human_ttl = false, + .human_tmstamp = true, + .generic = false, + .ascii_to_idn = name_to_idn + }, + .show_query = false, + .show_header = false, + .show_edns = false, + .show_question = true, + .show_answer = true, + .show_authority = true, + .show_additional = true, + .show_tsig = false, + .show_footer = false +}; + +static int khost_init(kdig_params_t *params) +{ + // Initialize params with kdig defaults. + int ret = kdig_init(params); + + if (ret != KNOT_EOK) { + return ret; + } + + // Set khost specific defaults. + free(params->config->port); + params->config->port = strdup(DEFAULT_DNS_PORT); + params->config->retries = DEFAULT_RETRIES_HOST; + params->config->wait = DEFAULT_TIMEOUT_HOST; + params->config->class_num = KNOT_CLASS_IN; + params->config->style = DEFAULT_STYLE_HOST; + params->config->idn = true; + + // Check port. + if (params->config->port == NULL) { + query_free(params->config); + return KNOT_ENOMEM; + } + + return KNOT_EOK; +} + +void khost_clean(kdig_params_t *params) +{ + if (params == NULL) { + DBG_NULL; + return; + } + + kdig_clean(params); +} + +static int parse_server(const char *value, list_t *servers, const char *def_port) +{ + if (params_parse_server(value, servers, def_port) != KNOT_EOK) { + ERR("invalid server %s\n", value); + return KNOT_EINVAL; + } + + return KNOT_EOK; +} + +static int parse_name(const char *value, list_t *queries, const query_t *conf) +{ + char *reverse = get_reverse_name(value); + char *ascii_name = (char *)value; + query_t *query; + + if (conf->idn) { + ascii_name = name_from_idn(value); + if (ascii_name == NULL) { + free(reverse); + return KNOT_EINVAL; + } + } + + // If name is not FQDN, append trailing dot. + char *fqd_name = get_fqd_name(ascii_name); + + if (conf->idn) { + free(ascii_name); + } + + // RR type is known. + if (conf->type_num >= 0) { + if (conf->type_num == KNOT_RRTYPE_PTR) { + free(fqd_name); + + // Check for correct address. + if (reverse == NULL) { + ERR("invalid IPv4/IPv6 address %s\n", value); + return KNOT_EINVAL; + } + + // Add reverse query for address. + query = query_create(reverse, conf); + free(reverse); + if (query == NULL) { + return KNOT_ENOMEM; + } + add_tail(queries, (node_t *)query); + } else { + free(reverse); + + // Add query for name and specified type. + query = query_create(fqd_name, conf); + free(fqd_name); + if (query == NULL) { + return KNOT_ENOMEM; + } + add_tail(queries, (node_t *)query); + } + // RR type is unknown, use defaults. + } else { + if (reverse == NULL) { + // Add query for name and type A. + query = query_create(fqd_name, conf); + if (query == NULL) { + free(fqd_name); + return KNOT_ENOMEM; + } + query->type_num = KNOT_RRTYPE_A; + add_tail(queries, (node_t *)query); + + // Add query for name and type AAAA. + query = query_create(fqd_name, conf); + if (query == NULL) { + free(fqd_name); + return KNOT_ENOMEM; + } + query->type_num = KNOT_RRTYPE_AAAA; + query->style.hide_cname = true; + add_tail(queries, (node_t *)query); + + // Add query for name and type MX. + query = query_create(fqd_name, conf); + if (query == NULL) { + free(fqd_name); + return KNOT_ENOMEM; + } + free(fqd_name); + query->type_num = KNOT_RRTYPE_MX; + query->style.hide_cname = true; + add_tail(queries, (node_t *)query); + } else { + free(fqd_name); + + // Add reverse query for address. + query = query_create(reverse, conf); + free(reverse); + if (query == NULL) { + return KNOT_ENOMEM; + } + query->type_num = KNOT_RRTYPE_PTR; + add_tail(queries, (node_t *)query); + } + } + + return KNOT_EOK; +} + +static void print_help(void) +{ + printf("Usage: %s [-4] [-6] [-adhrsTvVw] [-c class] [-t type]\n" + " [-R retries] [-W time] name [server]\n\n" + " -4 Use IPv4 protocol only.\n" + " -6 Use IPv6 protocol only.\n" + " -a Same as -t ANY -v.\n" + " -d Allow debug messages.\n" + " -h, --help Print the program help.\n" + " -r Disable recursion.\n" + " -T Use TCP protocol.\n" + " -v Verbose output.\n" + " -V, --version Print the program version.\n" + " -w Wait forever.\n" + " -c Set query class.\n" + " -t Set query type.\n" + " -R Set number of UDP retries.\n" + " -W Set wait interval.\n", + PROGRAM_NAME); +} + +int khost_parse(kdig_params_t *params, int argc, char *argv[]) +{ + if (params == NULL || argv == NULL) { + DBG_NULL; + return KNOT_EINVAL; + } + + if (khost_init(params) != KNOT_EOK) { + return KNOT_ERROR; + } + +#ifdef LIBIDN + // Set up localization. + if (setlocale(LC_CTYPE, "") == NULL) { + params->config->idn = false; + } +#endif + + query_t *conf = params->config; + uint16_t rclass, rtype; + int64_t serial; + bool notify; + + // Long options. + struct option opts[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'V' }, + { NULL } + }; + + // Command line options processing. + int opt = 0; + while ((opt = getopt_long(argc, argv, "46adhrsTvVwc:t:R:W:", opts, NULL)) + != -1) { + switch (opt) { + case '4': + conf->ip = IP_4; + break; + case '6': + conf->ip = IP_6; + break; + case 'a': + conf->type_num = KNOT_RRTYPE_ANY; + conf->style.format = FORMAT_FULL; + conf->style.show_header = true; + conf->style.show_edns = true; + conf->style.show_footer = true; + break; + case 'd': + msg_enable_debug(1); + break; + case 'h': + print_help(); + params->stop = false; + return KNOT_EOK; + case 'r': + conf->flags.rd_flag = false; + break; + case 'T': + conf->protocol = PROTO_TCP; + break; + case 'v': + conf->style.format = FORMAT_FULL; + conf->style.show_header = true; + conf->style.show_edns = true; + conf->style.show_footer = true; + break; + case 'V': + print_version(PROGRAM_NAME); + params->stop = false; + return KNOT_EOK; + case 'w': + conf->wait = -1; + break; + case 'c': + if (params_parse_class(optarg, &rclass) != KNOT_EOK) { + ERR("invalid class '%s'\n", optarg); + return KNOT_EINVAL; + } + conf->class_num = rclass; + break; + case 't': + if (params_parse_type(optarg, &rtype, &serial, ¬ify) + != KNOT_EOK) { + ERR("invalid type '%s'\n", optarg); + return KNOT_EINVAL; + } + conf->type_num = rtype; + conf->serial = serial; + conf->notify = notify; + + // If NOTIFY, reset default RD flag. + if (conf->notify) { + conf->flags.rd_flag = false; + } + break; + case 'R': + if (str_to_u32(optarg, &conf->retries) != KNOT_EOK) { + ERR("invalid retries '%s'\n", optarg); + return KNOT_EINVAL; + } + break; + case 'W': + if (params_parse_wait(optarg, &conf->wait) != KNOT_EOK) { + ERR("invalid wait '%s'\n", optarg); + return KNOT_EINVAL; + } + break; + default: + print_help(); + return KNOT_ENOTSUP; + } + } + + // Process non-option parameters. + switch (argc - optind) { + case 2: + if (parse_server(argv[optind + 1], &conf->servers, conf->port) + != KNOT_EOK) { + return KNOT_EINVAL; + } + case 1: // Fall through. + if (parse_name(argv[optind], ¶ms->queries, conf) + != KNOT_EOK) { + return KNOT_EINVAL; + } + break; + default: + print_help(); + return KNOT_ENOTSUP; + } + + // Complete missing data in queries based on defaults. + complete_queries(¶ms->queries, params->config); + + return KNOT_EOK; +} diff --git a/src/utils/khost/khost_params.h b/src/utils/khost/khost_params.h new file mode 100644 index 0000000..7fbd1f5 --- /dev/null +++ b/src/utils/khost/khost_params.h @@ -0,0 +1,22 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "utils/kdig/kdig_params.h" + +int khost_parse(kdig_params_t *params, int argc, char *argv[]); +void khost_clean(kdig_params_t *params); diff --git a/src/utils/kjournalprint/main.c b/src/utils/kjournalprint/main.c new file mode 100644 index 0000000..920782d --- /dev/null +++ b/src/utils/kjournalprint/main.c @@ -0,0 +1,412 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stdlib.h> +#include <getopt.h> +#include <sys/stat.h> + +#include "libknot/libknot.h" +#include "knot/journal/journal.h" +#include "knot/zone/zone-dump.h" +#include "utils/common/exec.h" +#include "contrib/dynarray.h" +#include "contrib/strtonum.h" +#include "contrib/string.h" + +#define PROGRAM_NAME "kjournalprint" + +#define RED "\x1B[31m" +#define GRN "\x1B[32m" +#define YLW "\x1B[93m" +#define RESET "\x1B[0m" + +static void print_help(void) +{ + printf("Usage: %s [parameter] <journal_db> <zone_name>\n" + "\n" + "Parameters:\n" + " -l, --limit <num> Read only <num> newest changes.\n" + " -n, --no-color Get output without terminal coloring.\n" + " -z, --zone-list Instead of reading jurnal, display the list\n" + " of zones in the DB (<zone_name> not needed).\n" + " -d, --debug Debug mode output.\n" + " -h, --help Print the program help.\n" + " -V, --version Print the program version.\n", + PROGRAM_NAME); +} + +// workaround: LMDB fails to detect proper mapsize +static int reconfigure_mapsize(const char *journal_path, size_t *mapsize) +{ + char *data_mdb = sprintf_alloc("%s/data.mdb", journal_path); + if (data_mdb == NULL) { + return KNOT_ENOMEM; + } + + struct stat st; + if (stat(data_mdb, &st) != 0 || st.st_size == 0) { + fprintf(stderr, "Journal does not exist: %s\n", journal_path); + free(data_mdb); + return KNOT_ENOENT; + } + + *mapsize = st.st_size + st.st_size / 8; // 112.5% to allow opening when growing + free(data_mdb); + return KNOT_EOK; +} + +static void print_changeset(const changeset_t *chs, bool color) +{ + printf(color ? YLW : ""); + if (chs->soa_from == NULL) { + printf(";; Zone-in-journal, serial: %u\n", + knot_soa_serial(chs->soa_to->rrs.rdata)); + } else { + printf(";; Changes between zone versions: %u -> %u\n", + knot_soa_serial(chs->soa_from->rrs.rdata), + knot_soa_serial(chs->soa_to->rrs.rdata)); + } + changeset_print(chs, stdout, color); +} + +dynarray_declare(rrtype, uint16_t, DYNARRAY_VISIBILITY_STATIC, 100) +dynarray_define(rrtype, uint16_t, DYNARRAY_VISIBILITY_STATIC) + +typedef struct { + rrtype_dynarray_t *arr; + size_t *counter; +} rrtypelist_ctx_t; + +static void rrtypelist_add(rrtype_dynarray_t *arr, uint16_t add_type) +{ + bool already_present = false; + dynarray_foreach(rrtype, uint16_t, i, *arr) { + if (*i == add_type) { + already_present = true; + break; + } + } + if (!already_present) { + rrtype_dynarray_add(arr, &add_type); + } +} + +static int rrtypelist_callback(zone_node_t *node, void *data) +{ + rrtypelist_ctx_t *ctx = data; + for (int i = 0; i < node->rrset_count; i++) { + knot_rrset_t rrset = node_rrset_at(node, i); + rrtypelist_add(ctx->arr, rrset.type); + *ctx->counter += rrset.rrs.count; + } + return KNOT_EOK; +} + +static void print_changeset_debugmode(const changeset_t *chs) +{ + // detect all types + rrtype_dynarray_t types = { 0 }; + size_t count_minus = 1, count_plus = 1; // 1 for SOA which is always present but not iterated + rrtypelist_ctx_t ctx_minus = { &types, &count_minus }, ctx_plus = { &types, &count_plus }; + (void)zone_contents_apply(chs->remove, rrtypelist_callback, &ctx_minus); + (void)zone_contents_nsec3_apply(chs->remove, rrtypelist_callback, &ctx_minus); + (void)zone_contents_apply(chs->add, rrtypelist_callback, &ctx_plus); + (void)zone_contents_nsec3_apply(chs->add, rrtypelist_callback, &ctx_plus); + + if (chs->soa_from == NULL) { + printf("Zone-in-journal %u +++: %zu\t size: %zu\t", knot_soa_serial(chs->soa_to->rrs.rdata), + count_plus, changeset_serialized_size(chs)); + } else { + printf("%u -> %u ---: %zu\t +++: %zu\t size: %zu\t", knot_soa_serial(chs->soa_from->rrs.rdata), + knot_soa_serial(chs->soa_to->rrs.rdata), count_minus, count_plus, changeset_serialized_size(chs)); + } + + char temp[100]; + dynarray_foreach(rrtype, uint16_t, i, types) { + (void)knot_rrtype_to_string(*i, temp, sizeof(temp)); + printf(" %s", temp); + } + printf("\n"); +} + +int print_journal(char *path, knot_dname_t *name, uint32_t limit, bool color, bool debugmode) +{ + list_t db; + init_list(&db); + int ret; + journal_db_t *jdb = NULL; + + ret = journal_db_init(&jdb, path, 1, JOURNAL_MODE_ROBUST); + if (ret != KNOT_EOK) { + return ret; + } + + ret = reconfigure_mapsize(jdb->path, &jdb->fslimit); + if (ret != KNOT_EOK) { + journal_db_close(&jdb); + return ret; + } + + ret = journal_open_db(&jdb); + if (ret != KNOT_EOK) { + journal_db_close(&jdb); + return ret; + } + + if (!journal_exists(&jdb, name)) { + fprintf(stderr, "This zone does not exist in DB %s\n", path); + ret = KNOT_ENOENT; + } + + journal_t *j = journal_new(); + + if (ret == KNOT_EOK) { + ret = journal_open(j, &jdb, name); + } + if (ret != KNOT_EOK) { + journal_free(&j); + journal_db_close(&jdb); + return ret; + } + + bool has_bootstrap; + kserial_t merged_serial, serial_from, last_flushed, serial_to; + uint64_t occupied, occupied_all; + journal_metadata_info(j, &has_bootstrap, &merged_serial, &serial_from, + &last_flushed, &serial_to, &occupied, &occupied_all); + + bool alternative_from = (has_bootstrap || merged_serial.valid); + bool is_empty = (!alternative_from && !serial_from.valid); + if (is_empty) { + ret = KNOT_ENOENT; + goto pj_finally; + } + + if (has_bootstrap) { + ret = journal_load_bootstrap(j, &db); + } else if (merged_serial.valid) { + ret = journal_load_changesets(j, &db, merged_serial.serial); + } else { + ret = journal_load_changesets(j, &db, serial_from.serial); + } + if (ret != KNOT_EOK) { + goto pj_finally; + } + + changeset_t *chs = NULL; + + size_t db_remains = list_size(&db); + + WALK_LIST(chs, db) { + if (--db_remains >= limit && limit > 0) { + continue; + } + if (debugmode) { + print_changeset_debugmode(chs); + } else { + print_changeset(chs, color); + } + } + + changesets_free(&db); + + if (debugmode) { + if ((alternative_from && serial_from.valid) || + kserial_equal(serial_from, last_flushed)) { + init_list(&db); + + ret = journal_load_changesets(j, &db, serial_from.serial); + switch (ret) { + case KNOT_EOK: + printf("---- Additional history ----\n"); + break; + case KNOT_ENOENT: + printf("---- No additional history ----\n"); + ret = KNOT_EOK; + break; + default: + goto pj_finally; + } + WALK_LIST(chs, db) { + print_changeset_debugmode(chs); + if (last_flushed.valid && + serial_equal(knot_soa_serial(chs->soa_from->rrs.rdata), last_flushed.serial)) { + break; + } + } + changesets_free(&db); + } else { + printf("---- No additional history ----\n"); + } + } + + if (debugmode) { + printf("Occupied this zone (approx): %"PRIu64" KiB\n", occupied / 1024); + printf("Occupied all zones together: %"PRIu64" KiB\n", occupied_all / 1024); + } + +pj_finally: + journal_close(j); + journal_free(&j); + journal_db_close(&jdb); + + return ret; +} + +int list_zones(char *path) +{ + journal_db_t *jdb = NULL; + int ret = journal_db_init(&jdb, path, 1, JOURNAL_MODE_ROBUST); + if (ret != KNOT_EOK) { + return ret; + } + + ret = reconfigure_mapsize(jdb->path, &jdb->fslimit); + if (ret != KNOT_EOK) { + journal_db_close(&jdb); + return ret; + } + + ret = journal_open_db(&jdb); + if (ret != KNOT_EOK) { + journal_db_close(&jdb); + return ret; + } + + list_t zones; + init_list(&zones); + ret = journal_db_list_zones(&jdb, &zones); + if (ret == KNOT_EOK) { + ptrnode_t *zn; + char buff[KNOT_DNAME_TXT_MAXLEN + 1]; + WALK_LIST(zn, zones) { + printf("%s\n", knot_dname_to_str(buff, (knot_dname_t *)zn->d, sizeof(buff))); + free(zn->d); + } + ptrlist_free(&zones, NULL); + } + + journal_db_close(&jdb); + return ret; +} + +int main(int argc, char *argv[]) +{ + uint32_t limit = 0; + bool color = true, justlist = false, debugmode = false; + + struct option opts[] = { + { "limit", required_argument, NULL, 'l' }, + { "no-color", no_argument, NULL, 'n' }, + { "zone-list", no_argument, NULL, 'z' }, + { "debug", no_argument, NULL, 'd' }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'V' }, + { NULL } + }; + + int opt = 0; + while ((opt = getopt_long(argc, argv, "l:nzdhV", opts, NULL)) != -1) { + switch (opt) { + case 'l': + if (str_to_u32(optarg, &limit) != KNOT_EOK) { + print_help(); + return EXIT_FAILURE; + } + break; + case 'n': + color = false; + break; + case 'z': + justlist = true; + break; + case 'd': + debugmode = true; + break; + case 'h': + print_help(); + return EXIT_SUCCESS; + case 'V': + print_version(PROGRAM_NAME); + return EXIT_SUCCESS; + default: + print_help(); + return EXIT_FAILURE; + } + } + + char *db = NULL; + knot_dname_t *name = NULL; + + switch (argc - optind) { + case 2: + name = knot_dname_from_str_alloc(argv[optind + 1]); + knot_dname_to_lower(name); + // FALLTHROUGH + case 1: + db = argv[optind]; + break; + default: + print_help(); + return EXIT_FAILURE; + } + + if (db == NULL) { + fprintf(stderr, "Journal DB path not specified\n"); + return EXIT_FAILURE; + } + + if (justlist) { + int ret = list_zones(db); + switch (ret) { + case KNOT_ENOENT: + printf("No zones in journal DB\n"); + // FALLTHROUGH + case KNOT_EOK: + return EXIT_SUCCESS; + case KNOT_EMALF: + fprintf(stderr, "The journal DB is broken\n"); + return EXIT_FAILURE; + default: + fprintf(stderr, "Failed to load zone list (%s)\n", knot_strerror(ret)); + return EXIT_FAILURE; + } + } + + if (name == NULL) { + fprintf(stderr, "Zone not specified\n"); + return EXIT_FAILURE; + } + + int ret = print_journal(db, name, limit, color, debugmode); + free(name); + + switch (ret) { + case KNOT_ENOENT: + printf("The journal is empty\n"); + break; + case KNOT_EOUTOFZONE: + fprintf(stderr, "The specified journal DB does not contain the specified zone\n"); + return EXIT_FAILURE; + case KNOT_EOK: + break; + default: + fprintf(stderr, "Failed to load changesets (%s)\n", knot_strerror(ret)); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/src/utils/knotc/commands.c b/src/utils/knotc/commands.c new file mode 100644 index 0000000..ea73012 --- /dev/null +++ b/src/utils/knotc/commands.c @@ -0,0 +1,1168 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "libknot/libknot.h" +#include "knot/common/log.h" +#include "knot/ctl/commands.h" +#include "knot/conf/conf.h" +#include "knot/conf/confdb.h" +#include "knot/zone/zonefile.h" +#include "knot/zone/zone-load.h" +#include "contrib/macros.h" +#include "contrib/string.h" +#include "contrib/openbsd/strlcat.h" +#include "utils/knotc/commands.h" +#include "utils/knotc/estimator.h" + +#define CMD_EXIT "exit" + +#define CMD_STATUS "status" +#define CMD_STOP "stop" +#define CMD_RELOAD "reload" +#define CMD_STATS "stats" + +#define CMD_ZONE_CHECK "zone-check" +#define CMD_ZONE_MEMSTATS "zone-memstats" +#define CMD_ZONE_STATUS "zone-status" +#define CMD_ZONE_RELOAD "zone-reload" +#define CMD_ZONE_REFRESH "zone-refresh" +#define CMD_ZONE_RETRANSFER "zone-retransfer" +#define CMD_ZONE_NOTIFY "zone-notify" +#define CMD_ZONE_FLUSH "zone-flush" +#define CMD_ZONE_SIGN "zone-sign" +#define CMD_ZONE_KSK_SBM "zone-ksk-submitted" +#define CMD_ZONE_FREEZE "zone-freeze" +#define CMD_ZONE_THAW "zone-thaw" + +#define CMD_ZONE_READ "zone-read" +#define CMD_ZONE_BEGIN "zone-begin" +#define CMD_ZONE_COMMIT "zone-commit" +#define CMD_ZONE_ABORT "zone-abort" +#define CMD_ZONE_DIFF "zone-diff" +#define CMD_ZONE_GET "zone-get" +#define CMD_ZONE_SET "zone-set" +#define CMD_ZONE_UNSET "zone-unset" +#define CMD_ZONE_PURGE "zone-purge" +#define CMD_ZONE_STATS "zone-stats" + +#define CMD_CONF_INIT "conf-init" +#define CMD_CONF_CHECK "conf-check" +#define CMD_CONF_IMPORT "conf-import" +#define CMD_CONF_EXPORT "conf-export" +#define CMD_CONF_LIST "conf-list" +#define CMD_CONF_READ "conf-read" +#define CMD_CONF_BEGIN "conf-begin" +#define CMD_CONF_COMMIT "conf-commit" +#define CMD_CONF_ABORT "conf-abort" +#define CMD_CONF_DIFF "conf-diff" +#define CMD_CONF_GET "conf-get" +#define CMD_CONF_SET "conf-set" +#define CMD_CONF_UNSET "conf-unset" + +#define CTL_LOG_STR "failed to control" + +#define CTL_SEND(type, data) \ + ret = knot_ctl_send(args->ctl, (type), (data)); \ + if (ret != KNOT_EOK) { \ + log_error(CTL_LOG_STR" (%s)", knot_strerror(ret)); \ + return ret; \ + } + +#define CTL_SEND_DATA CTL_SEND(KNOT_CTL_TYPE_DATA, &data) +#define CTL_SEND_BLOCK CTL_SEND(KNOT_CTL_TYPE_BLOCK, NULL) + +static int check_args(cmd_args_t *args, int min, int max) +{ + if (max == 0 && args->argc > 0) { + log_error("command doesn't take arguments"); + return KNOT_EINVAL; + } else if (min == max && args->argc != min) { + log_error("command requires %i arguments", min); + return KNOT_EINVAL; + } else if (args->argc < min) { + log_error("command requires at least %i arguments", min); + return KNOT_EINVAL; + } else if (max > 0 && args->argc > max) { + log_error("command takes at most %i arguments", max); + return KNOT_EINVAL; + } + + return KNOT_EOK; +} + +static int check_conf_args(cmd_args_t *args) +{ + // Mask relevant flags. + cmd_flag_t flags = args->desc->flags; + flags &= CMD_FOPT_ITEM | CMD_FREQ_ITEM | CMD_FOPT_DATA; + + switch (args->argc) { + case 0: + if (flags == CMD_FNONE || (flags & CMD_FOPT_ITEM)) { + return KNOT_EOK; + } + break; + case 1: + if (flags & (CMD_FOPT_ITEM | CMD_FREQ_ITEM)) { + return KNOT_EOK; + } + break; + default: + if (flags != CMD_FNONE) { + return KNOT_EOK; + } + break; + } + + log_error("invalid number of arguments"); + + return KNOT_EINVAL; +} + +static int get_conf_key(const char *key, knot_ctl_data_t *data) +{ + // Get key0. + const char *key0 = key; + + // Check for id. + char *id = strchr(key, '['); + if (id != NULL) { + // Separate key0 and id. + *id++ = '\0'; + + // Check for id end. + char *id_end = id; + while ((id_end = strchr(id_end, ']')) != NULL) { + // Check for escaped character. + if (*(id_end - 1) != '\\') { + break; + } + id_end++; + } + + // Check for unclosed id. + if (id_end == NULL) { + log_error("(missing bracket after identifier) %s", id); + return KNOT_EINVAL; + } + + // Separate id and key1. + *id_end = '\0'; + + key = id_end + 1; + + // Key1 or nothing must follow. + if (*key != '.' && *key != '\0') { + log_error("(unexpected token) %s", key); + return KNOT_EINVAL; + } + } + + // Check for key1. + char *key1 = strchr(key, '.'); + if (key1 != NULL) { + // Separate key0/id and key1. + *key1++ = '\0'; + + if (*key1 == '\0') { + log_error("(missing item specification)"); + return KNOT_EINVAL; + } + } + + (*data)[KNOT_CTL_IDX_SECTION] = key0; + (*data)[KNOT_CTL_IDX_ITEM] = key1; + (*data)[KNOT_CTL_IDX_ID] = id; + + return KNOT_EOK; +} + +static void format_data(ctl_cmd_t cmd, knot_ctl_type_t data_type, + knot_ctl_data_t *data, bool *empty) +{ + const char *error = (*data)[KNOT_CTL_IDX_ERROR]; + const char *flags = (*data)[KNOT_CTL_IDX_FLAGS]; + const char *key0 = (*data)[KNOT_CTL_IDX_SECTION]; + const char *key1 = (*data)[KNOT_CTL_IDX_ITEM]; + const char *id = (*data)[KNOT_CTL_IDX_ID]; + const char *zone = (*data)[KNOT_CTL_IDX_ZONE]; + const char *owner = (*data)[KNOT_CTL_IDX_OWNER]; + const char *ttl = (*data)[KNOT_CTL_IDX_TTL]; + const char *type = (*data)[KNOT_CTL_IDX_TYPE]; + const char *value = (*data)[KNOT_CTL_IDX_DATA]; + + const char *sign = NULL; + if (ctl_has_flag(flags, CTL_FLAG_ADD)) { + sign = CTL_FLAG_ADD; + } else if (ctl_has_flag(flags, CTL_FLAG_REM)) { + sign = CTL_FLAG_REM; + } + + switch (cmd) { + case CTL_STATUS: + if (error != NULL) { + printf("error: (%s)%s%s", error, + (type != NULL) ? " " : "", + (type != NULL) ? type : ""); + } else if (value != NULL) { + printf("%s", value); + *empty = false; + } + break; + case CTL_STOP: + case CTL_RELOAD: + case CTL_CONF_BEGIN: + case CTL_CONF_ABORT: + // Only error message is expected here. + if (error != NULL) { + printf("error: (%s)", error); + } + break; + case CTL_ZONE_STATUS: + case CTL_ZONE_RELOAD: + case CTL_ZONE_REFRESH: + case CTL_ZONE_RETRANSFER: + case CTL_ZONE_NOTIFY: + case CTL_ZONE_FLUSH: + case CTL_ZONE_SIGN: + case CTL_ZONE_KSK_SBM: + case CTL_ZONE_BEGIN: + case CTL_ZONE_COMMIT: + case CTL_ZONE_ABORT: + case CTL_ZONE_PURGE: + if (data_type == KNOT_CTL_TYPE_DATA) { + printf("%s%s%s%s%s%s%s%s", + (!(*empty) ? "\n" : ""), + (error != NULL ? "error: " : ""), + (zone != NULL ? "[" : ""), + (zone != NULL ? zone : ""), + (zone != NULL ? "]" : ""), + (error != NULL ? " (" : ""), + (error != NULL ? error : ""), + (error != NULL ? ")" : "")); + *empty = false; + } + if (cmd == CTL_ZONE_STATUS && type != NULL) { + printf("%s %s: %s", + (data_type != KNOT_CTL_TYPE_DATA ? " |" : ""), + type, value); + } + break; + case CTL_CONF_COMMIT: // Can return a check error context. + case CTL_CONF_LIST: + case CTL_CONF_READ: + case CTL_CONF_DIFF: + case CTL_CONF_GET: + case CTL_CONF_SET: + case CTL_CONF_UNSET: + if (data_type == KNOT_CTL_TYPE_DATA) { + printf("%s%s%s%s%s%s%s%s%s%s%s%s", + (!(*empty) ? "\n" : ""), + (error != NULL ? "error: (" : ""), + (error != NULL ? error : ""), + (error != NULL ? ") " : ""), + (sign != NULL ? sign : ""), + (key0 != NULL ? key0 : ""), + (id != NULL ? "[" : ""), + (id != NULL ? id : ""), + (id != NULL ? "]" : ""), + (key1 != NULL ? "." : ""), + (key1 != NULL ? key1 : ""), + (value != NULL ? " =" : "")); + *empty = false; + } + if (value != NULL) { + printf(" %s", value); + } + break; + case CTL_ZONE_READ: + case CTL_ZONE_DIFF: + case CTL_ZONE_GET: + case CTL_ZONE_SET: + case CTL_ZONE_UNSET: + printf("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", + (!(*empty) ? "\n" : ""), + (error != NULL ? "error: (" : ""), + (error != NULL ? error : ""), + (error != NULL ? ") " : ""), + (zone != NULL ? "[" : ""), + (zone != NULL ? zone : ""), + (zone != NULL ? "] " : ""), + (sign != NULL ? sign : ""), + (owner != NULL ? owner : ""), + (ttl != NULL ? " " : ""), + (ttl != NULL ? ttl : ""), + (type != NULL ? " " : ""), + (type != NULL ? type : ""), + (value != NULL ? " " : ""), + (value != NULL ? value : "")); + *empty = false; + break; + case CTL_STATS: + case CTL_ZONE_STATS: + printf("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", + (!(*empty) ? "\n" : ""), + (error != NULL ? "error: (" : ""), + (error != NULL ? error : ""), + (error != NULL ? ") " : ""), + (zone != NULL ? "[" : ""), + (zone != NULL ? zone : ""), + (zone != NULL ? "] " : ""), + (key0 != NULL ? key0 : ""), + (key1 != NULL ? "." : ""), + (key1 != NULL ? key1 : ""), + (id != NULL ? "[" : ""), + (id != NULL ? id : ""), + (id != NULL ? "]" : ""), + (value != NULL ? " = " : ""), + (value != NULL ? value : "")); + *empty = false; + break; + default: + assert(0); + } +} + +static void format_block(ctl_cmd_t cmd, bool failed, bool empty) +{ + switch (cmd) { + case CTL_STATUS: + printf("%s\n", (failed || !empty) ? "" : "Running"); + break; + case CTL_STOP: + printf("%s\n", failed ? "" : "Stopped"); + break; + case CTL_RELOAD: + printf("%s\n", failed ? "" : "Reloaded"); + break; + case CTL_CONF_BEGIN: + case CTL_CONF_COMMIT: + case CTL_CONF_ABORT: + case CTL_CONF_SET: + case CTL_CONF_UNSET: + case CTL_ZONE_RELOAD: + case CTL_ZONE_REFRESH: + case CTL_ZONE_RETRANSFER: + case CTL_ZONE_NOTIFY: + case CTL_ZONE_FLUSH: + case CTL_ZONE_SIGN: + case CTL_ZONE_KSK_SBM: + case CTL_ZONE_FREEZE: + case CTL_ZONE_THAW: + case CTL_ZONE_BEGIN: + case CTL_ZONE_COMMIT: + case CTL_ZONE_ABORT: + case CTL_ZONE_SET: + case CTL_ZONE_UNSET: + case CTL_ZONE_PURGE: + printf("%s\n", failed ? "" : "OK"); + break; + case CTL_ZONE_STATUS: + case CTL_ZONE_READ: + case CTL_ZONE_DIFF: + case CTL_ZONE_GET: + case CTL_CONF_LIST: + case CTL_CONF_READ: + case CTL_CONF_DIFF: + case CTL_CONF_GET: + case CTL_ZONE_STATS: + case CTL_STATS: + printf("%s", empty ? "" : "\n"); + break; + default: + assert(0); + } +} + +static int ctl_receive(cmd_args_t *args) +{ + bool failed = false; + bool empty = true; + + while (true) { + knot_ctl_type_t type; + knot_ctl_data_t data; + + int ret = knot_ctl_receive(args->ctl, &type, &data); + if (ret != KNOT_EOK) { + log_error(CTL_LOG_STR" (%s)", knot_strerror(ret)); + return ret; + } + + switch (type) { + case KNOT_CTL_TYPE_END: + log_error(CTL_LOG_STR" (%s)", knot_strerror(KNOT_EMALF)); + return KNOT_EMALF; + case KNOT_CTL_TYPE_BLOCK: + format_block(args->desc->cmd, failed, empty); + return failed ? KNOT_ERROR : KNOT_EOK; + case KNOT_CTL_TYPE_DATA: + case KNOT_CTL_TYPE_EXTRA: + format_data(args->desc->cmd, type, &data, &empty); + break; + default: + assert(0); + return KNOT_EINVAL; + } + + if (data[KNOT_CTL_IDX_ERROR] != NULL) { + failed = true; + } + } + + return KNOT_EOK; +} + +static int cmd_ctl(cmd_args_t *args) +{ + int ret = check_args(args, 0, (args->desc->cmd == CTL_STATUS ? 1 : 0)); + if (ret != KNOT_EOK) { + return ret; + } + + knot_ctl_data_t data = { + [KNOT_CTL_IDX_CMD] = ctl_cmd_to_str(args->desc->cmd), + [KNOT_CTL_IDX_FLAGS] = args->force ? CTL_FLAG_FORCE : NULL, + [KNOT_CTL_IDX_TYPE] = args->argc > 0 ? args->argv[0] : NULL + }; + + CTL_SEND_DATA + CTL_SEND_BLOCK + + return ctl_receive(args); +} + +static int set_stats_items(cmd_args_t *args, knot_ctl_data_t *data) +{ + int min_args, max_args; + switch (args->desc->cmd) { + case CTL_STATS: min_args = 0; max_args = 1; break; + case CTL_ZONE_STATS: min_args = 1; max_args = 2; break; + default: + assert(0); + return KNOT_EINVAL; + } + + // Check the number of arguments. + int ret = check_args(args, min_args, max_args); + if (ret != KNOT_EOK) { + return ret; + } + + int idx = 0; + + // Set ZONE name. + if (args->argc > idx && args->desc->cmd == CTL_ZONE_STATS) { + if (strcmp(args->argv[idx], "--") != 0) { + (*data)[KNOT_CTL_IDX_ZONE] = args->argv[idx]; + } + idx++; + } + + if (args->argc > idx) { + (*data)[KNOT_CTL_IDX_SECTION] = args->argv[idx]; + + char *item = strchr(args->argv[idx], '.'); + if (item != NULL) { + // Separate section and item. + *item++ = '\0'; + (*data)[KNOT_CTL_IDX_ITEM] = item; + } + } + + return KNOT_EOK; +} + +static int cmd_stats_ctl(cmd_args_t *args) +{ + knot_ctl_data_t data = { + [KNOT_CTL_IDX_CMD] = ctl_cmd_to_str(args->desc->cmd), + [KNOT_CTL_IDX_FLAGS] = args->force ? CTL_FLAG_FORCE : NULL + }; + + int ret = set_stats_items(args, &data); + if (ret != KNOT_EOK) { + return ret; + } + + CTL_SEND_DATA + CTL_SEND_BLOCK + + return ctl_receive(args); +} + +static int zone_exec(cmd_args_t *args, int (*fcn)(const knot_dname_t *, void *), + void *data) +{ + bool failed = false; + + // Process specified zones. + if (args->argc > 0) { + uint8_t id[KNOT_DNAME_MAXLEN]; + + for (int i = 0; i < args->argc; i++) { + if (knot_dname_from_str(id, args->argv[i], sizeof(id)) == NULL) { + log_zone_str_error(args->argv[i], "invalid name"); + failed = true; + continue; + } + knot_dname_to_lower(id); + + if (!conf_rawid_exists(conf(), C_ZONE, id, knot_dname_size(id))) { + log_zone_error(id, "%s", knot_strerror(KNOT_ENOZONE)); + failed = true; + continue; + } + + if (fcn(id, data) != KNOT_EOK) { + failed = true; + } + } + // Process all configured zones. + } else { + for (conf_iter_t iter = conf_iter(conf(), C_ZONE); + iter.code == KNOT_EOK; conf_iter_next(conf(), &iter)) { + conf_val_t val = conf_iter_id(conf(), &iter); + const knot_dname_t *id = conf_dname(&val); + + if (fcn(id, data) != KNOT_EOK) { + failed = true; + } + } + } + + return failed ? KNOT_ERROR : KNOT_EOK; +} + +static int zone_check(const knot_dname_t *dname, void *data) +{ + UNUSED(data); + + zone_contents_t *contents; + int ret = zone_load_contents(conf(), dname, &contents); + if (ret == KNOT_EOK) { + zone_contents_deep_free(contents); + } + return ret; +} + +static int cmd_zone_check(cmd_args_t *args) +{ + return zone_exec(args, zone_check, NULL); +} + +static int zone_memstats(const knot_dname_t *dname, void *data) +{ + // Init malloc wrapper for trie size estimation. + size_t malloc_size = 0; + knot_mm_t mem_ctx = { + .ctx = &malloc_size, + .alloc = estimator_malloc, + .free = estimator_free + }; + + // Init memory estimation context. + zone_estim_t est = { + .node_table = trie_create(&mem_ctx), + }; + + char buff[KNOT_DNAME_TXT_MAXLEN + 1]; + char *zone_name = knot_dname_to_str(buff, dname, sizeof(buff)); + char *zone_file = conf_zonefile(conf(), dname); + zs_scanner_t *zs = malloc(sizeof(zs_scanner_t)); + + if (est.node_table == NULL || zone_name == NULL || zone_file == NULL || + zs == NULL) { + log_zone_error(dname, "%s", knot_strerror(KNOT_ENOMEM)); + trie_free(est.node_table); + free(zone_file); + free(zs); + return KNOT_ENOMEM; + } + + // Do a parser run, but do not actually create the zone. + if (zs_init(zs, zone_name, KNOT_CLASS_IN, 3600) != 0 || + zs_set_processing(zs, estimator_rrset_memsize_wrap, NULL, &est) != 0 || + zs_set_input_file(zs, zone_file) != 0 || + zs_parse_all(zs) != 0) { + log_zone_error(dname, "failed to parse zone file '%s' (%s)", + zone_file, zs_errorname(zs->error.code)); + trie_apply(est.node_table, estimator_free_trie_node, NULL); + trie_free(est.node_table); + free(zone_file); + zs_deinit(zs); + free(zs); + return KNOT_EPARSEFAIL; + } + free(zone_file); + zs_deinit(zs); + free(zs); + + // Cleanup. + trie_apply(est.node_table, estimator_free_trie_node, NULL); + trie_free(est.node_table); + + double zone_size = (est.rdata_size + est.node_size + est.dname_size + + malloc_size) / (1024.0 * 1024.0); + + log_zone_info(dname, "%zu records, %.1f MiB memory", + est.record_count, zone_size); + + double *total_size = (double *)data; + *total_size += zone_size; + + return KNOT_EOK; +} + +static int cmd_zone_memstats(cmd_args_t *args) +{ + double total_size = 0; + + int ret = zone_exec(args, zone_memstats, &total_size); + + if (args->argc != 1) { + log_info("Total %.1f MiB memory", total_size); + } + + return ret; +} + +static int cmd_zone_ctl(cmd_args_t *args) +{ + knot_ctl_data_t data = { + [KNOT_CTL_IDX_CMD] = ctl_cmd_to_str(args->desc->cmd), + [KNOT_CTL_IDX_FLAGS] = args->force ? CTL_FLAG_FORCE : NULL + }; + + // Check the number of arguments. + int ret = check_args(args, (args->desc->flags & CMD_FREQ_ZONE) ? 1 : 0, -1); + if (ret != KNOT_EOK) { + return ret; + } + + if (args->desc->cmd == CTL_ZONE_PURGE && !args->force) { + log_error("force option required!"); + return KNOT_EDENIED; + } + + // Ignore all zones argument. + if (args->argc == 1 && strcmp(args->argv[0], "--") == 0) { + args->argc = 0; + } + + if (args->argc == 0) { + CTL_SEND_DATA + } + for (int i = 0; i < args->argc; i++) { + data[KNOT_CTL_IDX_ZONE] = args->argv[i]; + + CTL_SEND_DATA + } + + CTL_SEND_BLOCK + + return ctl_receive(args); +} + +#define MAX_FILTERS 7 + +typedef struct { + const char *name; + char id; + bool with_data; // Only ONE filter of each filter_desc_t may have data! +} filter_desc_t; + +const filter_desc_t zone_flush_filters[MAX_FILTERS] = { + { "+outdir", CTL_FILTER_FLUSH_OUTDIR, true }, +}; + +const filter_desc_t zone_status_filters[MAX_FILTERS] = { + { "+role", CTL_FILTER_STATUS_ROLE }, + { "+serial", CTL_FILTER_STATUS_SERIAL }, + { "+transaction", CTL_FILTER_STATUS_TRANSACTION }, + { "+freeze", CTL_FILTER_STATUS_FREEZE }, + { "+events", CTL_FILTER_STATUS_EVENTS }, +}; + +const filter_desc_t zone_purge_filters[MAX_FILTERS] = { + { "+expire", CTL_FILTER_PURGE_EXPIRE }, + { "+timers", CTL_FILTER_PURGE_TIMERS }, + { "+zonefile", CTL_FILTER_PURGE_ZONEFILE }, + { "+journal", CTL_FILTER_PURGE_JOURNAL }, + { "+kaspdb", CTL_FILTER_PURGE_KASPDB }, + { "+orphan", CTL_FILTER_PURGE_ORPHAN }, +}; + +const filter_desc_t null_filter = { 0 }; + +static const filter_desc_t *get_filter(ctl_cmd_t cmd, const char *filter_name) +{ + const filter_desc_t *fd = NULL; + switch (cmd) { + case CTL_ZONE_FLUSH: + fd = zone_flush_filters; + break; + case CTL_ZONE_STATUS: + fd = zone_status_filters; + break; + case CTL_ZONE_PURGE: + fd = zone_purge_filters; + break; + default: + return &null_filter; + } + for (size_t i = 0; i < MAX_FILTERS && fd[i].name != NULL; i++) { + if (strcmp(fd[i].name, filter_name) == 0) { + return &fd[i]; + } + } + return &null_filter; +} + +static int cmd_zone_filter_ctl(cmd_args_t *args) +{ + knot_ctl_data_t data = { + [KNOT_CTL_IDX_CMD] = ctl_cmd_to_str(args->desc->cmd), + [KNOT_CTL_IDX_FLAGS] = args->force ? CTL_FLAG_FORCE : NULL + }; + + if (args->desc->cmd == CTL_ZONE_PURGE && !args->force) { + log_error("force option required!"); + return KNOT_EDENIED; + } + + char filter_buff[MAX_FILTERS + 1] = { 0 }; + + // First, process the filters. + for (int i = 0; i < args->argc; i++) { + if (args->argv[i][0] == '+') { + if (data[KNOT_CTL_IDX_FILTER] == NULL) { + data[KNOT_CTL_IDX_FILTER] = filter_buff; + } + char filter_id[2] = { get_filter(args->desc->cmd, args->argv[i])->id, 0 }; + if (filter_id[0] == '\0') { + log_error("unknown filter: %s", args->argv[i]); + return KNOT_EINVAL; + } + if (strchr(filter_buff, filter_id[0]) == NULL) { + assert(strlen(filter_buff) < MAX_FILTERS); + strlcat(filter_buff, filter_id, sizeof(filter_buff)); + } + if (get_filter(args->desc->cmd, args->argv[i])->with_data) { + data[KNOT_CTL_IDX_DATA] = args->argv[++i]; + } + } + } + + // Second, process zones. + int ret; + int sentzones = 0; + bool twodash = false; + for (int i = 0; i < args->argc; i++) { + // Skip filters. + if (args->argv[i][0] == '+') { + if (get_filter(args->desc->cmd, args->argv[i])->with_data) { + i++; + } + continue; + } + + if (strcmp(args->argv[i], "--") != 0) { + data[KNOT_CTL_IDX_ZONE] = args->argv[i]; + CTL_SEND_DATA + sentzones++; + } else { + twodash = true; + } + } + + if ((args->desc->flags & CMD_FREQ_ZONE) && sentzones == 0 && !twodash) { + log_error("zone must be specified (or -- for all zones)"); + return KNOT_EDENIED; + } + + if (sentzones == 0) { + CTL_SEND_DATA + } + CTL_SEND_BLOCK + + return ctl_receive(args); +} + +static int set_rdata(cmd_args_t *args, int pos, char *rdata, size_t rdata_len) +{ + rdata[0] = '\0'; + + for (int i = pos; i < args->argc; i++) { + if (i > pos && strlcat(rdata, " ", rdata_len) >= rdata_len) { + return KNOT_ESPACE; + } + if (strlcat(rdata, args->argv[i], rdata_len) >= rdata_len) { + return KNOT_ESPACE; + } + } + + return KNOT_EOK; +} + +static int set_node_items(cmd_args_t *args, knot_ctl_data_t *data, char *rdata, + size_t rdata_len) +{ + int min_args, max_args; + switch (args->desc->cmd) { + case CTL_ZONE_READ: + case CTL_ZONE_GET: min_args = 1; max_args = 3; break; + case CTL_ZONE_DIFF: min_args = 1; max_args = 1; break; + case CTL_ZONE_SET: min_args = 3; max_args = -1; break; + case CTL_ZONE_UNSET: min_args = 2; max_args = -1; break; + default: + assert(0); + return KNOT_EINVAL; + } + + // Check the number of arguments. + int ret = check_args(args, min_args, max_args); + if (ret != KNOT_EOK) { + return ret; + } + + int idx = 0; + + // Set ZONE name. + assert(args->argc > idx); + if (strcmp(args->argv[idx], "--") != 0) { + (*data)[KNOT_CTL_IDX_ZONE] = args->argv[idx]; + } + idx++; + + // Set OWNER name if specified. + if (args->argc > idx) { + (*data)[KNOT_CTL_IDX_OWNER] = args->argv[idx]; + idx++; + } + + // Set TTL only with an editing operation. + if (args->argc > idx) { + uint16_t type; + if (knot_rrtype_from_string(args->argv[idx], &type) != 0) { + switch (args->desc->cmd) { + case CTL_ZONE_SET: + case CTL_ZONE_UNSET: + (*data)[KNOT_CTL_IDX_TTL] = args->argv[idx]; + idx++; + break; + default: + return KNOT_EINVAL; + } + } + } + + // Set record TYPE if specified. + if (args->argc > idx) { + (*data)[KNOT_CTL_IDX_TYPE] = args->argv[idx]; + idx++; + } + + // Set record DATA if specified. + if (args->argc > idx) { + ret = set_rdata(args, idx, rdata, rdata_len); + if (ret != KNOT_EOK) { + return ret; + } + (*data)[KNOT_CTL_IDX_DATA] = rdata; + } + + return KNOT_EOK; +} + +static int cmd_zone_node_ctl(cmd_args_t *args) +{ + knot_ctl_data_t data = { + [KNOT_CTL_IDX_CMD] = ctl_cmd_to_str(args->desc->cmd), + [KNOT_CTL_IDX_FLAGS] = args->force ? CTL_FLAG_FORCE : NULL + }; + + char rdata[65536]; // Maximum item size in libknot control interface. + + int ret = set_node_items(args, &data, rdata, sizeof(rdata)); + if (ret != KNOT_EOK) { + return ret; + } + + CTL_SEND_DATA + CTL_SEND_BLOCK + + return ctl_receive(args); +} + +static int cmd_conf_init(cmd_args_t *args) +{ + int ret = check_args(args, 0, 0); + if (ret != KNOT_EOK) { + return ret; + } + + ret = conf_db_check(conf(), &conf()->read_txn); + if ((ret >= KNOT_EOK || ret == KNOT_CONF_EVERSION)) { + if (ret != KNOT_EOK && !args->force) { + log_error("use force option to overwrite the existing " + "destination and ensure the server is not running!"); + return KNOT_EDENIED; + } + + ret = conf_import(conf(), "", false); + } + + if (ret == KNOT_EOK) { + log_info("OK"); + } else { + log_error("init (%s)", knot_strerror(ret)); + } + + return ret; +} + +static int cmd_conf_check(cmd_args_t *args) +{ + int ret = check_args(args, 0, 0); + if (ret != KNOT_EOK) { + return ret; + } + + log_info("Configuration is valid"); + + return 0; +} + +static int cmd_conf_import(cmd_args_t *args) +{ + int ret = check_args(args, 1, 1); + if (ret != KNOT_EOK) { + return ret; + } + + ret = conf_db_check(conf(), &conf()->read_txn); + if ((ret >= KNOT_EOK || ret == KNOT_CONF_EVERSION)) { + if (ret != KNOT_EOK && !args->force) { + log_error("use force option to overwrite the existing " + "destination and ensure the server is not running!"); + return KNOT_EDENIED; + } + + log_debug("importing confdb from file '%s'", args->argv[0]); + + ret = conf_import(conf(), args->argv[0], true); + } + + if (ret == KNOT_EOK) { + log_info("OK"); + } else { + log_error("import (%s)", knot_strerror(ret)); + } + + return ret; +} + +static int cmd_conf_export(cmd_args_t *args) +{ + int ret = check_args(args, 0, 1); + if (ret != KNOT_EOK) { + return ret; + } + + // Stdout is the default output file. + const char *file_name = NULL; + if (args->argc > 0) { + file_name = args->argv[0]; + log_debug("exporting confdb into file '%s'", file_name); + } + + ret = conf_export(conf(), file_name, YP_SNONE); + + if (ret == KNOT_EOK) { + if (args->argc > 0) { + log_info("OK"); + } + } else { + log_error("export (%s)", knot_strerror(ret)); + } + + return ret; +} + +static int cmd_conf_ctl(cmd_args_t *args) +{ + // Check the number of arguments. + int ret = check_conf_args(args); + if (ret != KNOT_EOK) { + return ret; + } + + knot_ctl_data_t data = { + [KNOT_CTL_IDX_CMD] = ctl_cmd_to_str(args->desc->cmd), + [KNOT_CTL_IDX_FLAGS] = args->force ? CTL_FLAG_FORCE : NULL + }; + + // Send the command without parameters. + if (args->argc == 0) { + CTL_SEND_DATA + // Set the first item argument. + } else { + ret = get_conf_key(args->argv[0], &data); + if (ret != KNOT_EOK) { + return ret; + } + + // Send if only one argument or item without values. + if (args->argc == 1 || !(args->desc->flags & CMD_FOPT_DATA)) { + CTL_SEND_DATA + } + } + + // Send the item values or the other items. + for (int i = 1; i < args->argc; i++) { + if (args->desc->flags & CMD_FOPT_DATA) { + data[KNOT_CTL_IDX_DATA] = args->argv[i]; + } else { + ret = get_conf_key(args->argv[i], &data); + if (ret != KNOT_EOK) { + return ret; + } + } + + CTL_SEND_DATA + } + + CTL_SEND_BLOCK + + return ctl_receive(args); +} + +const cmd_desc_t cmd_table[] = { + { CMD_EXIT, NULL, CTL_NONE }, + + { CMD_STATUS, cmd_ctl, CTL_STATUS, CMD_FOPT_DATA}, + { CMD_STOP, cmd_ctl, CTL_STOP }, + { CMD_RELOAD, cmd_ctl, CTL_RELOAD }, + { CMD_STATS, cmd_stats_ctl, CTL_STATS }, + + { CMD_ZONE_CHECK, cmd_zone_check, CTL_NONE, CMD_FOPT_ZONE | CMD_FREAD }, + { CMD_ZONE_MEMSTATS, cmd_zone_memstats, CTL_NONE, CMD_FOPT_ZONE | CMD_FREAD }, + { CMD_ZONE_STATUS, cmd_zone_filter_ctl, CTL_ZONE_STATUS, CMD_FOPT_ZONE }, + { CMD_ZONE_RELOAD, cmd_zone_ctl, CTL_ZONE_RELOAD, CMD_FOPT_ZONE }, + { CMD_ZONE_REFRESH, cmd_zone_ctl, CTL_ZONE_REFRESH, CMD_FOPT_ZONE }, + { CMD_ZONE_RETRANSFER, cmd_zone_ctl, CTL_ZONE_RETRANSFER, CMD_FOPT_ZONE }, + { CMD_ZONE_NOTIFY, cmd_zone_ctl, CTL_ZONE_NOTIFY, CMD_FOPT_ZONE }, + { CMD_ZONE_FLUSH, cmd_zone_filter_ctl, CTL_ZONE_FLUSH, CMD_FOPT_ZONE }, + { CMD_ZONE_SIGN, cmd_zone_ctl, CTL_ZONE_SIGN, CMD_FOPT_ZONE }, + { CMD_ZONE_KSK_SBM, cmd_zone_ctl, CTL_ZONE_KSK_SBM, CMD_FREQ_ZONE | CMD_FOPT_ZONE }, + { CMD_ZONE_FREEZE, cmd_zone_ctl, CTL_ZONE_FREEZE, CMD_FOPT_ZONE }, + { CMD_ZONE_THAW, cmd_zone_ctl, CTL_ZONE_THAW, CMD_FOPT_ZONE }, + + { CMD_ZONE_READ, cmd_zone_node_ctl, CTL_ZONE_READ, CMD_FREQ_ZONE }, + { CMD_ZONE_BEGIN, cmd_zone_ctl, CTL_ZONE_BEGIN, CMD_FREQ_ZONE | CMD_FOPT_ZONE }, + { CMD_ZONE_COMMIT, cmd_zone_ctl, CTL_ZONE_COMMIT, CMD_FREQ_ZONE | CMD_FOPT_ZONE }, + { CMD_ZONE_ABORT, cmd_zone_ctl, CTL_ZONE_ABORT, CMD_FREQ_ZONE | CMD_FOPT_ZONE }, + { CMD_ZONE_DIFF, cmd_zone_node_ctl, CTL_ZONE_DIFF, CMD_FREQ_ZONE }, + { CMD_ZONE_GET, cmd_zone_node_ctl, CTL_ZONE_GET, CMD_FREQ_ZONE }, + { CMD_ZONE_SET, cmd_zone_node_ctl, CTL_ZONE_SET, CMD_FREQ_ZONE }, + { CMD_ZONE_UNSET, cmd_zone_node_ctl, CTL_ZONE_UNSET, CMD_FREQ_ZONE }, + { CMD_ZONE_PURGE, cmd_zone_filter_ctl, CTL_ZONE_PURGE, CMD_FREQ_ZONE }, + { CMD_ZONE_STATS, cmd_stats_ctl, CTL_ZONE_STATS, CMD_FREQ_ZONE }, + + { CMD_CONF_INIT, cmd_conf_init, CTL_NONE, CMD_FWRITE }, + { CMD_CONF_CHECK, cmd_conf_check, CTL_NONE, CMD_FREAD | CMD_FREQ_MOD }, + { CMD_CONF_IMPORT, cmd_conf_import, CTL_NONE, CMD_FWRITE | CMD_FOPT_MOD }, + { CMD_CONF_EXPORT, cmd_conf_export, CTL_NONE, CMD_FREAD | CMD_FOPT_MOD }, + { CMD_CONF_LIST, cmd_conf_ctl, CTL_CONF_LIST, CMD_FOPT_ITEM }, + { CMD_CONF_READ, cmd_conf_ctl, CTL_CONF_READ, CMD_FOPT_ITEM }, + { CMD_CONF_BEGIN, cmd_conf_ctl, CTL_CONF_BEGIN }, + { CMD_CONF_COMMIT, cmd_conf_ctl, CTL_CONF_COMMIT }, + { CMD_CONF_ABORT, cmd_conf_ctl, CTL_CONF_ABORT }, + { CMD_CONF_DIFF, cmd_conf_ctl, CTL_CONF_DIFF, CMD_FOPT_ITEM | CMD_FREQ_TXN }, + { CMD_CONF_GET, cmd_conf_ctl, CTL_CONF_GET, CMD_FOPT_ITEM | CMD_FREQ_TXN }, + { CMD_CONF_SET, cmd_conf_ctl, CTL_CONF_SET, CMD_FREQ_ITEM | CMD_FOPT_DATA | CMD_FREQ_TXN }, + { CMD_CONF_UNSET, cmd_conf_ctl, CTL_CONF_UNSET, CMD_FOPT_ITEM | CMD_FOPT_DATA | CMD_FREQ_TXN }, + { NULL } +}; + +static const cmd_help_t cmd_help_table[] = { + { CMD_EXIT, "", "Exit interactive mode." }, + { "", "", "" }, + { CMD_STATUS, "[<detail>]", "Check if the server is running." }, + { CMD_STOP, "", "Stop the server if running." }, + { CMD_RELOAD, "", "Reload the server configuration and modified zones." }, + { CMD_STATS, "[<module>[.<counter>]]", "Show global statistics counter(s)." }, + { "", "", "" }, + { CMD_ZONE_CHECK, "[<zone>...]", "Check if the zone can be loaded. (*)" }, + { CMD_ZONE_MEMSTATS, "[<zone>...]", "Estimate memory use for the zone. (*)" }, + { CMD_ZONE_RELOAD, "[<zone>...]", "Reload a zone from a disk." }, + { CMD_ZONE_REFRESH, "[<zone>...]", "Force slave zone refresh." }, + { CMD_ZONE_NOTIFY, "[<zone>...]", "Send a NOTIFY message to all configured remotes." }, + { CMD_ZONE_RETRANSFER, "[<zone>...]", "Force slave zone retransfer (no serial check)." }, + { CMD_ZONE_FLUSH, "[<zone>...] [<filter>...]", "Flush zone journal into the zone file." }, + { CMD_ZONE_SIGN, "[<zone>...]", "Re-sign the automatically signed zone." }, + { CMD_ZONE_KSK_SBM, "<zone>...", "When KSK submission, confirm parent's DS presence." }, + { CMD_ZONE_FREEZE, "[<zone>...]", "Temporarily postpone automatic zone-changing events." }, + { CMD_ZONE_THAW, "[<zone>...]", "Dismiss zone freeze." }, + { "", "", "" }, + { CMD_ZONE_READ, "<zone> [<owner> [<type>]]", "Get zone data that are currently being presented." }, + { CMD_ZONE_BEGIN, "<zone>...", "Begin a zone transaction." }, + { CMD_ZONE_COMMIT, "<zone>...", "Commit the zone transaction." }, + { CMD_ZONE_ABORT, "<zone>...", "Abort the zone transaction." }, + { CMD_ZONE_DIFF, "<zone>", "Get zone changes within the transaction." }, + { CMD_ZONE_GET, "<zone> [<owner> [<type>]]", "Get zone data within the transaction." }, + { CMD_ZONE_SET, "<zone> <owner> [<ttl>] <type> <rdata>", "Add zone record within the transaction." }, + { CMD_ZONE_UNSET, "<zone> <owner> [<type> [<rdata>]]", "Remove zone data within the transaction." }, + { CMD_ZONE_PURGE, "<zone>... [<filter>...]", "Purge zone data, zone file, journal, timers, and KASP data." }, + { CMD_ZONE_STATS, "<zone> [<module>[.<counter>]]", "Show zone statistics counter(s)."}, + { CMD_ZONE_STATUS, "<zone> [<filter>...]", "Show the zone status." }, + { "", "", "" }, + { CMD_CONF_INIT, "", "Initialize the confdb. (*)" }, + { CMD_CONF_CHECK, "", "Check the server configuration. (*)" }, + { CMD_CONF_IMPORT, "<filename>", "Import a config file into the confdb. (*)" }, + { CMD_CONF_EXPORT, "[<filename>]", "Export the confdb into a config file or stdout. (*)" }, + { CMD_CONF_LIST, "[<item>...]", "List the confdb sections or section items." }, + { CMD_CONF_READ, "[<item>...]", "Get the item from the active confdb." }, + { CMD_CONF_BEGIN, "", "Begin a writing confdb transaction." }, + { CMD_CONF_COMMIT, "", "Commit the confdb transaction." }, + { CMD_CONF_ABORT, "", "Rollback the confdb transaction." }, + { CMD_CONF_DIFF, "[<item>...]", "Get the item difference within the transaction." }, + { CMD_CONF_GET, "[<item>...]", "Get the item data within the transaction." }, + { CMD_CONF_SET, " <item> [<data>...]", "Set the item data within the transaction." }, + { CMD_CONF_UNSET, "[<item>] [<data>...]", "Unset the item data within the transaction." }, + { NULL } +}; + +void print_commands(void) +{ + printf("\nActions:\n"); + + for (const cmd_help_t *cmd = cmd_help_table; cmd->name != NULL; cmd++) { + printf(" %-18s %-38s %s\n", cmd->name, cmd->params, cmd->desc); + } + + printf("\n" + "Note:\n" + " Use @ owner to denote the zone name.\n" + " Empty or '--' <zone> parameter means all zones or all zones with a transaction.\n" + " Type <item> parameter in the form of <section>[<identifier>].<name>.\n" + " (*) indicates a local operation which requires a configuration.\n"); +} diff --git a/src/utils/knotc/commands.h b/src/utils/knotc/commands.h new file mode 100644 index 0000000..5cabaf1 --- /dev/null +++ b/src/utils/knotc/commands.h @@ -0,0 +1,68 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include "libknot/control/control.h" +#include "knot/ctl/commands.h" + +/*! \brief Command condition flags. */ +typedef enum { + CMD_FNONE = 0, /*!< Empty flag. */ + CMD_FREAD = 1 << 0, /*!< Required read access to config or confdb. */ + CMD_FWRITE = 1 << 1, /*!< Required write access to confdb. */ + CMD_FOPT_ITEM = 1 << 2, /*!< Optional item argument. */ + CMD_FREQ_ITEM = 1 << 3, /*!< Required item argument. */ + CMD_FOPT_DATA = 1 << 4, /*!< Optional item data argument. */ + CMD_FOPT_ZONE = 1 << 5, /*!< Optional zone name argument. */ + CMD_FREQ_ZONE = 1 << 6, /*!< Required zone name argument. */ + CMD_FREQ_TXN = 1 << 7, /*!< Required open confdb transaction. */ + CMD_FOPT_MOD = 1 << 8, /*!< Optional configured modules dependency. */ + CMD_FREQ_MOD = 1 << 9, /*!< Required configured modules dependency. */ +} cmd_flag_t; + +struct cmd_desc; +typedef struct cmd_desc cmd_desc_t; + +/*! \brief Command callback arguments. */ +typedef struct { + const cmd_desc_t *desc; + knot_ctl_t *ctl; + int argc; + const char **argv; + bool force; +} cmd_args_t; + +/*! \brief Command callback description. */ +struct cmd_desc { + const char *name; + int (*fcn)(cmd_args_t *); + ctl_cmd_t cmd; + cmd_flag_t flags; +}; + +/*! \brief Command description. */ +typedef struct { + const char *name; + const char *params; + const char *desc; +} cmd_help_t; + +/*! \brief Table of commands. */ +extern const cmd_desc_t cmd_table[]; + +/*! \brief Prints commands help. */ +void print_commands(void); diff --git a/src/utils/knotc/estimator.c b/src/utils/knotc/estimator.c new file mode 100644 index 0000000..f1fa1a7 --- /dev/null +++ b/src/utils/knotc/estimator.c @@ -0,0 +1,147 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <assert.h> + +#include "utils/knotc/estimator.h" +#include "knot/zone/node.h" +#include "libknot/libknot.h" +#include "contrib/macros.h" +#include "contrib/string.h" +#include "contrib/ucw/lists.h" + +// Addition constants used for tweaking, mostly malloc overhead +enum estim_consts { + MALLOC_OVERHEAD = sizeof(size_t), // set according to malloc.c + MALLOC_MIN = MALLOC_OVERHEAD * 3 // minimum size of malloc'd chunk +}; + +typedef struct { + node_t n; + uint16_t type; +} type_list_item_t; + +static size_t add_overhead(size_t size) +{ + return MALLOC_OVERHEAD + size + size % MALLOC_MIN; +} + +// return: 0 not present, 1 - present +static int find_in_list(list_t *node_list, uint16_t type) +{ + node_t *n = NULL; + WALK_LIST(n, *node_list) { + type_list_item_t *l_entr = (type_list_item_t *)n; + assert(l_entr); + if (l_entr->type == type) { + return 1; + } + } + + type_list_item_t *new_entry = malloc(sizeof(type_list_item_t)); + assert(new_entry != NULL); + new_entry->type = type; + + add_head(node_list, (node_t *)new_entry); + return 0; +} + +// return: 0 not present (added), 1 - present +static int dummy_node_add_type(list_t *l, uint16_t t) +{ + return find_in_list(l, t); +} + +// return: 0 - unique, 1 - duplicate +static int insert_dname_into_table(trie_t *table, const knot_dname_t *d, + list_t **dummy_node) +{ + size_t d_size = knot_dname_size(d); + + trie_val_t *val = trie_get_try(table, (char *)d, d_size); + if (val == NULL) { + // Create new dummy node to use for this dname + *dummy_node = malloc(sizeof(list_t)); + assert(dummy_node != NULL); + init_list(*dummy_node); + *trie_get_ins(table, (char *)d, d_size) = *dummy_node; + return 0; + } else { + // Return previously found dummy node + *dummy_node = (list_t *)(*val); + return 1; + } +} + +static void rrset_memsize(zone_estim_t *est, const zs_scanner_t *scanner) +{ + // Handle RRSet's owner + list_t *dummy_node = NULL; + if (insert_dname_into_table(est->node_table, scanner->r_owner, &dummy_node) == 0) { + // First time we see this name == new node + est->node_size += add_overhead(sizeof(zone_node_t)); + // Also, node has an owner. + est->dname_size += add_overhead(knot_dname_size(scanner->r_owner)); + // Trie's nodes handled at the end of computation + } + assert(dummy_node); + + // Add RDATA + size + size_t rdlen = knot_rdata_size(scanner->r_data_length); + est->rdata_size += add_overhead(rdlen); + est->record_count++; + + /* + * RDATA size done, now add static part of RRSet to size. + * Do not add for RRs that would be merged. + * All possible duplicates will be added to total size. + */ + if (dummy_node_add_type(dummy_node, scanner->r_type) == 0) { + /* + * New RR type, add actual rr_data struct's size. No way to + * guess the actual overhead taken up by the array, so we add + * it each time. + */ + est->node_size += add_overhead(sizeof(struct rr_data)); + } +} + +void *estimator_malloc(void *ctx, size_t len) +{ + size_t *count = (size_t *)ctx; + *count += add_overhead(len); + return malloc(len); +} + +void estimator_free(void *p) +{ + free(p); +} + +void estimator_rrset_memsize_wrap(zs_scanner_t *scanner) +{ + rrset_memsize(scanner->process.data, scanner); +} + +int estimator_free_trie_node(trie_val_t *val, void *data) +{ + UNUSED(data); + list_t *trie_n = (list_t *)(*val); + WALK_LIST_FREE(*trie_n); + free(trie_n); + + return KNOT_EOK; +} diff --git a/src/utils/knotc/estimator.h b/src/utils/knotc/estimator.h new file mode 100644 index 0000000..ee05d5f --- /dev/null +++ b/src/utils/knotc/estimator.h @@ -0,0 +1,64 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include "contrib/qp-trie/trie.h" +#include "libzscanner/scanner.h" + +/*! + * \brief Memory estimation context. + */ +typedef struct { + trie_t *node_table; /*!< Same trie is in actual zone. */ + size_t rdata_size; /*!< Estimated RDATA size. */ + size_t dname_size; /*!< Estimated DNAME size. */ + size_t node_size; /*!< Estimated node size. */ + size_t record_count; /*!< Total record count for zone. */ +} zone_estim_t; + +/*! + * \brief Size counting malloc wrapper. + * + * \param ctx Data for malloc wrapper. + * \param len Size to allocate. + * + * \retval Alloc'd data on succes. + * \retval NULL on error. + */ +void *estimator_malloc(void *ctx, size_t len); + +/*! + * \brief Size counting free wrapper. + * + * \param p Data to free. + */ +void estimator_free(void *p); + +/*! + * \brief For use with scanner - counts memsize of RRSets. + * + * \param scanner Scanner context. + */ +void estimator_rrset_memsize_wrap(zs_scanner_t *scanner); + +/*! + * \brief Cleanup function for use with trie. + * + * \param val Data to free. + * \param data Unused variable. + */ +int estimator_free_trie_node(trie_val_t *val, void *data); diff --git a/src/utils/knotc/interactive.c b/src/utils/knotc/interactive.c new file mode 100644 index 0000000..ad14104 --- /dev/null +++ b/src/utils/knotc/interactive.c @@ -0,0 +1,433 @@ +/* Copyright (C) 2016 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stdio.h> +#include <histedit.h> + +#include "knot/common/log.h" +#include "utils/common/lookup.h" +#include "utils/knotc/interactive.h" +#include "utils/knotc/commands.h" +#include "contrib/string.h" + +#define PROGRAM_NAME "knotc" +#define HISTORY_FILE ".knotc_history" + +extern params_t params; + +static void cmds_lookup(EditLine *el, const char *str, size_t str_len) +{ + lookup_t lookup; + int ret = lookup_init(&lookup); + if (ret != KNOT_EOK) { + return; + } + + // Fill the lookup with command names. + for (const cmd_desc_t *desc = cmd_table; desc->name != NULL; desc++) { + ret = lookup_insert(&lookup, desc->name, NULL); + if (ret != KNOT_EOK) { + goto cmds_lookup_finish; + } + } + + lookup_complete(&lookup, str, str_len, el, true); + +cmds_lookup_finish: + lookup_deinit(&lookup); +} + +static void local_zones_lookup(EditLine *el, const char *str, size_t str_len) +{ + lookup_t lookup; + int ret = lookup_init(&lookup); + if (ret != KNOT_EOK) { + return; + } + + char buff[KNOT_DNAME_TXT_MAXLEN + 1]; + + // Fill the lookup with local zone names. + for (conf_iter_t iter = conf_iter(conf(), C_ZONE); + iter.code == KNOT_EOK; conf_iter_next(conf(), &iter)) { + conf_val_t val = conf_iter_id(conf(), &iter); + char *name = knot_dname_to_str(buff, conf_dname(&val), sizeof(buff)); + + ret = lookup_insert(&lookup, name, NULL); + if (ret != KNOT_EOK) { + conf_iter_finish(conf(), &iter); + goto local_zones_lookup_finish; + } + } + + lookup_complete(&lookup, str, str_len, el, true); + +local_zones_lookup_finish: + lookup_deinit(&lookup); +} + +static char *get_id_name(const char *section) +{ + const cmd_desc_t *desc = cmd_table; + while (desc->name != NULL && desc->cmd != CTL_CONF_LIST) { + desc++; + } + assert(desc->name != NULL); + + knot_ctl_data_t query = { + [KNOT_CTL_IDX_CMD] = ctl_cmd_to_str(desc->cmd), + [KNOT_CTL_IDX_SECTION] = section + }; + + knot_ctl_t *ctl = NULL; + knot_ctl_type_t type; + knot_ctl_data_t reply; + + // Try to get the first group item (possible id). + if (set_ctl(&ctl, desc, ¶ms) != KNOT_EOK || + knot_ctl_send(ctl, KNOT_CTL_TYPE_DATA, &query) != KNOT_EOK || + knot_ctl_send(ctl, KNOT_CTL_TYPE_BLOCK, NULL) != KNOT_EOK || + knot_ctl_receive(ctl, &type, &reply) != KNOT_EOK || + type != KNOT_CTL_TYPE_DATA || reply[KNOT_CTL_IDX_ERROR] != NULL) { + unset_ctl(ctl); + return NULL; + } + + char *id_name = strdup(reply[KNOT_CTL_IDX_ITEM]); + + unset_ctl(ctl); + + return id_name; +} + +static void id_lookup(EditLine *el, const char *str, size_t str_len, + const cmd_desc_t *cmd_desc, const char *section, bool add_space) +{ + // Decide which confdb transaction to ask. + unsigned ctl_code = (cmd_desc->flags & CMD_FREQ_TXN) ? + CTL_CONF_GET : CTL_CONF_READ; + + const cmd_desc_t *desc = cmd_table; + while (desc->name != NULL && desc->cmd != ctl_code) { + desc++; + } + assert(desc->name != NULL); + + char *id_name = get_id_name(section); + if (id_name == NULL) { + return; + } + + knot_ctl_data_t query = { + [KNOT_CTL_IDX_CMD] = ctl_cmd_to_str(desc->cmd), + [KNOT_CTL_IDX_SECTION] = section, + [KNOT_CTL_IDX_ITEM] = id_name + }; + + lookup_t lookup; + knot_ctl_t *ctl = NULL; + + if (set_ctl(&ctl, desc, ¶ms) != KNOT_EOK || + knot_ctl_send(ctl, KNOT_CTL_TYPE_DATA, &query) != KNOT_EOK || + knot_ctl_send(ctl, KNOT_CTL_TYPE_BLOCK, NULL) != KNOT_EOK || + lookup_init(&lookup) != KNOT_EOK) { + unset_ctl(ctl); + free(id_name); + return; + } + + free(id_name); + + while (true) { + knot_ctl_type_t type; + knot_ctl_data_t reply; + + // Receive one section id. + if (knot_ctl_receive(ctl, &type, &reply) != KNOT_EOK) { + goto id_lookup_finish; + } + + // Stop if finished transfer. + if (type != KNOT_CTL_TYPE_DATA) { + break; + } + + // Insert the id into the lookup. + if (reply[KNOT_CTL_IDX_ERROR] != NULL || + lookup_insert(&lookup, reply[KNOT_CTL_IDX_DATA], NULL) != KNOT_EOK) { + goto id_lookup_finish; + } + } + + lookup_complete(&lookup, str, str_len, el, add_space); + +id_lookup_finish: + lookup_deinit(&lookup); + unset_ctl(ctl); +} + +static void list_lookup(EditLine *el, const char *section, const char *item) +{ + const cmd_desc_t *desc = cmd_table; + while (desc->name != NULL && desc->cmd != CTL_CONF_LIST) { + desc++; + } + assert(desc->name != NULL); + + knot_ctl_data_t query = { + [KNOT_CTL_IDX_CMD] = ctl_cmd_to_str(desc->cmd), + [KNOT_CTL_IDX_SECTION] = section + }; + + lookup_t lookup; + knot_ctl_t *ctl = NULL; + + if (set_ctl(&ctl, desc, ¶ms) != KNOT_EOK || + knot_ctl_send(ctl, KNOT_CTL_TYPE_DATA, &query) != KNOT_EOK || + knot_ctl_send(ctl, KNOT_CTL_TYPE_BLOCK, NULL) != KNOT_EOK || + lookup_init(&lookup) != KNOT_EOK) { + unset_ctl(ctl); + return; + } + + while (true) { + knot_ctl_type_t type; + knot_ctl_data_t reply; + + // Receive one section/item name. + if (knot_ctl_receive(ctl, &type, &reply) != KNOT_EOK) { + goto list_lookup_finish; + } + + // Stop if finished transfer. + if (type != KNOT_CTL_TYPE_DATA) { + break; + } + + const char *str = (section == NULL) ? reply[KNOT_CTL_IDX_SECTION] : + reply[KNOT_CTL_IDX_ITEM]; + + // Insert the name into the lookup. + if (reply[KNOT_CTL_IDX_ERROR] != NULL || + lookup_insert(&lookup, str, NULL) != KNOT_EOK) { + goto list_lookup_finish; + } + } + + lookup_complete(&lookup, item, strlen(item), el, section != NULL); + +list_lookup_finish: + lookup_deinit(&lookup); + unset_ctl(ctl); +} + +static void item_lookup(EditLine *el, const char *str, const cmd_desc_t *cmd_desc) +{ + // List all sections. + if (str == NULL) { + list_lookup(el, NULL, ""); + return; + } + + // Check for id specification. + char *id = (strchr(str, '[')); + if (id != NULL) { + char *section = strndup(str, id - str); + + // Check for completed id specification. + char *id_stop = (strchr(id, ']')); + if (id_stop != NULL) { + // Complete the item name. + if (*(id_stop + 1) == '.') { + list_lookup(el, section, id_stop + 2); + } + } else { + // Complete the section id. + id_lookup(el, id + 1, strlen(id + 1), cmd_desc, section, false); + } + + free(section); + } else { + // Check for item specification. + char *dot = (strchr(str, '.')); + if (dot != NULL) { + // Complete the item name. + char *section = strndup(str, dot - str); + list_lookup(el, section, dot + 1); + free(section); + } else { + // Complete the section name. + list_lookup(el, NULL, str); + } + } +} + +static unsigned char complete(EditLine *el, int ch) +{ + int argc, token, pos; + const char **argv; + + const LineInfo *li = el_line(el); + Tokenizer *tok = tok_init(NULL); + + // Parse the line. + int ret = tok_line(tok, li, &argc, &argv, &token, &pos); + if (ret != 0) { + goto complete_exit; + } + + // Show possible commands. + if (argc == 0) { + print_commands(); + goto complete_exit; + } + + // Complete the command name. + if (token == 0) { + cmds_lookup(el, argv[0], pos); + goto complete_exit; + } + + // Find the command descriptor. + const cmd_desc_t *desc = cmd_table; + while (desc->name != NULL && strcmp(desc->name, argv[0]) != 0) { + desc++; + } + if (desc->name == NULL) { + goto complete_exit; + } + + // Finish if a command with no or unsupported arguments. + if (desc->flags == CMD_FNONE || desc->flags == CMD_FREAD || + desc->flags == CMD_FWRITE) { + goto complete_exit; + } + + ret = set_config(desc, ¶ms); + if (ret != KNOT_EOK) { + goto complete_exit; + } + + // Complete the zone name. + if (desc->flags & (CMD_FREQ_ZONE | CMD_FOPT_ZONE)) { + if (token > 1 && !(desc->flags & CMD_FOPT_ZONE)) { + goto complete_exit; + } + + if (desc->flags & CMD_FREAD) { + local_zones_lookup(el, argv[token], pos); + } else { + id_lookup(el, argv[token], pos, desc, "zone", true); + } + goto complete_exit; + // Complete the section/id/item name. + } else if (desc->flags & (CMD_FOPT_ITEM | CMD_FREQ_ITEM)) { + if (token == 1) { + item_lookup(el, argv[1], desc); + } + goto complete_exit; + } + +complete_exit: + conf_update(NULL, CONF_UPD_FNONE); + tok_reset(tok); + tok_end(tok); + + return CC_REDISPLAY; +} + +static char *prompt(EditLine *el) +{ + return PROGRAM_NAME"> "; +} + +int interactive_loop(params_t *params) +{ + char *hist_file = NULL; + const char *home = getenv("HOME"); + if (home != NULL) { + hist_file = sprintf_alloc("%s/%s", home, HISTORY_FILE); + } + if (hist_file == NULL) { + log_notice("failed to get home directory"); + } + + EditLine *el = el_init(PROGRAM_NAME, stdin, stdout, stderr); + if (el == NULL) { + log_error("interactive mode not available"); + free(hist_file); + return KNOT_ERROR; + } + + History *hist = history_init(); + if (hist == NULL) { + log_error("interactive mode not available"); + el_end(el); + free(hist_file); + return KNOT_ERROR; + } + + HistEvent hev = { 0 }; + history(hist, &hev, H_SETSIZE, 100); + el_set(el, EL_HIST, history, hist); + history(hist, &hev, H_LOAD, hist_file); + + el_set(el, EL_TERMINAL, NULL); + el_set(el, EL_EDITOR, "emacs"); + el_set(el, EL_PROMPT, prompt); + el_set(el, EL_SIGNAL, 1); + el_source(el, NULL); + + el_set(el, EL_ADDFN, PROGRAM_NAME"-complete", + "Perform "PROGRAM_NAME" completion.", complete); + el_set(el, EL_BIND, "^I", PROGRAM_NAME"-complete", NULL); + + int count; + const char *line; + while ((line = el_gets(el, &count)) != NULL && count > 0) { + history(hist, &hev, H_ENTER, line); + + Tokenizer *tok = tok_init(NULL); + + // Tokenize the current line. + int argc; + const char **argv; + const LineInfo *li = el_line(el); + int ret = tok_line(tok, li, &argc, &argv, NULL, NULL); + if (ret != 0) { + continue; + } + + // Process the command. + ret = process_cmd(argc, argv, params); + + history(hist, &hev, H_SAVE, hist_file); + tok_reset(tok); + tok_end(tok); + + // Check for the exit command. + if (ret == KNOT_CTL_ESTOP) { + break; + } + } + + history_end(hist); + free(hist_file); + + el_end(el); + + return KNOT_EOK; +} diff --git a/src/utils/knotc/interactive.h b/src/utils/knotc/interactive.h new file mode 100644 index 0000000..6c7b3be --- /dev/null +++ b/src/utils/knotc/interactive.h @@ -0,0 +1,26 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include "utils/knotc/process.h" + +/*! + * Executes an interactive processing loop. + * + * \param[in] params Utility parameters. + */ +int interactive_loop(params_t *params); diff --git a/src/utils/knotc/main.c b/src/utils/knotc/main.c new file mode 100644 index 0000000..826c7bf --- /dev/null +++ b/src/utils/knotc/main.c @@ -0,0 +1,145 @@ +/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <getopt.h> +#include <stdio.h> + +#include "contrib/strtonum.h" +#include "knot/common/log.h" +#include "utils/common/params.h" +#include "utils/knotc/commands.h" +#include "utils/knotc/interactive.h" +#include "utils/knotc/process.h" + +#define PROGRAM_NAME "knotc" +#define SPACE " " +#define DEFAULT_CTL_TIMEOUT 10 + +static void print_help(void) +{ + printf("Usage: %s [parameters] <action> [action_args]\n" + "\n" + "Parameters:\n" + " -c, --config <file> "SPACE"Use a textual configuration file.\n" + " "SPACE" (default %s)\n" + " -C, --confdb <dir> "SPACE"Use a binary configuration database directory.\n" + " "SPACE" (default %s)\n" + " -m, --max-conf-size <MiB>"SPACE"Set maximum configuration size (max 10000 MiB).\n" + " "SPACE" (default %d MiB)\n" + " -s, --socket <path> "SPACE"Use a control UNIX socket path.\n" + " "SPACE" (default %s)\n" + " -t, --timeout <sec> "SPACE"Use a control socket timeout (max 7200 seconds).\n" + " "SPACE" (default %u seconds)\n" + " -f, --force "SPACE"Forced operation. Overrides some checks.\n" + " -v, --verbose "SPACE"Enable debug output.\n" + " -h, --help "SPACE"Print the program help.\n" + " -V, --version "SPACE"Print the program version.\n", + PROGRAM_NAME, CONF_DEFAULT_FILE, CONF_DEFAULT_DBDIR, + CONF_MAPSIZE, RUN_DIR "/knot.sock", DEFAULT_CTL_TIMEOUT); + + print_commands(); +} + +params_t params = { + .max_conf_size = (size_t)CONF_MAPSIZE * 1024 * 1024, + .timeout = DEFAULT_CTL_TIMEOUT * 1000 +}; + +int main(int argc, char **argv) +{ + /* Long options. */ + struct option opts[] = { + { "config", required_argument, NULL, 'c' }, + { "confdb", required_argument, NULL, 'C' }, + { "max-conf-size", required_argument, NULL, 'm' }, + { "socket", required_argument, NULL, 's' }, + { "timeout", required_argument, NULL, 't' }, + { "force", no_argument, NULL, 'f' }, + { "verbose", no_argument, NULL, 'v' }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'V' }, + { NULL } + }; + + /* Parse command line arguments */ + int opt = 0; + while ((opt = getopt_long(argc, argv, "+c:C:m:s:t:fvhV", opts, NULL)) != -1) { + switch (opt) { + case 'c': + params.config = optarg; + break; + case 'C': + params.confdb = optarg; + break; + case 'm': + if (str_to_size(optarg, ¶ms.max_conf_size, 1, 10000) != KNOT_EOK) { + print_help(); + return EXIT_FAILURE; + } + /* Convert to bytes. */ + params.max_conf_size *= 1024 * 1024; + break; + case 's': + params.socket = optarg; + break; + case 't': + if (str_to_int(optarg, ¶ms.timeout, 0, 7200) != KNOT_EOK) { + print_help(); + return EXIT_FAILURE; + } + /* Convert to milliseconds. */ + params.timeout *= 1000; + break; + case 'f': + params.force = true; + break; + case 'v': + params.verbose = true; + break; + case 'h': + print_help(); + return EXIT_SUCCESS; + case 'V': + print_version(PROGRAM_NAME); + return EXIT_SUCCESS; + default: + print_help(); + return EXIT_FAILURE; + } + } + + /* Set up simplified logging just to stdout/stderr. */ + log_init(); + log_levels_set(LOG_TARGET_STDOUT, LOG_SOURCE_ANY, + LOG_MASK(LOG_INFO) | LOG_MASK(LOG_NOTICE)); + log_levels_set(LOG_TARGET_STDERR, LOG_SOURCE_ANY, LOG_UPTO(LOG_WARNING)); + log_levels_set(LOG_TARGET_SYSLOG, LOG_SOURCE_ANY, 0); + log_flag_set(LOG_FLAG_NOTIMESTAMP | LOG_FLAG_NOINFO); + if (params.verbose) { + log_levels_add(LOG_TARGET_STDOUT, LOG_SOURCE_ANY, LOG_MASK(LOG_DEBUG)); + } + + int ret; + if (argc - optind < 1) { + ret = interactive_loop(¶ms); + } else { + ret = process_cmd(argc - optind, (const char **)argv + optind, ¶ms); + } + + log_close(); + + return (ret == KNOT_EOK) ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/src/utils/knotc/process.c b/src/utils/knotc/process.c new file mode 100644 index 0000000..7f0502f --- /dev/null +++ b/src/utils/knotc/process.c @@ -0,0 +1,238 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <sys/stat.h> + +#include "knot/conf/conf.h" +#include "knot/common/log.h" +#include "utils/knotc/commands.h" +#include "utils/knotc/process.h" + +static const cmd_desc_t *get_cmd_desc(const char *command) +{ + /* Find requested command. */ + const cmd_desc_t *desc = cmd_table; + while (desc->name != NULL) { + if (strcmp(desc->name, command) == 0) { + break; + } + desc++; + } + if (desc->name == NULL) { + log_error("invalid command '%s'", command); + return NULL; + } + + return desc; +} + +int set_config(const cmd_desc_t *desc, params_t *params) +{ + if (params->config != NULL && params->confdb != NULL) { + log_error("ambiguous configuration source"); + return KNOT_EINVAL; + } + + /* Mask relevant flags. */ + cmd_flag_t flags = desc->flags & (CMD_FREAD | CMD_FWRITE); + cmd_flag_t mod_flags = desc->flags & (CMD_FOPT_MOD | CMD_FREQ_MOD); + + /* Choose the optimal config source. */ + struct stat st; + bool import = false; + if (flags == CMD_FNONE && params->socket != NULL) { + /* Control operation, known socket, skip configuration. */ + return KNOT_EOK; + } else if (params->confdb != NULL) { + import = false; + } else if (flags == CMD_FWRITE) { + import = false; + params->confdb = CONF_DEFAULT_DBDIR; + } else if (params->config != NULL){ + import = true; + } else if (stat(CONF_DEFAULT_DBDIR, &st) == 0) { + import = false; + params->confdb = CONF_DEFAULT_DBDIR; + } else if (stat(CONF_DEFAULT_FILE, &st) == 0) { + import = true; + params->config = CONF_DEFAULT_FILE; + } else if (flags != CMD_FNONE) { + log_error("no configuration source available"); + return KNOT_EINVAL; + } + + const char *src = import ? params->config : params->confdb; + log_debug("%s '%s'", import ? "config" : "confdb", + (src != NULL) ? src : "empty"); + + /* Prepare config flags. */ + conf_flag_t conf_flags = CONF_FNOHOSTNAME; + if (params->confdb != NULL && !(flags & CMD_FWRITE)) { + conf_flags |= CONF_FREADONLY; + } + if (import || mod_flags & CMD_FOPT_MOD) { + conf_flags |= CONF_FOPTMODULES; + } else if (mod_flags & CMD_FREQ_MOD) { + conf_flags |= CONF_FREQMODULES; + } + + /* Open confdb. */ + conf_t *new_conf = NULL; + int ret = conf_new(&new_conf, conf_schema, params->confdb, + params->max_conf_size, conf_flags); + if (ret != KNOT_EOK) { + log_error("failed to open configuration database '%s' (%s)", + (params->confdb != NULL) ? params->confdb : "", + knot_strerror(ret)); + return ret; + } + + /* Import the config file. */ + if (import) { + ret = conf_import(new_conf, params->config, true); + if (ret != KNOT_EOK) { + log_error("failed to load configuration file '%s' (%s)", + params->config, knot_strerror(ret)); + conf_free(new_conf); + return ret; + } + } + + /* Update to the new config. */ + conf_update(new_conf, CONF_UPD_FNONE); + + return KNOT_EOK; +} + +int set_ctl(knot_ctl_t **ctl, const cmd_desc_t *desc, params_t *params) +{ + if (desc == NULL) { + *ctl = NULL; + return KNOT_EINVAL; + } + + /* Mask relevant flags. */ + cmd_flag_t flags = desc->flags & (CMD_FREAD | CMD_FWRITE); + + /* Check if control socket is required. */ + if (flags != CMD_FNONE) { + *ctl = NULL; + return KNOT_EOK; + } + + /* Get control socket path. */ + char *path = NULL; + if (params->socket != NULL) { + path = strdup(params->socket); + } else { + conf_val_t listen_val = conf_get(conf(), C_CTL, C_LISTEN); + conf_val_t rundir_val = conf_get(conf(), C_SRV, C_RUNDIR); + char *rundir = conf_abs_path(&rundir_val, NULL); + path = conf_abs_path(&listen_val, rundir); + free(rundir); + } + if (path == NULL) { + log_error("empty control socket path"); + return KNOT_EINVAL; + } + + log_debug("socket '%s'", path); + + *ctl = knot_ctl_alloc(); + if (*ctl == NULL) { + free(path); + return KNOT_ENOMEM; + } + + knot_ctl_set_timeout(*ctl, params->timeout); + + int ret = knot_ctl_connect(*ctl, path); + if (ret != KNOT_EOK) { + knot_ctl_free(*ctl); + *ctl = NULL; + log_error("failed to connect to socket '%s' (%s)", path, + knot_strerror(ret)); + free(path); + return ret; + } + + free(path); + + return KNOT_EOK; +} + +void unset_ctl(knot_ctl_t *ctl) +{ + if (ctl == NULL) { + return; + } + + int ret = knot_ctl_send(ctl, KNOT_CTL_TYPE_END, NULL); + if (ret != KNOT_EOK && ret != KNOT_ECONN) { + log_error("failed to finish control (%s)", knot_strerror(ret)); + } + + knot_ctl_close(ctl); + knot_ctl_free(ctl); +} + +int process_cmd(int argc, const char **argv, params_t *params) +{ + if (argc == 0) { + return KNOT_ENOTSUP; + } + + /* Check the command name. */ + const cmd_desc_t *desc = get_cmd_desc(argv[0]); + if (desc == NULL) { + return KNOT_ENOENT; + } + + /* Check for exit. */ + if (desc->fcn == NULL) { + return KNOT_CTL_ESTOP; + } + + /* Set up the configuration. */ + int ret = set_config(desc, params); + if (ret != KNOT_EOK) { + return ret; + } + + /* Prepare command parameters. */ + cmd_args_t args = { + .desc = desc, + .argc = argc - 1, + .argv = argv + 1, + .force = params->force + }; + + /* Set control interface if necessary. */ + ret = set_ctl(&args.ctl, desc, params); + if (ret != KNOT_EOK) { + conf_update(NULL, CONF_UPD_FNONE); + return ret; + } + + /* Execute the command. */ + ret = desc->fcn(&args); + + /* Cleanup */ + unset_ctl(args.ctl); + conf_update(NULL, CONF_UPD_FNONE); + + return ret; +} diff --git a/src/utils/knotc/process.h b/src/utils/knotc/process.h new file mode 100644 index 0000000..20b5357 --- /dev/null +++ b/src/utils/knotc/process.h @@ -0,0 +1,69 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include "utils/knotc/commands.h" + +/*! Utility command line parameters. */ +typedef struct { + const char *config; + const char *confdb; + size_t max_conf_size; + const char *socket; + bool verbose; + bool force; + int timeout; +} params_t; + +/*! + * Prepares a proper configuration according to the specified command. + * + * \param[in] desc Utility command descriptor. + * \param[in] params Utility parameters. + * + * \return Error code, KNOT_EOK if successful. + */ +int set_config(const cmd_desc_t *desc, params_t *params); + +/*! + * Estabilishes a control interface if necessary. + * + * \param[in] ctl Control context. + * \param[in] desc Utility command descriptor. + * \param[in] params Utility parameters. + * + * \return Error code, KNOT_EOK if successful. + */ +int set_ctl(knot_ctl_t **ctl, const cmd_desc_t *desc, params_t *params); + +/*! + * Cleans up the control context. + * + * \param[in] ctl Control context. + */ +void unset_ctl(knot_ctl_t *ctl); + +/*! + * Processes the given utility command. + * + * \param[in] argc Number of command arguments. + * \param[in] argv Command arguments. + * \param[in] params Utility parameters. + * + * \return Error code, KNOT_EOK if successful. + */ +int process_cmd(int argc, const char **argv, params_t *params); diff --git a/src/utils/knotd/main.c b/src/utils/knotd/main.c new file mode 100644 index 0000000..b2bcc83 --- /dev/null +++ b/src/utils/knotd/main.c @@ -0,0 +1,620 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <dirent.h> +#include <fcntl.h> +#include <poll.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <getopt.h> +#include <sys/stat.h> +#include <urcu.h> + +#ifdef ENABLE_CAP_NG +#include <cap-ng.h> +#endif + +#ifdef ENABLE_SYSTEMD +#include <systemd/sd-daemon.h> +#endif + +#include "libdnssec/crypto.h" +#include "libknot/libknot.h" +#include "contrib/strtonum.h" +#include "knot/ctl/process.h" +#include "knot/conf/conf.h" +#include "knot/conf/migration.h" +#include "knot/conf/module.h" +#include "knot/common/log.h" +#include "knot/common/process.h" +#include "knot/common/stats.h" +#include "knot/server/server.h" +#include "knot/server/tcp-handler.h" +#include "knot/zone/timers.h" + +#define PROGRAM_NAME "knotd" + +/* Signal flags. */ +static volatile bool sig_req_stop = false; +static volatile bool sig_req_reload = false; + +/* \brief Signal started state to the init system. */ +static void init_signal_started(void) +{ +#ifdef ENABLE_SYSTEMD + sd_notify(0, "READY=1"); +#endif +} + +static int make_daemon(int nochdir, int noclose) +{ + int fd, ret; + + switch (fork()) { + case -1: + /* Error */ + return -1; + case 0: + /* Forked */ + break; + default: + /* Exit the main process */ + _exit(0); + } + + if (setsid() == -1) { + return -1; + } + + if (!nochdir) { + ret = chdir("/"); + if (ret == -1) + return errno; + } + + if (!noclose) { + ret = close(STDIN_FILENO); + ret += close(STDOUT_FILENO); + ret += close(STDERR_FILENO); + if (ret < 0) { + return errno; + } + + fd = open("/dev/null", O_RDWR); + if (fd == -1) { + return errno; + } + + if (dup2(fd, STDIN_FILENO) < 0) { + close(fd); + return errno; + } + if (dup2(fd, STDOUT_FILENO) < 0) { + close(fd); + return errno; + } + if (dup2(fd, STDERR_FILENO) < 0) { + close(fd); + return errno; + } + } + + return 0; +} + +struct signal { + int signum; + bool handle; +}; + +/*! \brief Signals used by the server. */ +static const struct signal SIGNALS[] = { + { SIGHUP, true }, /* Reload server. */ + { SIGINT, true }, /* Terminate server .*/ + { SIGTERM, true }, + { SIGALRM, false }, /* Internal thread synchronization. */ + { SIGPIPE, false }, /* Ignored. Some I/O errors. */ + { 0 } +}; + +/*! \brief Server signal handler. */ +static void handle_signal(int signum) +{ + switch (signum) { + case SIGHUP: + sig_req_reload = true; + break; + case SIGINT: + case SIGTERM: + if (sig_req_stop) { + exit(EXIT_FAILURE); + } + sig_req_stop = true; + break; + default: + /* ignore */ + break; + } +} + +/*! \brief Setup signal handlers and blocking mask. */ +static void setup_signals(void) +{ + /* Block all signals. */ + static sigset_t all; + sigfillset(&all); + sigdelset(&all, SIGPROF); + pthread_sigmask(SIG_SETMASK, &all, NULL); + + /* Setup handlers. */ + struct sigaction action = { .sa_handler = handle_signal }; + for (const struct signal *s = SIGNALS; s->signum > 0; s++) { + sigaction(s->signum, &action, NULL); + } +} + +/*! \brief Unblock server control signals. */ +static void enable_signals(void) +{ + sigset_t mask; + sigemptyset(&mask); + + for (const struct signal *s = SIGNALS; s->signum > 0; s++) { + if (s->handle) { + sigaddset(&mask, s->signum); + } + } + + pthread_sigmask(SIG_UNBLOCK, &mask, NULL); +} + +/*! \brief Drop POSIX 1003.1e capabilities. */ +static void drop_capabilities(void) +{ +#ifdef ENABLE_CAP_NG + /* Drop all capabilities. */ + if (capng_have_capability(CAPNG_EFFECTIVE, CAP_SETPCAP)) { + capng_clear(CAPNG_SELECT_BOTH); + + /* Apply. */ + if (capng_apply(CAPNG_SELECT_BOTH) < 0) { + log_error("failed to set process capabilities (%s)", + strerror(errno)); + } + } else { + log_info("process not allowed to set capabilities, skipping"); + } +#endif /* ENABLE_CAP_NG */ +} + +/*! \brief Event loop listening for signals and remote commands. */ +static void event_loop(server_t *server, const char *socket) +{ + knot_ctl_t *ctl = knot_ctl_alloc(); + if (ctl == NULL) { + log_fatal("control, failed to initialize (%s)", + knot_strerror(KNOT_ENOMEM)); + return; + } + + // Set control timeout. + knot_ctl_set_timeout(ctl, conf()->cache.ctl_timeout); + + /* Get control socket configuration. */ + char *listen; + if (socket == NULL) { + conf_val_t listen_val = conf_get(conf(), C_CTL, C_LISTEN); + conf_val_t rundir_val = conf_get(conf(), C_SRV, C_RUNDIR); + char *rundir = conf_abs_path(&rundir_val, NULL); + listen = conf_abs_path(&listen_val, rundir); + free(rundir); + } else { + listen = strdup(socket); + } + if (listen == NULL) { + knot_ctl_free(ctl); + log_fatal("control, empty socket path"); + return; + } + + log_info("control, binding to '%s'", listen); + + /* Bind the control socket. */ + int ret = knot_ctl_bind(ctl, listen); + if (ret != KNOT_EOK) { + knot_ctl_free(ctl); + log_fatal("control, failed to bind socket '%s' (%s)", + listen, knot_strerror(ret)); + free(listen); + return; + } + free(listen); + + enable_signals(); + + /* Run event loop. */ + for (;;) { + /* Interrupts. */ + if (sig_req_stop) { + break; + } + if (sig_req_reload) { + sig_req_reload = false; + server_reload(server); + } + + // Update control timeout. + knot_ctl_set_timeout(ctl, conf()->cache.ctl_timeout); + + ret = knot_ctl_accept(ctl); + if (ret != KNOT_EOK) { + continue; + } + + ret = ctl_process(ctl, server); + knot_ctl_close(ctl); + if (ret == KNOT_CTL_ESTOP) { + break; + } + } + + /* Unbind the control socket. */ + knot_ctl_unbind(ctl); + knot_ctl_free(ctl); +} + +static void print_help(void) +{ + printf("Usage: %s [parameters]\n" + "\n" + "Parameters:\n" + " -c, --config <file> Use a textual configuration file.\n" + " (default %s)\n" + " -C, --confdb <dir> Use a binary configuration database directory.\n" + " (default %s)\n" + " -m, --max-conf-size <MiB> Set maximum configuration size (max 10000 MiB).\n" + " (default %d MiB)\n" + " -s, --socket <path> Use a remote control UNIX socket path.\n" + " (default %s)\n" + " -d, --daemonize=[dir] Run the server as a daemon (with new root directory).\n" + " -v, --verbose Enable debug output.\n" + " -h, --help Print the program help.\n" + " -V, --version Print the program version.\n", + PROGRAM_NAME, CONF_DEFAULT_FILE, CONF_DEFAULT_DBDIR, + CONF_MAPSIZE, RUN_DIR "/knot.sock"); +} + +static void print_version(void) +{ + printf("%s (Knot DNS), version %s\n", PROGRAM_NAME, PACKAGE_VERSION); +} + +static int set_config(const char *confdb, const char *config, size_t max_conf_size) +{ + if (config != NULL && confdb != NULL) { + log_fatal("ambiguous configuration source"); + return KNOT_EINVAL; + } + + /* Choose the optimal config source. */ + struct stat st; + bool import = false; + if (confdb != NULL) { + import = false; + } else if (config != NULL){ + import = true; + } else if (stat(CONF_DEFAULT_DBDIR, &st) == 0) { + import = false; + confdb = CONF_DEFAULT_DBDIR; + } else { + import = true; + config = CONF_DEFAULT_FILE; + } + + const char *src = import ? config : confdb; + log_debug("%s '%s'", import ? "config" : "confdb", + (src != NULL) ? src : "empty"); + + /* Open confdb. */ + conf_t *new_conf = NULL; + int ret = conf_new(&new_conf, conf_schema, confdb, max_conf_size, CONF_FREQMODULES); + if (ret != KNOT_EOK) { + log_fatal("failed to open configuration database '%s' (%s)", + (confdb != NULL) ? confdb : "", knot_strerror(ret)); + return ret; + } + + /* Import the config file. */ + if (import) { + ret = conf_import(new_conf, config, true); + if (ret != KNOT_EOK) { + log_fatal("failed to load configuration file '%s' (%s)", + config, knot_strerror(ret)); + conf_free(new_conf); + return ret; + } + } + + // Migrate from old schema. + ret = conf_migrate(new_conf); + if (ret != KNOT_EOK) { + log_error("failed to migrate configuration (%s)", knot_strerror(ret)); + } + + /* Update to the new config. */ + conf_update(new_conf, CONF_UPD_FNONE); + + return KNOT_EOK; +} + +static void write_timers(const zone_t *zone, knot_db_txn_t *txn, int *ret) +{ + if (*ret == KNOT_EOK) { + *ret = zone_timers_write(NULL, zone->name, &zone->timers, txn); + } +} + +static void update_timerdb(server_t *server) +{ + if (server->timers_db == NULL) { + return; + } + + log_info("updating persistent timer DB"); + + knot_db_txn_t txn; + int ret = zone_timers_write_begin(server->timers_db, &txn); + if (ret == KNOT_EOK) { + knot_zonedb_foreach(server->zone_db, write_timers, &txn, &ret); + } + if (ret == KNOT_EOK) { + ret = zone_timers_write_end(&txn); + } + if (ret != KNOT_EOK) { + log_warning("failed to update persistent timer DB (%s)", + knot_strerror(ret)); + } +} + +int main(int argc, char **argv) +{ + bool daemonize = false; + const char *config = NULL; + const char *confdb = NULL; + size_t max_conf_size = (size_t)CONF_MAPSIZE * 1024 * 1024; + const char *daemon_root = "/"; + char *socket = NULL; + bool verbose = false; + + /* Long options. */ + struct option opts[] = { + { "config", required_argument, NULL, 'c' }, + { "confdb", required_argument, NULL, 'C' }, + { "max-conf-size", required_argument, NULL, 'm' }, + { "socket", required_argument, NULL, 's' }, + { "daemonize", optional_argument, NULL, 'd' }, + { "verbose", no_argument, NULL, 'v' }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'V' }, + { NULL } + }; + + /* Parse command line arguments. */ + int opt = 0; + while ((opt = getopt_long(argc, argv, "c:C:m:s:dvhV", opts, NULL)) != -1) { + switch (opt) { + case 'c': + config = optarg; + break; + case 'C': + confdb = optarg; + break; + case 'm': + if (str_to_size(optarg, &max_conf_size, 1, 10000) != KNOT_EOK) { + print_help(); + return EXIT_FAILURE; + } + /* Convert to bytes. */ + max_conf_size *= 1024 * 1024; + break; + case 's': + socket = optarg; + break; + case 'd': + daemonize = true; + if (optarg) { + daemon_root = optarg; + } + break; + case 'v': + verbose = true; + break; + case 'h': + print_help(); + return EXIT_SUCCESS; + case 'V': + print_version(); + return EXIT_SUCCESS; + default: + print_help(); + return EXIT_FAILURE; + } + } + + /* Check for non-option parameters. */ + if (argc - optind > 0) { + print_help(); + return EXIT_FAILURE; + } + + /* Set file creation mask to remove all permissions for others. */ + umask(S_IROTH|S_IWOTH|S_IXOTH); + + /* Now check if we want to daemonize. */ + if (daemonize) { + if (make_daemon(1, 0) != 0) { + fprintf(stderr, "Daemonization failed, shutting down...\n"); + return EXIT_FAILURE; + } + } + + /* Setup base signal handling. */ + setup_signals(); + + /* Initialize cryptographic backend. */ + dnssec_crypto_init(); + atexit(dnssec_crypto_cleanup); + + /* Initialize pseudorandom number generator. */ + srand(time(NULL)); + + /* Initialize logging subsystem. */ + log_init(); + if (verbose) { + log_levels_add(LOG_TARGET_STDOUT, LOG_SOURCE_ANY, LOG_MASK(LOG_DEBUG)); + } + + /* Set up the configuration */ + int ret = set_config(confdb, config, max_conf_size); + if (ret != KNOT_EOK) { + log_close(); + return EXIT_FAILURE; + } + + /* Reconfigure logging. */ + log_reconfigure(conf()); + + /* Initialize server. */ + server_t server; + ret = server_init(&server, conf_bg_threads(conf())); + if (ret != KNOT_EOK) { + log_fatal("failed to initialize server (%s)", knot_strerror(ret)); + conf_free(conf()); + log_close(); + return EXIT_FAILURE; + } + + /* Reconfigure server interfaces. + * @note This MUST be done before we drop privileges. */ + server_reconfigure(conf(), &server); + + /* Alter privileges. */ + int uid, gid; + if (conf_user(conf(), &uid, &gid) != KNOT_EOK || + log_update_privileges(uid, gid) != KNOT_EOK || + proc_update_privileges(uid, gid) != KNOT_EOK) { + log_fatal("failed to drop privileges"); + server_wait(&server); + server_deinit(&server); + conf_free(conf()); + log_close(); + return EXIT_FAILURE; + } + + /* Drop POSIX capabilities. */ + drop_capabilities(); + + /* Activate global query modules. */ + conf_activate_modules(conf(), NULL, conf()->query_modules, + &conf()->query_plan); + + /* Check and create PID file. */ + long pid = (long)getpid(); + if (daemonize) { + char *pidfile = pid_check_and_create(); + if (pidfile == NULL) { + server_wait(&server); + server_deinit(&server); + conf_free(conf()); + log_close(); + return EXIT_FAILURE; + } + + log_info("PID stored in '%s'", pidfile); + free(pidfile); + if (chdir(daemon_root) != 0) { + log_warning("failed to change working directory to %s", + daemon_root); + } else { + log_info("changed directory to %s", daemon_root); + } + } + + /* Now we're going multithreaded. */ + rcu_register_thread(); + + /* Populate zone database. */ + log_info("loading %zu zones", conf_id_count(conf(), C_ZONE)); + server_update_zones(conf(), &server); + + /* Check number of loaded zones. */ + if (knot_zonedb_size(server.zone_db) == 0) { + log_warning("no zones loaded"); + } + + stats_reconfigure(conf(), &server); + + /* Start it up. */ + log_info("starting server"); + conf_val_t async_val = conf_get(conf(), C_SRV, C_ASYNC_START); + ret = server_start(&server, conf_bool(&async_val)); + if (ret != KNOT_EOK) { + log_fatal("failed to start server (%s)", knot_strerror(ret)); + server_wait(&server); + stats_deinit(); + server_deinit(&server); + rcu_unregister_thread(); + pid_cleanup(); + log_close(); + conf_free(conf()); + return EXIT_FAILURE; + } + + if (daemonize) { + log_info("server started as a daemon, PID %ld", pid); + } else { + log_info("server started in the foreground, PID %ld", pid); + init_signal_started(); + } + + /* Start the event loop. */ + event_loop(&server, socket); + + /* Teardown server. */ + server_stop(&server); + server_wait(&server); + stats_deinit(); + + /* Update timers database. */ + update_timerdb(&server); + + /* Cleanup PID file. */ + pid_cleanup(); + + /* Free server and configuration. */ + server_deinit(&server); + conf_free(conf()); + + /* Unhook from RCU. */ + rcu_unregister_thread(); + + log_info("shutting down"); + log_close(); + + return EXIT_SUCCESS; +} diff --git a/src/utils/knsec3hash/knsec3hash.c b/src/utils/knsec3hash/knsec3hash.c new file mode 100644 index 0000000..b901676 --- /dev/null +++ b/src/utils/knsec3hash/knsec3hash.c @@ -0,0 +1,184 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <assert.h> +#include <getopt.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> + +#include "contrib/base32hex.h" +#include "contrib/strtonum.h" +#include "libdnssec/error.h" +#include "libdnssec/nsec.h" +#include "libdnssec/shared/dname.h" +#include "libdnssec/shared/hex.h" +#include "libknot/libknot.h" + +#define PROGRAM_NAME "knsec3hash" +#define error(fmt, ...) fprintf(stderr, fmt "\n", ##__VA_ARGS__) + +/*! + * \brief Print program help (and example). + */ +static void print_help(void) +{ + printf("Usage: " PROGRAM_NAME " <salt> <algorithm> <iterations> <domain-name>\n"); + printf("Example: " PROGRAM_NAME " c01dcafe 1 10 knot-dns.cz\n"); +} + +/*! + * \brief Print program version. + */ +static void print_version(void) +{ + printf("%s (Knot DNS), version %s\n", PROGRAM_NAME, PACKAGE_VERSION); +} + +/*! + * \brief Parse NSEC3 salt. + */ +static int str_to_salt(const char *str, dnssec_binary_t *salt) +{ + if (strcmp(str, "-") == 0) { + salt->size = 0; + return DNSSEC_EOK; + } else { + return hex_to_bin(str, salt); + } +} + +/*! + * \brief Parse NSEC3 parameters and fill structure with NSEC3 parameters. + */ +static bool parse_nsec3_params(dnssec_nsec3_params_t *params, const char *salt_str, + const char *algorithm_str, const char *iterations_str) +{ + uint8_t algorithm = 0; + int r = str_to_u8(algorithm_str, &algorithm); + if (r != KNOT_EOK) { + error("Invalid algorithm number."); + return false; + } + + uint16_t iterations = 0; + r = str_to_u16(iterations_str, &iterations); + if (r != KNOT_EOK) { + error("Invalid iteration count."); + return false; + } + + dnssec_binary_t salt = { 0 }; + r = str_to_salt(salt_str, &salt); + if (r != DNSSEC_EOK) { + error("Invalid salt, %s.", knot_strerror(r)); + return false; + } + + if (salt.size > UINT8_MAX) { + error("Invalid salt, maximum length is %d bytes.", UINT8_MAX); + dnssec_binary_free(&salt); + return false; + } + + params->algorithm = algorithm; + params->iterations = iterations; + params->salt = salt; + params->flags = 0; + + return true; +} + +/*! + * \brief Entry point of 'knsec3hash'. + */ +int main(int argc, char *argv[]) +{ + struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'V' }, + { NULL } + }; + + int opt = 0; + while ((opt = getopt_long(argc, argv, "hV", options, NULL)) != -1) { + switch(opt) { + case 'h': + print_help(); + return 0; + case 'V': + print_version(); + return 0; + default: + print_help(); + return 1; + } + } + + // knsec3hash <salt> <algorithm> <iterations> <domain> + if (argc != 5) { + print_help(); + return 1; + } + + int exit_code = 1; + dnssec_nsec3_params_t nsec3_params = { 0 }; + + dnssec_binary_t dname = { 0 }; + dnssec_binary_t digest = { 0 }; + dnssec_binary_t digest_print = { 0 }; + + if (!parse_nsec3_params(&nsec3_params, argv[1], argv[2], argv[3])) { + goto fail; + } + + dname.data = knot_dname_from_str_alloc(argv[4]); + if (dname.data == NULL) { + error("Cannot parse domain name."); + goto fail; + } + dname.size = dname_length(dname.data); + + dname_normalize(dname.data); + + int r = dnssec_nsec3_hash(&dname, &nsec3_params, &digest); + if (r != DNSSEC_EOK) { + error("Cannot compute NSEC3 hash, %s.", knot_strerror(r)); + goto fail; + } + + r = base32hex_encode_alloc(digest.data, digest.size, &digest_print.data); + if (r < 0) { + error("Cannot encode computed hash, %s.", knot_strerror(r)); + goto fail; + } + digest_print.size = r; + + exit_code = 0; + + printf("%.*s (salt=%s, hash=%d, iterations=%d)\n", (int)digest_print.size, + digest_print.data, argv[1], nsec3_params.algorithm, + nsec3_params.iterations); + +fail: + dnssec_nsec3_params_free(&nsec3_params); + dnssec_binary_free(&dname); + dnssec_binary_free(&digest); + dnssec_binary_free(&digest_print); + + return exit_code; +} diff --git a/src/utils/knsupdate/knsupdate_exec.c b/src/utils/knsupdate/knsupdate_exec.c new file mode 100644 index 0000000..a92de88 --- /dev/null +++ b/src/utils/knsupdate/knsupdate_exec.c @@ -0,0 +1,1058 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <assert.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/socket.h> +#include <unistd.h> + +#include "libdnssec/random.h" +#include "utils/knsupdate/knsupdate_exec.h" +#include "utils/common/exec.h" +#include "utils/common/msg.h" +#include "utils/common/netio.h" +#include "utils/common/params.h" +#include "utils/common/sign.h" +#include "utils/common/token.h" +#include "libknot/libknot.h" +#include "contrib/ctype.h" +#include "contrib/getline.h" +#include "contrib/macros.h" +#include "contrib/string.h" +#include "contrib/strtonum.h" +#include "contrib/openbsd/strlcpy.h" + +/* Declarations of cmd parse functions. */ +typedef int (*cmd_handle_f)(const char *lp, knsupdate_params_t *params); +int cmd_add(const char* lp, knsupdate_params_t *params); +int cmd_answer(const char* lp, knsupdate_params_t *params); +int cmd_class(const char* lp, knsupdate_params_t *params); +int cmd_debug(const char* lp, knsupdate_params_t *params); +int cmd_del(const char* lp, knsupdate_params_t *params); +int cmd_gsstsig(const char* lp, knsupdate_params_t *params); +int cmd_key(const char* lp, knsupdate_params_t *params); +int cmd_local(const char* lp, knsupdate_params_t *params); +int cmd_nxdomain(const char *lp, knsupdate_params_t *params); +int cmd_nxrrset(const char *lp, knsupdate_params_t *params); +int cmd_oldgsstsig(const char* lp, knsupdate_params_t *params); +int cmd_origin(const char* lp, knsupdate_params_t *params); +int cmd_prereq(const char* lp, knsupdate_params_t *params); +int cmd_quit(const char* lp, knsupdate_params_t *params); +int cmd_realm(const char* lp, knsupdate_params_t *params); +int cmd_send(const char* lp, knsupdate_params_t *params); +int cmd_server(const char* lp, knsupdate_params_t *params); +int cmd_show(const char* lp, knsupdate_params_t *params); +int cmd_ttl(const char* lp, knsupdate_params_t *params); +int cmd_update(const char* lp, knsupdate_params_t *params); +int cmd_yxdomain(const char *lp, knsupdate_params_t *params); +int cmd_yxrrset(const char *lp, knsupdate_params_t *params); +int cmd_zone(const char* lp, knsupdate_params_t *params); + +/* Sorted list of commands. + * This way we could identify command byte-per-byte and + * cancel early if the next is lexicographically greater. + */ +const char* cmd_array[] = { + "\x3" "add", + "\x6" "answer", + "\x5" "class", /* {classname} */ + "\x5" "debug", + "\x3" "del", + "\x6" "delete", + "\x7" "gsstsig", + "\x3" "key", /* {[alg:]name} {secret} */ + "\x5" "local", /* {address} [port] */ + "\x8" "nxdomain", + "\x7" "nxrrset", + "\xa" "oldgsstsig", + "\x6" "origin", /* {name} */ + "\x6" "prereq", /* (nx|yx)(domain|rrset) {domain-name} ... */ + "\x4" "quit", + "\x5" "realm", /* {[realm_name]} */ + "\x4" "send", + "\x6" "server", /* {servername} [port] */ + "\x4" "show", + "\x3" "ttl", /* {seconds} */ + "\x6" "update", /* (add|delete) {domain-name} ... */ + "\x8" "yxdomain", + "\x7" "yxrrset", + "\x4" "zone", /* {zonename} */ + NULL +}; + +cmd_handle_f cmd_handle[] = { + cmd_add, + cmd_answer, + cmd_class, + cmd_debug, + cmd_del, + cmd_del, /* delete/del synonyms */ + cmd_gsstsig, + cmd_key, + cmd_local, + cmd_nxdomain, + cmd_nxrrset, + cmd_oldgsstsig, + cmd_origin, + cmd_prereq, + cmd_quit, + cmd_realm, + cmd_send, + cmd_server, + cmd_show, + cmd_ttl, + cmd_update, + cmd_yxdomain, + cmd_yxrrset, + cmd_zone, +}; + +/* {prereq} command table. */ +const char* pq_array[] = { + "\x8" "nxdomain", + "\x7" "nxrrset", + "\x8" "yxdomain", + "\x7" "yxrrset", + NULL +}; + +enum { + PQ_NXDOMAIN = 0, + PQ_NXRRSET, + PQ_YXDOMAIN, + PQ_YXRRSET +}; + +/* RR parser flags */ +enum { + PARSE_NODEFAULT = 1 << 0, /* Do not fill defaults. */ + PARSE_NAMEONLY = 1 << 1, /* Parse only name. */ + PARSE_NOTTL = 1 << 2 /* Ignore TTL item. */ +}; + +static bool dname_isvalid(const char *lp) +{ + knot_dname_t *dn = knot_dname_from_str_alloc(lp); + if (dn == NULL) { + return false; + } + knot_dname_free(dn, NULL); + return true; +} + +/* This is probably redundant, but should be a bit faster so let's keep it. */ +static int parse_full_rr(zs_scanner_t *s, const char* lp) +{ + if (zs_set_input_string(s, lp, strlen(lp)) != 0 || + zs_parse_all(s) != 0) { + ERR("invalid record (%s)\n", zs_strerror(s->error.code)); + return KNOT_EPARSEFAIL; + } + + /* Class must not differ from specified. */ + if (s->r_class != s->default_class) { + char cls_s[16] = {0}; + knot_rrclass_to_string(s->default_class, cls_s, sizeof(cls_s)); + ERR("class mismatch '%s'\n", cls_s); + return KNOT_EPARSEFAIL; + } + + return KNOT_EOK; +} + +static int parse_partial_rr(zs_scanner_t *s, const char *lp, unsigned flags) +{ + /* Extract owner. */ + size_t len = strcspn(lp, SEP_CHARS); + char *owner_str = calloc(1, len + 2); // 2 ~ ('.' + '\0') + memcpy(owner_str, lp, len); + + /* Make dname FQDN if it isn't. */ + bool fqdn = true; + if (owner_str[len - 1] != '.') { + owner_str[len] = '.'; + fqdn = false; + } + + knot_dname_t *owner = knot_dname_from_str_alloc(owner_str); + free(owner_str); + if (owner == NULL) { + return KNOT_ENOMEM; + } + + s->r_owner_length = knot_dname_size(owner); + memcpy(s->r_owner, owner, s->r_owner_length); + knot_dname_free(owner, NULL); + + /* Append origin if not FQDN. */ + if (!fqdn) { + s->r_owner_length--; + memcpy(s->r_owner + s->r_owner_length, s->zone_origin, + s->zone_origin_length); + s->r_owner_length += s->zone_origin_length; + } + + lp = tok_skipspace(lp + len); + + /* Initialize */ + s->r_type = KNOT_RRTYPE_ANY; + s->r_class = s->default_class; + s->r_data_length = 0; + if (flags & PARSE_NODEFAULT) { + s->r_ttl = 0; + } else { + s->r_ttl = s->default_ttl; + } + + /* Parse only name? */ + if (flags & PARSE_NAMEONLY) { + if (*lp != '\0') { + WARN("ignoring input data '%s'\n", lp); + } + return KNOT_EOK; + } + + /* Now there could be [ttl] [class] [type [data...]]. */ + char *np = NULL; + long ttl = strtol(lp, &np, 10); + if (ttl >= 0 && np && (*np == '\0' || is_space(*np))) { + DBG("%s: parsed ttl=%lu\n", __func__, ttl); + if (flags & PARSE_NOTTL) { + WARN("ignoring TTL value '%ld'\n", ttl); + } else { + s->r_ttl = ttl; + } + lp = tok_skipspace(np); + } + + uint16_t num; + char *buff = NULL; + char *cls = NULL; + char *type = NULL; + + /* Try to find class. */ + len = strcspn(lp, SEP_CHARS); + if (len > 0) { + buff = strndup(lp, len); + } + + if (knot_rrclass_from_string(buff, &num) == 0) { + /* Class must not differ from specified. */ + if (num != s->default_class) { + ERR("class mismatch '%s'\n", buff); + free(buff); + return KNOT_EPARSEFAIL; + } + cls = buff; + buff = NULL; + s->r_class = num; + DBG("%s: parsed class=%u '%s'\n", __func__, s->r_class, cls); + lp = tok_skipspace(lp + len); + } + + /* Try to parser type. */ + if (cls != NULL) { + len = strcspn(lp, SEP_CHARS); + if (len > 0) { + buff = strndup(lp, len); + } + } + if (knot_rrtype_from_string(buff, &num) == 0) { + type = buff; + buff = NULL; + s->r_type = num; + DBG("%s: parsed type=%u '%s'\n", __func__, s->r_type, type); + lp = tok_skipspace(lp + len); + } + + free(buff); + + /* Remainder */ + if (*lp == '\0') { + free(cls); + free(type); + return KNOT_EOK; + } + + /* Need to parse rdata, synthetize input. */ + char *rr = sprintf_alloc(" %u IN %s %s\n", s->r_ttl, type, lp); + free(cls); + free(type); + if (rr == NULL) { + return KNOT_ENOMEM; + } + if (zs_set_input_string(s, rr, strlen(rr)) != 0 || + zs_parse_all(s) != 0) { + ERR("invalid rdata (%s)\n", zs_strerror(s->error.code)); + return KNOT_EPARSEFAIL; + } + free(rr); + + return KNOT_EOK; +} + +static srv_info_t *parse_host(const char *lp, const char* default_port) +{ + /* Extract server address. */ + srv_info_t *srv = NULL; + size_t len = strcspn(lp, SEP_CHARS); + char *addr = strndup(lp, len); + if (!addr) return NULL; + DBG("%s: parsed addr: %s\n", __func__, addr); + + /* Store port/service if present. */ + lp = tok_skipspace(lp + len); + if (*lp == '\0') { + srv = srv_info_create(addr, default_port); + free(addr); + return srv; + } + + len = strcspn(lp, SEP_CHARS); + char *port = strndup(lp, len); + if (!port) { + free(addr); + return NULL; + } + DBG("%s: parsed port: %s\n", __func__, port); + + /* Create server struct. */ + srv = srv_info_create(addr, port); + free(addr); + free(port); + return srv; +} + +/* Append parsed RRSet to list. */ +static int rr_list_append(zs_scanner_t *s, list_t *target_list, knot_mm_t *mm) +{ + knot_rrset_t *rr = knot_rrset_new(s->r_owner, s->r_type, s->r_class, + s->r_ttl, NULL); + if (!rr) { + DBG("%s: failed to create rrset\n", __func__); + return KNOT_ENOMEM; + } + + /* Create RDATA. */ + int ret = knot_rrset_add_rdata(rr, s->r_data, s->r_data_length, NULL); + if (ret != KNOT_EOK) { + DBG("%s: failed to set rrset from wire (%s)\n", + __func__, knot_strerror(ret)); + knot_rrset_free(rr, NULL); + return ret; + } + + if (ptrlist_add(target_list, rr, mm) == NULL) { + knot_rrset_free(rr, NULL); + return KNOT_ENOMEM; + } + + return KNOT_EOK; +} + +/*! \brief Write RRSet list to packet section. */ +static int rr_list_to_packet(knot_pkt_t *dst, list_t *list) +{ + assert(dst != NULL); + assert(list != NULL); + + ptrnode_t *node = NULL; + WALK_LIST(node, *list) { + int ret = knot_pkt_put(dst, KNOT_COMPR_HINT_NONE, + (knot_rrset_t *)node->d, 0); + if (ret != KNOT_EOK) { + return ret; + } + } + + return KNOT_EOK; +} + +/*! \brief Build UPDATE query. */ +static int build_query(knsupdate_params_t *params) +{ + /* Clear old query. */ + knot_pkt_t *query = params->query; + knot_pkt_clear(query); + + /* Write question. */ + knot_wire_set_id(query->wire, dnssec_random_uint16_t()); + knot_wire_set_opcode(query->wire, KNOT_OPCODE_UPDATE); + knot_dname_t *qname = knot_dname_from_str_alloc(params->zone); + int ret = knot_pkt_put_question(query, qname, params->class_num, + params->type_num); + knot_dname_free(qname, NULL); + if (ret != KNOT_EOK) { + return ret; + } + + /* Now, PREREQ => ANSWER section. */ + ret = knot_pkt_begin(query, KNOT_ANSWER); + if (ret != KNOT_EOK) { + return ret; + } + + /* Write PREREQ. */ + ret = rr_list_to_packet(query, ¶ms->prereq_list); + if (ret != KNOT_EOK) { + return ret; + } + + /* Now, UPDATE data => AUTHORITY section. */ + ret = knot_pkt_begin(query, KNOT_AUTHORITY); + if (ret != KNOT_EOK) { + return ret; + } + + /* Write UPDATE data. */ + return rr_list_to_packet(query, ¶ms->update_list); +} + +static int pkt_sendrecv(knsupdate_params_t *params) +{ + net_t net; + int ret; + + ret = net_init(params->srcif, + params->server, + get_iptype(params->ip), + get_socktype(params->protocol, KNOT_RRTYPE_SOA), + params->wait, + NET_FLAGS_NONE, + NULL, + &net); + if (ret != KNOT_EOK) { + return -1; + } + + ret = net_connect(&net); + DBG("%s: send_msg = %d\n", __func__, net.sockfd); + if (ret != KNOT_EOK) { + net_clean(&net); + return -1; + } + + ret = net_send(&net, params->query->wire, params->query->size); + if (ret != KNOT_EOK) { + net_close(&net); + net_clean(&net); + return -1; + } + + /* Clear response buffer. */ + knot_pkt_clear(params->answer); + + /* Wait for reception. */ + int rb = net_receive(&net, params->answer->wire, params->answer->max_size); + DBG("%s: receive_msg = %d\n", __func__, rb); + if (rb <= 0) { + net_close(&net); + net_clean(&net); + return -1; + } else { + params->answer->size = rb; + } + + net_close(&net); + net_clean(&net); + + return rb; +} + +static int process_line(char *lp, void *arg) +{ + knsupdate_params_t *params = (knsupdate_params_t *)arg; + + /* Check for empty line or comment. */ + if (lp[0] == '\0' || lp[0] == ';') { + return KNOT_EOK; + } + + int ret = tok_find(lp, cmd_array); + if (ret < 0) { + return ret; /* Syntax error - do nothing. */ + } + + const char *cmd = cmd_array[ret]; + const char *val = tok_skipspace(lp + TOK_L(cmd)); + ret = cmd_handle[ret](val, params); + if (ret != KNOT_EOK) { + DBG("operation '%s' failed (%s) on line '%s'\n", + TOK_S(cmd), knot_strerror(ret), lp); + } + + return ret; +} + +static bool is_terminal(FILE *file) +{ + int fd = fileno(file); + assert(fd >= 0); + return isatty(fd); +} + +static int process_lines(knsupdate_params_t *params, FILE *input) +{ + char *buf = NULL; + size_t buflen = 0; + bool interactive = is_terminal(input); + int ret = KNOT_EOK; + + /* Print first program prompt if interactive. */ + if (interactive) { + fprintf(stderr, "> "); + } + + /* Process lines. */ + while (!params->stop && knot_getline(&buf, &buflen, input) != -1) { + /* Remove leading and trailing white space. */ + char *line = strstrip(buf); + int call_ret = process_line(line, params); + memset(line, 0, strlen(line)); + free(line); + if (call_ret != KNOT_EOK) { + /* Return the first error. */ + if (ret == KNOT_EOK) { + ret = call_ret; + } + + /* Exit if error and not interactive. */ + if (!interactive) { + break; + } + } + + /* Print program prompt if interactive. */ + if (interactive && !params->stop) { + fprintf(stderr, "> "); + } + } + + if (interactive && feof(input)) { + /* Terminate line after empty prompt. */ + fprintf(stderr, "\n"); + } + + if (buf != NULL) { + memset(buf, 0, buflen); + free(buf); + } + + return ret; +} + +int knsupdate_exec(knsupdate_params_t *params) +{ + if (!params) { + return KNOT_EINVAL; + } + + int ret = KNOT_EOK; + + /* If no file specified, use stdin. */ + if (EMPTY_LIST(params->qfiles)) { + ret = process_lines(params, stdin); + } + + /* Read from each specified file. */ + ptrnode_t *n = NULL; + WALK_LIST(n, params->qfiles) { + const char *filename = (const char*)n->d; + if (strcmp(filename, "-") == 0) { + ret = process_lines(params, stdin); + if (ret != KNOT_EOK) { + break; + } + continue; + } + + FILE *fp = fopen(filename, "r"); + if (!fp) { + ERR("failed to open '%s' (%s)\n", + filename, strerror(errno)); + ret = KNOT_EFILE; + break; + } + ret = process_lines(params, fp); + fclose(fp); + if (ret != KNOT_EOK) { + break; + } + } + + return ret; +} + +int cmd_update(const char* lp, knsupdate_params_t *params) +{ + DBG("%s: lp='%s'\n", __func__, lp); + + /* update is optional token, next add|del|delete */ + int bp = tok_find(lp, cmd_array); + if (bp < 0) return bp; /* Syntax error. */ + + /* allow only specific tokens */ + cmd_handle_f *h = cmd_handle; + if (h[bp] != cmd_add && h[bp] != cmd_del) { + ERR("unexpected token '%s' after 'update', allowed: '%s'\n", + lp, "{add|del|delete}"); + return KNOT_EPARSEFAIL; + } + + return h[bp](tok_skipspace(lp + TOK_L(cmd_array[bp])), params); +} + +int cmd_add(const char* lp, knsupdate_params_t *params) +{ + DBG("%s: lp='%s'\n", __func__, lp); + + if (parse_full_rr(¶ms->parser, lp) != KNOT_EOK) { + return KNOT_EPARSEFAIL; + } + + return rr_list_append(¶ms->parser, ¶ms->update_list, ¶ms->mm); +} + +int cmd_del(const char* lp, knsupdate_params_t *params) +{ + DBG("%s: lp='%s'\n", __func__, lp); + + zs_scanner_t *rrp = ¶ms->parser; + int ret = parse_partial_rr(rrp, lp, PARSE_NODEFAULT); + if (ret != KNOT_EOK) { + return ret; + } + + /* Check owner name. */ + if (rrp->r_owner_length == 0) { + ERR("failed to parse owner name '%s'\n", lp); + return KNOT_EPARSEFAIL; + } + + rrp->r_ttl = 0; /* Set TTL = 0 when deleting. */ + + /* When deleting whole RRSet, use ANY class */ + if (rrp->r_data_length == 0) { + rrp->r_class = KNOT_CLASS_ANY; + } else { + rrp->r_class = KNOT_CLASS_NONE; + } + + return rr_list_append(rrp, ¶ms->update_list, ¶ms->mm); +} + +int cmd_class(const char* lp, knsupdate_params_t *params) +{ + DBG("%s: lp='%s'\n", __func__, lp); + + uint16_t cls; + + if (knot_rrclass_from_string(lp, &cls) != 0) { + ERR("failed to parse class '%s'\n", lp); + return KNOT_EPARSEFAIL; + } + + params->class_num = cls; + params->parser.default_class = params->class_num; + + return KNOT_EOK; +} + +int cmd_ttl(const char* lp, knsupdate_params_t *params) +{ + DBG("%s: lp='%s'\n", __func__, lp); + + uint32_t ttl = 0; + + if (str_to_u32(lp, &ttl) != KNOT_EOK) { + ERR("failed to parse ttl '%s'\n", lp); + return KNOT_EPARSEFAIL; + } + + return knsupdate_set_ttl(params, ttl); +} + +int cmd_debug(const char* lp, knsupdate_params_t *params) +{ + UNUSED(params); + DBG("%s: lp='%s'\n", __func__, lp); + + msg_enable_debug(1); + + return KNOT_EOK; +} + +int cmd_nxdomain(const char *lp, knsupdate_params_t *params) +{ + DBG("%s: lp='%s'\n", __func__, lp); + + zs_scanner_t *s = ¶ms->parser; + int ret = parse_partial_rr(s, lp, PARSE_NODEFAULT | PARSE_NAMEONLY); + if (ret != KNOT_EOK) { + return ret; + } + + s->r_ttl = 0; + s->r_class = KNOT_CLASS_NONE; + + return rr_list_append(s, ¶ms->prereq_list, ¶ms->mm); +} + +int cmd_yxdomain(const char *lp, knsupdate_params_t *params) +{ + DBG("%s: lp='%s'\n", __func__, lp); + + zs_scanner_t *s = ¶ms->parser; + int ret = parse_partial_rr(s, lp, PARSE_NODEFAULT | PARSE_NAMEONLY); + if (ret != KNOT_EOK) { + return ret; + } + + s->r_ttl = 0; + s->r_class = KNOT_CLASS_ANY; + + return rr_list_append(s, ¶ms->prereq_list, ¶ms->mm); +} + +int cmd_nxrrset(const char *lp, knsupdate_params_t *params) +{ + DBG("%s: lp='%s'\n", __func__, lp); + + zs_scanner_t *s = ¶ms->parser; + int ret = parse_partial_rr(s, lp, PARSE_NOTTL); + if (ret != KNOT_EOK) { + return ret; + } + + /* Check owner name. */ + if (s->r_owner_length == 0) { + ERR("failed to parse prereq owner name '%s'\n", lp); + return KNOT_EPARSEFAIL; + } + + s->r_ttl = 0; + s->r_class = KNOT_CLASS_NONE; + + return rr_list_append(s, ¶ms->prereq_list, ¶ms->mm); +} + +int cmd_yxrrset(const char *lp, knsupdate_params_t *params) +{ + DBG("%s: lp='%s'\n", __func__, lp); + + zs_scanner_t *s = ¶ms->parser; + int ret = parse_partial_rr(s, lp, PARSE_NOTTL); + if (ret != KNOT_EOK) { + return ret; + } + + /* Check owner name. */ + if (s->r_owner_length == 0) { + ERR("failed to parse prereq owner name '%s'\n", lp); + return KNOT_EPARSEFAIL; + } + + s->r_ttl = 0; + if (s->r_data_length > 0) { + s->r_class = KNOT_CLASS_IN; + } else { + s->r_class = KNOT_CLASS_ANY; + } + + return rr_list_append(s, ¶ms->prereq_list, ¶ms->mm); +} + +int cmd_prereq(const char* lp, knsupdate_params_t *params) +{ + DBG("%s: lp='%s'\n", __func__, lp); + + /* Scan prereq specifier ([ny]xrrset|[ny]xdomain) */ + int prereq_type = tok_find(lp, pq_array); + if (prereq_type < 0) { + return prereq_type; + } + + const char *tok = pq_array[prereq_type]; + DBG("%s: type %s\n", __func__, TOK_S(tok)); + lp = tok_skipspace(lp + TOK_L(tok)); + + int ret = KNOT_EOK; + switch(prereq_type) { + case PQ_NXDOMAIN: + ret = cmd_nxdomain(lp, params); + break; + case PQ_YXDOMAIN: + ret = cmd_yxdomain(lp, params); + break; + case PQ_NXRRSET: + ret = cmd_nxrrset(lp, params); + break; + case PQ_YXRRSET: + ret = cmd_yxrrset(lp, params); + break; + default: + ret = KNOT_ERROR; + } + + return ret; +} + +int cmd_quit(const char* lp, knsupdate_params_t *params) +{ + DBG("%s: lp='%s'\n", __func__, lp); + + params->stop = true; + + return KNOT_EOK; +} + +int cmd_send(const char* lp, knsupdate_params_t *params) +{ + DBG("%s: lp='%s'\n", __func__, lp); + DBG("sending packet\n"); + + /* Build query packet. */ + int ret = build_query(params); + if (ret != KNOT_EOK) { + ERR("failed to build UPDATE message (%s)\n", knot_strerror(ret)); + return ret; + } + + /* Sign if key specified. */ + sign_context_t sign_ctx = { 0 }; + if (params->tsig_key.name) { + ret = sign_context_init_tsig(&sign_ctx, ¶ms->tsig_key); + if (ret != KNOT_EOK) { + ERR("failed to initialize signing context (%s)\n", + knot_strerror(ret)); + return ret; + } + + ret = sign_packet(params->query, &sign_ctx); + if (ret != KNOT_EOK) { + ERR("failed to sign UPDATE message (%s)\n", + knot_strerror(ret)); + sign_context_deinit(&sign_ctx); + return ret; + } + } + + int rb = 0; + /* Send/recv message (1 try + N retries). */ + int tries = 1 + params->retries; + for (; tries > 0; --tries) { + rb = pkt_sendrecv(params); + if (rb > 0) { + break; + } + } + + /* Check Send/recv result. */ + if (rb <= 0) { + sign_context_deinit(&sign_ctx); + return KNOT_ECONNREFUSED; + } + + /* Parse response. */ + ret = knot_pkt_parse(params->answer, 0); + if (ret != KNOT_EOK) { + ERR("failed to parse response (%s)\n", knot_strerror(ret)); + sign_context_deinit(&sign_ctx); + return ret; + } + + /* Check signature if expected. */ + if (params->tsig_key.name) { + ret = verify_packet(params->answer, &sign_ctx); + sign_context_deinit(&sign_ctx); + if (ret != KNOT_EOK) { + print_packet(params->answer, NULL, 0, -1, 0, true, + ¶ms->style); + ERR("reply verification (%s)\n", knot_strerror(ret)); + return ret; + } + } + + /* Free RRSet lists. */ + knsupdate_reset(params); + + /* Check return code. */ + if (knot_pkt_ext_rcode(params->answer) != KNOT_RCODE_NOERROR) { + print_packet(params->answer, NULL, 0, -1, 0, true, ¶ms->style); + ERR("update failed with error '%s'\n", + knot_pkt_ext_rcode_name(params->answer)); + ret = KNOT_ERROR; + } else { + DBG("update success\n"); + } + + return ret; +} + +int cmd_zone(const char* lp, knsupdate_params_t *params) +{ + DBG("%s: lp='%s'\n", __func__, lp); + + /* Check zone name. */ + if (!dname_isvalid(lp)) { + ERR("failed to parse zone '%s'\n", lp); + return KNOT_EPARSEFAIL; + } + + free(params->zone); + params->zone = strdup(lp); + + return KNOT_EOK; +} + +int cmd_server(const char* lp, knsupdate_params_t *params) +{ + DBG("%s: lp='%s'\n", __func__, lp); + + /* Parse host. */ + srv_info_t *srv = parse_host(lp, params->server->service); + if (!srv) { + ERR("failed to parse server '%s'\n", lp); + return KNOT_ENOMEM; + } + + srv_info_free(params->server); + params->server = srv; + + return KNOT_EOK; +} + +int cmd_local(const char* lp, knsupdate_params_t *params) +{ + DBG("%s: lp='%s'\n", __func__, lp); + + /* Parse host. */ + srv_info_t *srv = parse_host(lp, "0"); + if (!srv) { + ERR("failed to parse local '%s'\n", lp); + return KNOT_ENOMEM; + } + + srv_info_free(params->srcif); + params->srcif = srv; + + return KNOT_EOK; +} + +int cmd_show(const char* lp, knsupdate_params_t *params) +{ + DBG("%s: lp='%s'\n", __func__, lp); + + if (!params->query) { + return KNOT_EOK; + } + + printf("Update query:\n"); + build_query(params); + print_packet(params->query, NULL, 0, -1, 0, false, ¶ms->style); + printf("\n"); + + return KNOT_EOK; +} + +int cmd_answer(const char* lp, knsupdate_params_t *params) +{ + DBG("%s: lp='%s'\n", __func__, lp); + + if (!params->answer) { + return KNOT_EOK; + } + + printf("\nAnswer:\n"); + print_packet(params->answer, NULL, 0, -1, 0, true, ¶ms->style); + + return KNOT_EOK; +} + +int cmd_key(const char* lp, knsupdate_params_t *params) +{ + DBG("%s: lp='%s'\n", __func__, lp); + + /* Convert to default format. */ + char *kstr = strdup(lp); + if (!kstr) { + return KNOT_ENOMEM; + } + + int ret = KNOT_EOK; + + /* Search for the name secret separation. Allow also alg:name:key form. */ + char *sep = strchr(kstr, ' '); + if (sep != NULL) { + /* Replace ' ' with ':'. More spaces are ignored in base64. */ + *sep = ':'; + } + + /* Override existing key. */ + knot_tsig_key_deinit(¶ms->tsig_key); + + ret = knot_tsig_key_init_str(¶ms->tsig_key, kstr); + if (ret != KNOT_EOK) { + ERR("invalid key specification\n"); + } + + free(kstr); + + return ret; +} + +int cmd_origin(const char* lp, knsupdate_params_t *params) +{ + DBG("%s: lp='%s'\n", __func__, lp); + + /* Check zone name. */ + if (!dname_isvalid(lp)) { + ERR("failed to parse zone '%s'\n", lp); + return KNOT_EPARSEFAIL; + } + + return knsupdate_set_origin(params, lp); +} + +/* + * Not implemented. + */ + +int cmd_gsstsig(const char* lp, knsupdate_params_t *params) +{ + UNUSED(params); + DBG("%s: lp='%s'\n", __func__, lp); + + return KNOT_ENOTSUP; +} + +int cmd_oldgsstsig(const char* lp, knsupdate_params_t *params) +{ + UNUSED(params); + DBG("%s: lp='%s'\n", __func__, lp); + + return KNOT_ENOTSUP; +} + +int cmd_realm(const char* lp, knsupdate_params_t *params) +{ + UNUSED(params); + DBG("%s: lp='%s'\n", __func__, lp); + + return KNOT_ENOTSUP; +} diff --git a/src/utils/knsupdate/knsupdate_exec.h b/src/utils/knsupdate/knsupdate_exec.h new file mode 100644 index 0000000..182c141 --- /dev/null +++ b/src/utils/knsupdate/knsupdate_exec.h @@ -0,0 +1,21 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include "utils/knsupdate/knsupdate_params.h" + +int knsupdate_exec(knsupdate_params_t *params); diff --git a/src/utils/knsupdate/knsupdate_main.c b/src/utils/knsupdate/knsupdate_main.c new file mode 100644 index 0000000..69d471f --- /dev/null +++ b/src/utils/knsupdate/knsupdate_main.c @@ -0,0 +1,44 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stdlib.h> + +#include "libdnssec/crypto.h" +#include "utils/knsupdate/knsupdate_exec.h" +#include "utils/knsupdate/knsupdate_params.h" +#include "libknot/libknot.h" + +int main(int argc, char *argv[]) +{ + + int ret = EXIT_SUCCESS; + + knsupdate_params_t params; + if (knsupdate_parse(¶ms, argc, argv) == KNOT_EOK) { + if (!params.stop) { + dnssec_crypto_init(); + if (knsupdate_exec(¶ms) != KNOT_EOK) { + ret = EXIT_FAILURE; + } + dnssec_crypto_cleanup(); + } + } else { + ret = EXIT_FAILURE; + } + + knsupdate_clean(¶ms); + return ret; +} diff --git a/src/utils/knsupdate/knsupdate_params.c b/src/utils/knsupdate/knsupdate_params.c new file mode 100644 index 0000000..f2eee91 --- /dev/null +++ b/src/utils/knsupdate/knsupdate_params.c @@ -0,0 +1,314 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <assert.h> +#include <getopt.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "utils/knsupdate/knsupdate_params.h" +#include "utils/common/msg.h" +#include "utils/common/netio.h" +#include "libknot/libknot.h" +#include "libknot/tsig.h" +#include "contrib/mempattern.h" +#include "contrib/strtonum.h" +#include "contrib/ucw/mempool.h" + +#define PROGRAM_NAME "knsupdate" + +#define DEFAULT_RETRIES_NSUPDATE 3 +#define DEFAULT_TIMEOUT_NSUPDATE 12 + +static const style_t DEFAULT_STYLE_NSUPDATE = { + .format = FORMAT_NSUPDATE, + .style = { + .wrap = false, + .show_class = true, + .show_ttl = true, + .verbose = false, + .original_ttl = false, + .empty_ttl = false, + .human_ttl = false, + .human_tmstamp = true, + .generic = false, + .ascii_to_idn = NULL + }, + .show_query = false, + .show_header = true, + .show_edns = false, + .show_question = true, + .show_answer = true, + .show_authority = true, + .show_additional = true, + .show_tsig = true, + .show_footer = false +}; + +static void parse_err(zs_scanner_t *s) { + ERR("failed to parse RR: %s\n", zs_strerror(s->error.code)); +} + +static int parser_set_default(zs_scanner_t *s, const char *fmt, ...) +{ + /* Format string. */ + char buf[512]; /* Must suffice for domain name and TTL. */ + va_list ap; + va_start(ap, fmt); + int n = vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + + if (n < 0 || (size_t)n >= sizeof(buf)) { + return KNOT_ESPACE; + } + + /* Buffer must contain newline */ + if (zs_set_input_string(s, buf, n) != 0 || + zs_parse_all(s) != 0) { + return KNOT_EPARSEFAIL; + } + + return KNOT_EOK; +} + +static int knsupdate_init(knsupdate_params_t *params) +{ + memset(params, 0, sizeof(knsupdate_params_t)); + + /* Initialize lists. */ + init_list(¶ms->qfiles); + init_list(¶ms->update_list); + init_list(¶ms->prereq_list); + + /* Initialize memory context. */ + mm_ctx_mempool(¶ms->mm, MM_DEFAULT_BLKSIZE); + + /* Default server. */ + params->server = srv_info_create(DEFAULT_IPV4_NAME, DEFAULT_DNS_PORT); + if (!params->server) + return KNOT_ENOMEM; + + /* Default settings. */ + params->ip = IP_ALL; + params->protocol = PROTO_ALL; + params->class_num = KNOT_CLASS_IN; + params->type_num = KNOT_RRTYPE_SOA; + params->ttl = 0; + params->retries = DEFAULT_RETRIES_NSUPDATE; + params->wait = DEFAULT_TIMEOUT_NSUPDATE; + params->zone = strdup("."); + + /* Initialize RR parser. */ + if (zs_init(¶ms->parser, ".", params->class_num, 0) != 0 || + zs_set_processing(¶ms->parser, NULL, parse_err, NULL) != 0) { + zs_deinit(¶ms->parser); + return KNOT_ENOMEM; + } + + /* Default style. */ + params->style = DEFAULT_STYLE_NSUPDATE; + + /* Create query/answer packets. */ + params->query = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, ¶ms->mm); + params->answer = knot_pkt_new(NULL, KNOT_WIRE_MAX_PKTSIZE, ¶ms->mm); + + return KNOT_EOK; +} + +void knsupdate_clean(knsupdate_params_t *params) +{ + if (params == NULL) { + return; + } + + /* Clear current query. */ + knsupdate_reset(params); + + /* Free qfiles. */ + ptrlist_free(¶ms->qfiles, ¶ms->mm); + + srv_info_free(params->server); + srv_info_free(params->srcif); + free(params->zone); + zs_deinit(¶ms->parser); + knot_pkt_free(params->query); + knot_pkt_free(params->answer); + knot_tsig_key_deinit(¶ms->tsig_key); + + /* Clean up the structure. */ + mp_delete(params->mm.ctx); + memset(params, 0, sizeof(*params)); +} + +/*! \brief Free RRSet list. */ +static void rr_list_free(list_t *list, knot_mm_t *mm) +{ + assert(list != NULL); + assert(mm != NULL); + + ptrnode_t *node = NULL; + WALK_LIST(node, *list) { + knot_rrset_t *rrset = (knot_rrset_t *)node->d; + knot_rrset_free(rrset, NULL); + } + ptrlist_free(list, mm); +} + +void knsupdate_reset(knsupdate_params_t *params) +{ + /* Free ADD/REMOVE RRSets. */ + rr_list_free(¶ms->update_list, ¶ms->mm); + + /* Free PREREQ RRSets. */ + rr_list_free(¶ms->prereq_list, ¶ms->mm); +} + +static void print_help(void) +{ + printf("Usage: %s [-d] [-v] [-k keyfile | -y [hmac:]name:key]\n" + " [-p port] [-t timeout] [-r retries] [filename]\n", + PROGRAM_NAME); +} + +int knsupdate_parse(knsupdate_params_t *params, int argc, char *argv[]) +{ + if (params == NULL || argv == NULL) { + return KNOT_EINVAL; + } + + int ret = knsupdate_init(params); + if (ret != KNOT_EOK) { + return ret; + } + + // Long options. + struct option opts[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'V' }, + { NULL } + }; + + /* Command line options processing. */ + int opt = 0; + while ((opt = getopt_long(argc, argv, "dhDvVp:t:r:y:k:", opts, NULL)) + != -1) { + switch (opt) { + case 'd': + case 'D': /* Extra debugging. */ + msg_enable_debug(1); + break; + case 'h': + print_help(); + params->stop = true; + return KNOT_EOK; + case 'v': + params->protocol = PROTO_TCP; + break; + case 'V': + print_version(PROGRAM_NAME); + params->stop = true; + return KNOT_EOK; + case 'p': + free(params->server->service); + params->server->service = strdup(optarg); + if (!params->server->service) { + ERR("failed to set default port '%s'\n", optarg); + return KNOT_ENOMEM; + } + break; + case 'r': + ret = str_to_u32(optarg, ¶ms->retries); + if (ret != KNOT_EOK) { + ERR("invalid retries '%s'\n", optarg); + return ret; + } + break; + case 't': + ret = params_parse_wait(optarg, ¶ms->wait); + if (ret != KNOT_EOK) { + ERR("invalid timeout '%s'\n", optarg); + return ret; + } + break; + case 'y': + knot_tsig_key_deinit(¶ms->tsig_key); + ret = knot_tsig_key_init_str(¶ms->tsig_key, optarg); + if (ret != KNOT_EOK) { + ERR("failed to parse key '%s'\n", optarg); + return ret; + } + break; + case 'k': + knot_tsig_key_deinit(¶ms->tsig_key); + ret = knot_tsig_key_init_file(¶ms->tsig_key, optarg); + if (ret != KNOT_EOK) { + ERR("failed to parse keyfile '%s'\n", optarg); + return ret; + } + break; + default: + print_help(); + return KNOT_ENOTSUP; + } + } + + /* No retries for TCP. */ + if (params->protocol == PROTO_TCP) { + params->retries = 0; + } else { + /* If wait/tries < 1 s, set 1 second for each try. */ + if (params->wait > 0 && + (uint32_t)params->wait < ( 1 + params->retries)) { + params->wait = 1; + } else { + params->wait /= (1 + params->retries); + } + } + + /* Process non-option parameters. */ + for (; optind < argc; ++optind) { + ptrlist_add(¶ms->qfiles, argv[optind], ¶ms->mm); + } + + return ret; +} + +int knsupdate_set_ttl(knsupdate_params_t *params, const uint32_t ttl) +{ + int ret = parser_set_default(¶ms->parser, "$TTL %u\n", ttl); + if (ret == KNOT_EOK) { + params->ttl = ttl; + } else { + ERR("failed to set default TTL, %s\n", knot_strerror(ret)); + } + return ret; +} + +int knsupdate_set_origin(knsupdate_params_t *params, const char *origin) +{ + char *fqdn = get_fqd_name(origin); + + int ret = parser_set_default(¶ms->parser, "$ORIGIN %s\n", fqdn); + + free(fqdn); + + if (ret != KNOT_EOK) { + ERR("failed to set default origin, %s\n", knot_strerror(ret)); + } + return ret; +} diff --git a/src/utils/knsupdate/knsupdate_params.h b/src/utils/knsupdate/knsupdate_params.h new file mode 100644 index 0000000..2c0c391 --- /dev/null +++ b/src/utils/knsupdate/knsupdate_params.h @@ -0,0 +1,74 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <stdint.h> + +#include "utils/common/netio.h" +#include "utils/common/params.h" +#include "utils/common/sign.h" +#include "libknot/libknot.h" +#include "libzscanner/scanner.h" +#include "contrib/ucw/lists.h" + +/*! \brief knsupdate-specific params data. */ +typedef struct { + /*!< Stop processing - just print help, version,... */ + bool stop; + /*!< List of files with query data. */ + list_t qfiles; + /*!< List of nameservers to query to. */ + srv_info_t *server; + /*!< Local interface (optional). */ + srv_info_t *srcif; + /*!< Version of ip protocol to use. */ + ip_t ip; + /*!< Type (TCP, UDP) protocol to use. */ + protocol_t protocol; + /*!< Default class number. */ + uint16_t class_num; + /*!< Default type number. */ + uint16_t type_num; + /*!< Default TTL. */ + uint32_t ttl; + /*!< Number of UDP retries. */ + uint32_t retries; + /*!< Wait for network response in seconds (-1 means forever). */ + int32_t wait; + /*!< Current zone. */ + char *zone; + /*!< RR parser. */ + zs_scanner_t parser; + /*!< Current packet. */ + knot_pkt_t *query; + /*!< Current response. */ + knot_pkt_t *answer; + /*< Lists of RRSets. */ + list_t update_list, prereq_list; + /*!< Transaction signature context. */ + knot_tsig_key_t tsig_key; + /*!< Default output settings. */ + style_t style; + /*!< Memory context. */ + knot_mm_t mm; +} knsupdate_params_t; + +int knsupdate_parse(knsupdate_params_t *params, int argc, char *argv[]); +int knsupdate_set_ttl(knsupdate_params_t *params, const uint32_t ttl); +int knsupdate_set_origin(knsupdate_params_t *params, const char *origin); +void knsupdate_clean(knsupdate_params_t *params); +void knsupdate_reset(knsupdate_params_t *params); diff --git a/src/utils/kzonecheck/main.c b/src/utils/kzonecheck/main.c new file mode 100644 index 0000000..2e8ce71 --- /dev/null +++ b/src/utils/kzonecheck/main.c @@ -0,0 +1,148 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <getopt.h> +#include <libgen.h> +#include <stdio.h> + +#include "contrib/time.h" +#include "libknot/libknot.h" +#include "knot/common/log.h" +#include "utils/common/params.h" +#include "utils/kzonecheck/zone_check.h" + +#define PROGRAM_NAME "kzonecheck" + +static void print_help(void) +{ + printf("Usage: %s [parameters] <filename>\n" + "\n" + "Parameters:\n" + " -o, --origin <zone_origin> Zone name.\n" + " (default filename without .zone)\n" + " -t, --time <timestamp> Current time specification.\n" + " (default current UNIX time)\n" + " -v, --verbose Enable debug output.\n" + " -h, --help Print the program help.\n" + " -V, --version Print the program version.\n" + "\n", + PROGRAM_NAME); +} + +int main(int argc, char *argv[]) +{ + const char *origin = NULL; + bool verbose = false; + knot_time_t check_time = (knot_time_t)time(NULL); + + /* Long options. */ + struct option opts[] = { + { "origin", required_argument, NULL, 'o' }, + { "time", required_argument, NULL, 't' }, + { "verbose", no_argument, NULL, 'v' }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'V' }, + { NULL } + }; + + /* Parse command line arguments */ + int opt = 0; + while ((opt = getopt_long(argc, argv, "o:t:vVh", opts, NULL)) != -1) { + switch (opt) { + case 'o': + origin = optarg; + break; + case 'v': + verbose = true; + break; + case 'h': + print_help(); + return EXIT_SUCCESS; + case 'V': + print_version(PROGRAM_NAME); + return EXIT_SUCCESS; + case 't': + if (knot_time_parse("YMDhms|#|+-#U|+-#", + optarg, &check_time) != KNOT_EOK) { + fprintf(stderr, "Unknown time format\n"); + return EXIT_FAILURE; + } + break; + default: + print_help(); + return EXIT_FAILURE; + } + } + + /* Check if there's at least one remaining non-option. */ + if (optind >= argc) { + fprintf(stderr, "Expected zone file name\n"); + print_help(); + return EXIT_FAILURE; + } + + char *filename = argv[optind]; + + char *zonename; + if (origin == NULL) { + /* Get zone name from file name. */ + const char *ext = ".zone"; + zonename = basename(filename); + if (strcmp(zonename + strlen(zonename) - strlen(ext), ext) == 0) { + zonename = strndup(zonename, strlen(zonename) - strlen(ext)); + } else { + zonename = strdup(zonename); + } + } else { + zonename = strdup(origin); + } + + log_init(); + log_levels_set(LOG_TARGET_STDOUT, LOG_SOURCE_ANY, 0); + log_levels_set(LOG_TARGET_STDERR, LOG_SOURCE_ANY, 0); + log_levels_set(LOG_TARGET_SYSLOG, LOG_SOURCE_ANY, 0); + log_flag_set(LOG_FLAG_NOTIMESTAMP | LOG_FLAG_NOINFO); + if (verbose) { + log_levels_add(LOG_TARGET_STDOUT, LOG_SOURCE_ANY, LOG_UPTO(LOG_DEBUG)); + } + + knot_dname_t *dname = knot_dname_from_str_alloc(zonename); + free(zonename); + int ret = zone_check(filename, dname, stdout, (time_t)check_time); + knot_dname_free(dname, NULL); + + log_close(); + + switch (ret) { + case KNOT_EOK: + if (verbose) { + fprintf(stdout, "No semantic error found\n"); + } + return EXIT_SUCCESS; + case KNOT_EZONEINVAL: + fprintf(stdout, "Serious semantic error detected\n"); + // FALLTHROUGH + case KNOT_ESEMCHECK: + return EXIT_FAILURE; + case KNOT_EACCES: + case KNOT_EFILE: + fprintf(stderr, "Failed to load the zone file\n"); + return EXIT_FAILURE; + default: + fprintf(stderr, "Failed to run semantic checks (%s)\n", knot_strerror(ret)); + return EXIT_FAILURE; + } +} diff --git a/src/utils/kzonecheck/zone_check.c b/src/utils/kzonecheck/zone_check.c new file mode 100644 index 0000000..5ddc7d0 --- /dev/null +++ b/src/utils/kzonecheck/zone_check.c @@ -0,0 +1,91 @@ +/* Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stdio.h> +#include <assert.h> + +#include "knot/zone/contents.h" +#include "knot/zone/zonefile.h" +#include "utils/kzonecheck/zone_check.h" + +typedef struct { + sem_handler_t handler; + FILE *outfile; + unsigned errors[SEM_ERR_UNKNOWN + 1]; /*!< Counting errors by type. */ + unsigned error_count; /*!< Total error count. */ +} err_handler_stats_t; + +static void err_callback(sem_handler_t *handler, const zone_contents_t *zone, + const zone_node_t *node, sem_error_t error, const char *data) +{ + assert(handler != NULL); + assert(zone != NULL); + err_handler_stats_t *stats = (err_handler_stats_t *)handler; + + char buff[KNOT_DNAME_TXT_MAXLEN + 1] = ""; + (void)knot_dname_to_str(buff, (node != NULL ? node->owner : zone->apex->owner), + sizeof(buff)); + + fprintf(stats->outfile, "[%s] %s%s%s\n", + buff, sem_error_msg(error), + (data != NULL ? " " : ""), + (data != NULL ? data : "")); + + stats->errors[error]++; + stats->error_count++; +} + +static void print_statistics(err_handler_stats_t *stats) +{ + fprintf(stats->outfile, "\nError summary:\n"); + for (sem_error_t i = 0; i <= SEM_ERR_UNKNOWN; ++i) { + if (stats->errors[i] > 0) { + fprintf(stats->outfile, "%4u\t%s\n", stats->errors[i], + sem_error_msg(i)); + } + } +} + +int zone_check(const char *zone_file, const knot_dname_t *zone_name, + FILE *outfile, time_t time) +{ + err_handler_stats_t stats = { + .handler = { .cb = err_callback }, + .outfile = outfile + }; + + zloader_t zl; + int ret = zonefile_open(&zl, zone_file, zone_name, true, time); + if (ret != KNOT_EOK) { + return ret; + } + zl.err_handler = (sem_handler_t *)&stats; + zl.creator->master = true; + + zone_contents_t *contents = zonefile_load(&zl); + zonefile_close(&zl); + if (contents == NULL && !stats.handler.fatal_error) { + return KNOT_ERROR; + } + zone_contents_deep_free(contents); + + if (stats.error_count > 0) { + print_statistics(&stats); + return stats.handler.fatal_error ? KNOT_EZONEINVAL : KNOT_ESEMCHECK; + } else { + return KNOT_EOK; + } +} diff --git a/src/utils/kzonecheck/zone_check.h b/src/utils/kzonecheck/zone_check.h new file mode 100644 index 0000000..67b0f5e --- /dev/null +++ b/src/utils/kzonecheck/zone_check.h @@ -0,0 +1,22 @@ +/* Copyright (C) 2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include "libknot/libknot.h" + +int zone_check(const char *zone_file, const knot_dname_t *zone_name, + FILE *outfile, time_t time); |