From 11970aeee2a66c0fc9614720301579f372961cf8 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 2 Oct 2025 12:27:42 +0200 Subject: [PATCH 01/23] - integration-unbound-changes, update acx_nlnetlabs.m4 version and set up libtool in configure.ac. --- acx_nlnetlabs.m4 | 71 ++++++++++++++++++++++++++++++++++++++++-------- configure.ac | 2 ++ 2 files changed, 61 insertions(+), 12 deletions(-) diff --git a/acx_nlnetlabs.m4 b/acx_nlnetlabs.m4 index 6a01dc5..c8195f5 100644 --- a/acx_nlnetlabs.m4 +++ b/acx_nlnetlabs.m4 @@ -2,7 +2,10 @@ # Copyright 2009, Wouter Wijngaards, NLnet Labs. # BSD licensed. # -# Version 48 +# Version 50 +# 2025-09-29 add ac_cv_func_malloc_0_nonnull as a cache value for the malloc(0) +# check by ACX_FUNC_MALLOC. +# 2025-09-29 add ACX_CHECK_NONSTRING_ATTRIBUTE, AHX_CONFIG_NONSTRING_ATTRIBUTE. # 2024-01-16 fix to add -l:libssp.a to -lcrypto link check. # and check for getaddrinfo with only header. # 2024-01-15 fix to add crypt32 to -lcrypto link check when checking for gdi32. @@ -71,6 +74,7 @@ # ACX_DEPFLAG - find cc dependency flags. # ACX_DETERMINE_EXT_FLAGS_UNBOUND - find out which flags enable BSD and POSIX. # ACX_CHECK_FORMAT_ATTRIBUTE - find cc printf format syntax. +# ACX_CHECK_NONSTRING_ATTRIBUTE - find cc nonstring attribute syntax. # ACX_CHECK_UNUSED_ATTRIBUTE - find cc variable unused syntax. # ACX_CHECK_FLTO - see if cc supports -flto and use it if so. # ACX_LIBTOOL_C_ONLY - create libtool for C only, improved. @@ -92,6 +96,7 @@ # ACX_FUNC_IOCTLSOCKET - find ioctlsocket, portably. # ACX_FUNC_MALLOC - check malloc, define replacement . # AHX_CONFIG_FORMAT_ATTRIBUTE - config.h text for format. +# AHX_CONFIG_NONSTRING_ATTRIBUTE - config.h text for nonstring. # AHX_CONFIG_UNUSED_ATTRIBUTE - config.h text for unused. # AHX_CONFIG_FSEEKO - define fseeko, ftello fallback. # AHX_CONFIG_RAND_MAX - define RAND_MAX if needed. @@ -490,7 +495,7 @@ AC_DEFUN([AHX_CONFIG_FORMAT_ATTRIBUTE], ]) dnl Check how to mark function arguments as unused. -dnl result in HAVE_ATTR_UNUSED. +dnl result in HAVE_ATTR_UNUSED. dnl Make sure you include AHX_CONFIG_UNUSED_ATTRIBUTE also. AC_DEFUN([ACX_CHECK_UNUSED_ATTRIBUTE], [AC_REQUIRE([AC_PROG_CC]) @@ -525,6 +530,45 @@ if test $ac_cv_c_unused_attribute = yes; then fi ])dnl +dnl Check how to mark function arguments as nonstring. +dnl result in HAVE_ATTR_NONSTRING. +dnl Make sure you include AHX_CONFIG_NONSTRING_ATTRIBUTE also. +AC_DEFUN([ACX_CHECK_NONSTRING_ATTRIBUTE], +[AC_REQUIRE([AC_PROG_CC]) +AC_MSG_CHECKING(whether the C compiler (${CC-cc}) accepts the "nonstring" attribute) +AC_CACHE_VAL(ac_cv_c_nonstring_attribute, +[ac_cv_c_nonstring_attribute=no +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include +struct test { + char __attribute__((nonstring)) s[1]; +}; +]], [[ + struct test t = { "1" }; + (void) t; +]])],[ac_cv_c_nonstring_attribute="yes"],[ac_cv_c_nonstring_attribute="no"]) +]) + +dnl Setup ATTR_NONSTRING config.h parts. +dnl make sure you call ACX_CHECK_NONSTRING_ATTRIBUTE also. +AC_DEFUN([AHX_CONFIG_NONSTRING_ATTRIBUTE], +[ +#if defined(DOXYGEN) +# define ATTR_NONSTRING(x) x +#elif defined(__cplusplus) +# define ATTR_NONSTRING(x) __attribute__((nonstring)) x +#elif defined(HAVE_ATTR_NONSTRING) +# define ATTR_NONSTRING(x) __attribute__((nonstring)) x +#else /* !HAVE_ATTR_NONSTRING */ +# define ATTR_NONSTRING(x) x +#endif /* !HAVE_ATTR_NONSTRING */ +]) + +AC_MSG_RESULT($ac_cv_c_nonstring_attribute) +if test $ac_cv_c_nonstring_attribute = yes; then + AC_DEFINE(HAVE_ATTR_NONSTRING, 1, [Whether the C compiler accepts the "nonstring" attribute]) +fi +])dnl + dnl Pre-fun for ACX_LIBTOOL_C_ONLY AC_DEFUN([ACX_LIBTOOL_C_PRE], [ # skip these tests, we do not need them. @@ -1190,8 +1234,9 @@ dnl detect malloc and provide malloc compat prototype. dnl $1: unique name for compat code AC_DEFUN([ACX_FUNC_MALLOC], [ - AC_MSG_CHECKING([for GNU libc compatible malloc]) - AC_RUN_IFELSE([AC_LANG_PROGRAM( + AC_CACHE_CHECK([for GNU libc compatible malloc],[ac_cv_func_malloc_0_nonnull], + [ + AC_RUN_IFELSE([AC_LANG_PROGRAM( [[#if defined STDC_HEADERS || defined HAVE_STDLIB_H #include #else @@ -1199,14 +1244,16 @@ char *malloc (); #endif ]], [ if(malloc(0) != 0) return 1;]) ], - [AC_MSG_RESULT([no]) - AC_LIBOBJ(malloc) - AC_DEFINE_UNQUOTED([malloc], [rpl_malloc_$1], [Define if replacement function should be used.])] , - [AC_MSG_RESULT([yes]) - AC_DEFINE([HAVE_MALLOC], 1, [If have GNU libc compatible malloc])], - [AC_MSG_RESULT([no (crosscompile)]) - AC_LIBOBJ(malloc) - AC_DEFINE_UNQUOTED([malloc], [rpl_malloc_$1], [Define if replacement function should be used.])] ) + [ac_cv_func_malloc_0_nonnull=no], + [ac_cv_func_malloc_0_nonnull=yes], + [ac_cv_func_malloc_0_nonnull="no (crosscompile)"]) + ]) + AS_IF([test "$ac_cv_func_malloc_0_nonnull" = yes], + [AC_DEFINE([HAVE_MALLOC], 1, [If have GNU libc compatible malloc])], + [ + AC_LIBOBJ(malloc) + AC_DEFINE_UNQUOTED([malloc], [rpl_malloc_$1], [Define if replacement function should be used.]) + ]) ]) dnl Define fallback for fseeko and ftello if needed. diff --git a/configure.ac b/configure.ac index 1a3c034..825093a 100644 --- a/configure.ac +++ b/configure.ac @@ -28,6 +28,8 @@ ACX_CHECK_COMPILER_FLAG(O2, [CFLAGS="$CFLAGS -O2"]) ACX_CHECK_PIE fi +ACX_LIBTOOL_C_ONLY + AC_CHECK_HEADERS([endian.h sys/endian.h],,, [AC_INCLUDES_DEFAULT]) AC_CHECK_DECLS([bswap16,bswap32,bswap64], [], [], [ AC_INCLUDES_DEFAULT From 7234150700b75e9cf848ac12df3f6a05e5e4a267 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 2 Oct 2025 14:10:25 +0200 Subject: [PATCH 02/23] - integration-unbound-changes, build .lo files in Makefile. --- Makefile.in | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/Makefile.in b/Makefile.in index 51aa1bc..57071e8 100644 --- a/Makefile.in +++ b/Makefile.in @@ -13,7 +13,12 @@ CPPFLAGS = @CPPFLAGS@ -Iinclude -I$(SOURCE)/include -I$(SOURCE)/src -I. CFLAGS = @CFLAGS@ DEPFLAGS = @DEPFLAGS@ VPATH = @srcdir@ - +libtool=@libtool@ +LIBTOOL=$(libtool) +srcdir=@srcdir@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ SOURCE = @srcdir@ SOURCES = src/zone.c src/fallback/parser.c @@ -36,9 +41,9 @@ DEPENDS = $(SOURCES:.c=.d) $(WESTMERE_SOURCES:.c=.d) $(HASWELL_SOURCES:.c=.d) # macros for compatibility. EXPORT_HEADER = include/zone/export.h -.PHONY: all clean +.PHONY: all clean lib -all: libzone.a +all: lib clean: @rm -f .depend @@ -59,21 +64,33 @@ devclean: realclean libzone.a: $(OBJECTS) $($(WESTMERE)_OBJECTS) $($(HASWELL)_OBJECTS) Makefile $(AR) rcs libzone.a $(OBJECTS) $($(WESTMERE)_OBJECTS) $($(HASWELL)_OBJECTS) +libzone.la: $(OBJECTS) $($(WESTMERE)_OBJECTS) $($(HASWELL)_OBJECTS) Makefile + $(LIBTOOL) --tag=CC --mode=link $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -version-info 0:1:0 -no-undefined -o $@ $(OBJECTS:.o=.lo) $($(WESTMERE)_OBJECTS:.o=.lo) $($(HASWELL)_OBJECTS:.o=.lo) -rpath $(libdir) + +# The copy to the main directory is because that is where (NSD) picks up +# the static library to link to. +# The shared object files are listed to link with. +lib: libzone.la + cp .libs/libzone.a . + +list_objs: + @echo $(OBJECTS:.o=.lo) $($(WESTMERE)_OBJECTS:.o=.lo) $($(HASWELL)_OBJECTS:.o=.lo) + $(EXPORT_HEADER): @mkdir -p include/zone @echo "#define ZONE_EXPORT" > $(EXPORT_HEADER) $(WESTMERE_OBJECTS): $(EXPORT_HEADER) .depend Makefile @mkdir -p src/westmere - $(CC) $(DEPFLAGS) $(CPPFLAGS) $(CFLAGS) -march=westmere -o $@ -c $(SOURCE)/$(@:.o=.c) + $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEPFLAGS) $(CPPFLAGS) $(CFLAGS) -march=westmere -o $@ -c $(SOURCE)/$(@:.o=.c) $(HASWELL_OBJECTS): $(EXPORT_HEADER) .depend Makefile @mkdir -p src/haswell - $(CC) $(DEPFLAGS) $(CPPFLAGS) $(CFLAGS) -march=haswell -o $@ -c $(SOURCE)/$(@:.o=.c) + $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEPFLAGS) $(CPPFLAGS) $(CFLAGS) -march=haswell -o $@ -c $(SOURCE)/$(@:.o=.c) $(OBJECTS): $(EXPORT_HEADER) .depend Makefile @mkdir -p src/fallback - $(CC) $(DEPFLAGS) $(CPPFLAGS) $(CFLAGS) -o $@ -c $(SOURCE)/$(@:.o=.c) + $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEPFLAGS) $(CPPFLAGS) $(CFLAGS) -o $@ -c $(SOURCE)/$(@:.o=.c) @touch $@ .depend: From f57929189352284a724465c240b34ba3f767de8c Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 2 Oct 2025 14:47:00 +0200 Subject: [PATCH 03/23] - integration-unbound-changes, Add files to gitignore. Version info from configure and export symbol list. Fix to not copy library if not needed. --- .gitignore | 7 +++++++ Makefile.in | 13 ++++++++----- configure.ac | 25 +++++++++++++++++++++++++ lzonesyms.def | 3 +++ 4 files changed, 43 insertions(+), 5 deletions(-) create mode 100644 lzonesyms.def diff --git a/.gitignore b/.gitignore index 32c9c17..437f81f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,8 @@ *.o +*.lo *.d +/.libs/ +/aclocal.m4 /autom4te.cache /build /config.guess @@ -11,7 +14,11 @@ /config.sub /configure /configure~ +/install-sh +/libtool /libzone.a +/libzone.la +/ltmain.sh /make.dep /.depend /Makefile diff --git a/Makefile.in b/Makefile.in index 57071e8..0082a57 100644 --- a/Makefile.in +++ b/Makefile.in @@ -20,6 +20,7 @@ prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ SOURCE = @srcdir@ +version_info=@version_info@ SOURCES = src/zone.c src/fallback/parser.c OBJECTS = $(SOURCES:.c=.o) @@ -61,17 +62,19 @@ maintainer-clean: realclean devclean: realclean @rm -rf config.h.in configure -libzone.a: $(OBJECTS) $($(WESTMERE)_OBJECTS) $($(HASWELL)_OBJECTS) Makefile - $(AR) rcs libzone.a $(OBJECTS) $($(WESTMERE)_OBJECTS) $($(HASWELL)_OBJECTS) +#libzone.a: $(OBJECTS) $($(WESTMERE)_OBJECTS) $($(HASWELL)_OBJECTS) Makefile + #$(AR) rcs libzone.a $(OBJECTS) $($(WESTMERE)_OBJECTS) $($(HASWELL)_OBJECTS) libzone.la: $(OBJECTS) $($(WESTMERE)_OBJECTS) $($(HASWELL)_OBJECTS) Makefile - $(LIBTOOL) --tag=CC --mode=link $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -version-info 0:1:0 -no-undefined -o $@ $(OBJECTS:.o=.lo) $($(WESTMERE)_OBJECTS:.o=.lo) $($(HASWELL)_OBJECTS:.o=.lo) -rpath $(libdir) + $(LIBTOOL) --tag=CC --mode=link $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -version-info $(version_info) -no-undefined -export-symbols $(srcdir)/lzonesyms.def -o $@ $(OBJECTS:.o=.lo) $($(WESTMERE)_OBJECTS:.o=.lo) $($(HASWELL)_OBJECTS:.o=.lo) -rpath $(libdir) + +libzone.a: libzone.la + cp .libs/libzone.a $@ # The copy to the main directory is because that is where (NSD) picks up # the static library to link to. # The shared object files are listed to link with. -lib: libzone.la - cp .libs/libzone.a . +lib: libzone.a list_objs: @echo $(OBJECTS:.o=.lo) $($(WESTMERE)_OBJECTS:.o=.lo) $($(HASWELL)_OBJECTS:.o=.lo) diff --git a/configure.ac b/configure.ac index 825093a..5f40af4 100644 --- a/configure.ac +++ b/configure.ac @@ -15,6 +15,31 @@ AC_INIT([simdzone],[0.2.4],[https://github.com/NLnetLabs/simdzone/issues]) AC_CONFIG_HEADERS([config.h]) AC_CONFIG_FILES([Makefile]) +version_info=0:1:0 +# This is current:revision:age +# 0.2.4 had 0:1:0 +# +# +# Current -- the number of the binary API that we're implementing +# Revision -- which iteration of the implementation of the binary +# API are we supplying? +# Age -- How many previous binary API versions do we also +# support? +# +# If we release a new version that does not change the binary API, +# increment Revision. +# +# If we release a new version that changes the binary API, but does +# not break programs compiled against the old binary API, increment +# Current and Age. Set Revision to 0, since this is the first +# implementation of the new API. +# +# Otherwise, we're changing the binary API and breaking backward +# compatibility with old binaries. Increment Current. Set Age to 0, +# since we're backward compatible with no previous APIs. Set Revision +# to 0 too. +AC_SUBST(version_info) + sinclude(acx_nlnetlabs.m4) m4_include(m4/ax_check_compile_flag.m4) diff --git a/lzonesyms.def b/lzonesyms.def new file mode 100644 index 0000000..645a1d4 --- /dev/null +++ b/lzonesyms.def @@ -0,0 +1,3 @@ +zone_log +zone_parse +zone_parse_string From 6a2afd75fb98c570d159b8000093563262a66006 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 2 Oct 2025 14:58:24 +0200 Subject: [PATCH 04/23] - integration-unbound-changes, fix ci to install libtool. --- .github/workflows/build-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 694a805..70c2e09 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -127,6 +127,6 @@ jobs: run: | set -e -x echo $PATH - autoreconf -i + autoreconf -fi ./configure make -j 2 From 1993bb7fea875e3d61173c37e7e7ae95b656ee91 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 2 Oct 2025 15:01:59 +0200 Subject: [PATCH 05/23] - integration-unbound-changes, fix ci to install libtool and autoconf. --- .github/workflows/build-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 70c2e09..fbfa668 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -20,7 +20,7 @@ jobs: analyzer: off sanitizer: address,undefined - os: macos-14 - packages: automake + packages: autoconf automake libtool build_type: Debug build_tool_options: -j 4 analyzer: off From 8f7b1b4bbe26ada1ee94633f18755c21ed5daabd Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 7 Oct 2025 15:51:26 +0200 Subject: [PATCH 06/23] - integration-unbound-changes, renames parser.o for haswell and westmere. With different object names, it stops libtool from having to rename them. --- CMakeLists.txt | 8 ++++---- Makefile.in | 4 ++-- src/haswell/{parser.c => hwparser.c} | 0 src/westmere/{parser.c => wmparser.c} | 0 4 files changed, 6 insertions(+), 6 deletions(-) rename src/haswell/{parser.c => hwparser.c} (100%) rename src/westmere/{parser.c => wmparser.c} (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index f28d224..eb352a5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -218,8 +218,8 @@ if(architecture STREQUAL "x86_64" OR architecture STREQUAL "amd64") unset(CMAKE_REQUIRED_FLAGS) if (HAVE_WESTMERE) set_source_files_properties( - src/westmere/parser.c PROPERTIES COMPILE_FLAGS "-march=westmere") - target_sources(zone PRIVATE src/westmere/parser.c) + src/westmere/wmparser.c PROPERTIES COMPILE_FLAGS "-march=westmere") + target_sources(zone PRIVATE src/westmere/wmparser.c) set_source_files_properties( src/westmere/bench.c PROPERTIES COMPILE_FLAGS "-march=westmere") target_sources(zone-bench PRIVATE src/westmere/bench.c) @@ -233,8 +233,8 @@ if(architecture STREQUAL "x86_64" OR architecture STREQUAL "amd64") unset(CMAKE_REQUIRED_FLAGS) if (HAVE_HASWELL) set_source_files_properties( - src/haswell/parser.c PROPERTIES COMPILE_FLAGS "-march=haswell") - target_sources(zone PRIVATE src/haswell/parser.c) + src/haswell/hwparser.c PROPERTIES COMPILE_FLAGS "-march=haswell") + target_sources(zone PRIVATE src/haswell/hwparser.c) set_source_files_properties( src/haswell/bench.c PROPERTIES COMPILE_FLAGS "-march=haswell") target_sources(zone-bench PRIVATE src/haswell/bench.c) diff --git a/Makefile.in b/Makefile.in index 0082a57..4bbe962 100644 --- a/Makefile.in +++ b/Makefile.in @@ -25,10 +25,10 @@ version_info=@version_info@ SOURCES = src/zone.c src/fallback/parser.c OBJECTS = $(SOURCES:.c=.o) -WESTMERE_SOURCES = src/westmere/parser.c +WESTMERE_SOURCES = src/westmere/wmparser.c WESTMERE_OBJECTS = $(WESTMERE_SOURCES:.c=.o) -HASWELL_SOURCES = src/haswell/parser.c +HASWELL_SOURCES = src/haswell/hwparser.c HASWELL_OBJECTS = $(HASWELL_SOURCES:.c=.o) NO_OBJECTS = diff --git a/src/haswell/parser.c b/src/haswell/hwparser.c similarity index 100% rename from src/haswell/parser.c rename to src/haswell/hwparser.c diff --git a/src/westmere/parser.c b/src/westmere/wmparser.c similarity index 100% rename from src/westmere/parser.c rename to src/westmere/wmparser.c From 611cc8e91c952e3b981aed05f1f9e4ce4d948b86 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Wed, 8 Oct 2025 17:14:51 +0200 Subject: [PATCH 07/23] - integration-unbound-changes, fix realpath function test for windows compile. --- configure.ac | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 5f40af4..152e693 100644 --- a/configure.ac +++ b/configure.ac @@ -167,7 +167,11 @@ int main(int argc, char *argv[]) fi fi -AC_CHECK_FUNCS([realpath],,[AC_MSG_ERROR([realpath is not available])]) +AC_CHECK_FUNCS([realpath],,[ + AC_CHECK_FUNCS([_fullpath],,[ + AC_MSG_ERROR([realpath and _fullpath are not available]) + ]) +]) AC_SUBST([HAVE_ENDIAN_H]) AC_SUBST([HAVE_WESTMERE]) From bd67c9aca6b31be7956ff6b1b5696563defb6d9b Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 10 Oct 2025 13:35:24 +0200 Subject: [PATCH 08/23] - integration-unbound-changes, add -lssp on mingw, and LDFLAGS and LIBS support. --- Makefile.in | 4 +++- configure.ac | 13 +++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/Makefile.in b/Makefile.in index 4bbe962..de1176c 100644 --- a/Makefile.in +++ b/Makefile.in @@ -12,6 +12,8 @@ CC = @CC@ CPPFLAGS = @CPPFLAGS@ -Iinclude -I$(SOURCE)/include -I$(SOURCE)/src -I. CFLAGS = @CFLAGS@ DEPFLAGS = @DEPFLAGS@ +LDFLAGS=@LDFLAGS@ +LIBS=@LIBS@ VPATH = @srcdir@ libtool=@libtool@ LIBTOOL=$(libtool) @@ -66,7 +68,7 @@ devclean: realclean #$(AR) rcs libzone.a $(OBJECTS) $($(WESTMERE)_OBJECTS) $($(HASWELL)_OBJECTS) libzone.la: $(OBJECTS) $($(WESTMERE)_OBJECTS) $($(HASWELL)_OBJECTS) Makefile - $(LIBTOOL) --tag=CC --mode=link $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -version-info $(version_info) -no-undefined -export-symbols $(srcdir)/lzonesyms.def -o $@ $(OBJECTS:.o=.lo) $($(WESTMERE)_OBJECTS:.o=.lo) $($(HASWELL)_OBJECTS:.o=.lo) -rpath $(libdir) + $(LIBTOOL) --tag=CC --mode=link $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -version-info $(version_info) -no-undefined -export-symbols $(srcdir)/lzonesyms.def -o $@ $(OBJECTS:.o=.lo) $($(WESTMERE)_OBJECTS:.o=.lo) $($(HASWELL)_OBJECTS:.o=.lo) $(LIBS) -rpath $(libdir) libzone.a: libzone.la cp .libs/libzone.a $@ diff --git a/configure.ac b/configure.ac index 152e693..f318824 100644 --- a/configure.ac +++ b/configure.ac @@ -46,6 +46,19 @@ m4_include(m4/ax_check_compile_flag.m4) CFLAGS="$CFLAGS" m4_version_prereq([2.70], [AC_PROG_CC], [AC_PROG_CC_STDC]) +# are we on MinGW? +if uname -s 2>&1 | grep MINGW >/dev/null; then on_mingw="yes" +else + if echo $host | grep mingw >/dev/null; then on_mingw="yes" + else on_mingw="no"; fi +fi +if test "$on_mingw" = "yes"; then + # link with libssp for the stack protector functions on the windows + # compile, for the static compile -l:libssp.a could be used. + # But there is no static option right now for simdzone. + if test -z "$LIBS"; then LIBS="-lssp"; else LIBS="$LIBS -lssp"; fi +fi + # allow user to override the -g -O2 flags. if test "x$CFLAGS" = "x" ; then ACX_CHECK_COMPILER_FLAG(g, [CFLAGS="$CFLAGS -g"]) From 873339842f795cc2085eb6793b93e5e0ceb23d02 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 16 Oct 2025 13:45:16 +0200 Subject: [PATCH 09/23] - integration-unbound-changes, fix libzone.a for disable-static build. --- Makefile.in | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile.in b/Makefile.in index de1176c..f27d07c 100644 --- a/Makefile.in +++ b/Makefile.in @@ -50,7 +50,7 @@ all: lib clean: @rm -f .depend - @rm -f libzone.a $(OBJECTS) $(EXPORT_HEADER) + @rm -f libzone.a libzone.la $(OBJECTS) $(EXPORT_HEADER) @rm -f $($(WESTMERE)_OBJECTS) $($(HASWELL)_OBJECTS) distclean: clean @@ -69,14 +69,14 @@ devclean: realclean libzone.la: $(OBJECTS) $($(WESTMERE)_OBJECTS) $($(HASWELL)_OBJECTS) Makefile $(LIBTOOL) --tag=CC --mode=link $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -version-info $(version_info) -no-undefined -export-symbols $(srcdir)/lzonesyms.def -o $@ $(OBJECTS:.o=.lo) $($(WESTMERE)_OBJECTS:.o=.lo) $($(HASWELL)_OBJECTS:.o=.lo) $(LIBS) -rpath $(libdir) + if test -f .libs/libzone.a; then cp .libs/libzone.a .; fi libzone.a: libzone.la - cp .libs/libzone.a $@ # The copy to the main directory is because that is where (NSD) picks up # the static library to link to. # The shared object files are listed to link with. -lib: libzone.a +lib: libzone.la list_objs: @echo $(OBJECTS:.o=.lo) $($(WESTMERE)_OBJECTS:.o=.lo) $($(HASWELL)_OBJECTS:.o=.lo) From 6948734a4a3de01ce6c9f4b46f5f6eeadb36768b Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 17 Oct 2025 16:10:34 +0200 Subject: [PATCH 10/23] - integration-unbound-changes, fix -lssp for mingw. --- configure.ac | 2 ++ 1 file changed, 2 insertions(+) diff --git a/configure.ac b/configure.ac index f318824..c45d04d 100644 --- a/configure.ac +++ b/configure.ac @@ -53,10 +53,12 @@ else else on_mingw="no"; fi fi if test "$on_mingw" = "yes"; then + if echo "$host" | $GREP -i -e linux >/dev/null; then # link with libssp for the stack protector functions on the windows # compile, for the static compile -l:libssp.a could be used. # But there is no static option right now for simdzone. if test -z "$LIBS"; then LIBS="-lssp"; else LIBS="$LIBS -lssp"; fi + fi fi # allow user to override the -g -O2 flags. From 5dbaff93fe6434706d49831dce44d1ca0fe7daed Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 9 Jan 2026 14:25:41 +0100 Subject: [PATCH 11/23] - integration-unbound-changes, add zone_parse_from_callback function. --- include/zone.h | 45 ++++++++++++++++++++++++++++++++++++++++++++ src/generic/parser.h | 33 +++++++++++++++++++++++--------- src/zone.c | 34 +++++++++++++++++++++++++++++++++ 3 files changed, 103 insertions(+), 9 deletions(-) diff --git a/include/zone.h b/include/zone.h index b7e27a8..516e153 100644 --- a/include/zone.h +++ b/include/zone.h @@ -428,6 +428,23 @@ typedef int32_t(*zone_include_t)( const char *, // fully qualified path void *); // user data +/** + * @brief Signature of callback function invoked to read data pieces. + * + * Called to read another piece of the data. Read up the number of bytes, + * or less bytes up to the end, and signal end of file, or error. + */ +typedef int32_t(*zone_read_data_t)( + zone_parser_t *, + char *, // The data buffer to read in to. + size_t, // The number of bytes to read that is requested. + size_t *, // The number of bytes successfully read, less than the requested + // amount means end-of-file after that. Also 0 means end-of-file + // without data read. Reading bytes or less or 0 at end-of-file + // return with ZONE_SUCCESS. For errors, can pass 0, and return + // with ZONE_READ_ERROR. + void *); // user data + /** * @brief Available configuration options. */ @@ -490,6 +507,8 @@ struct zone_parser { /** @private */ zone_options_t options; /** @private */ + zone_read_data_t read_data_callback; + /** @private */ void *user_data; struct { size_t size; @@ -584,6 +603,32 @@ zone_parse_string( void *user_data) zone_nonnull((1,2,3,4)); +/** + * @brief Parse zone using a callback to retrieve data. + * + * Parse using a callback to retrieve data pieces, containing resource + * records in presentation format. The callback is called to return eg. + * the next up-to-N bytes, or up to the end, and signals end of file. + * It is called to read chunks of a couple Kb, until the last (possibly + * partial) chunk, and then end-of-file is returned. + * + * @param[in] parser Zone parser + * @param[in] options Settings used for parsing. + * @param[in] buffers Scratch buffers used by parsing. + * @param[in] callback Callback function that is called to read the data. + * @param[in] user_data Pointer passed verbatim to callbacks. + * + * @returns @ref ZONE_SUCCESS on success or a negative number on error. + */ +ZONE_EXPORT int32_t +zone_parse_from_callback( + zone_parser_t *parser, + const zone_options_t *options, + zone_buffers_t *buffers, + zone_read_data_t callback, + void *user_data) +zone_nonnull((1,2,3,4)); + /** * @defgroup log_priorities Log categories. * diff --git a/src/generic/parser.h b/src/generic/parser.h index 34a90e5..cf5c54c 100644 --- a/src/generic/parser.h +++ b/src/generic/parser.h @@ -305,19 +305,34 @@ static int32_t refill(parser_t *parser) parser->file->fields.head[0] = data; } - size_t count = fread( - parser->file->buffer.data + parser->file->buffer.length, - sizeof(parser->file->buffer.data[0]), - parser->file->buffer.size - parser->file->buffer.length, - parser->file->handle); - - if (!count && ferror(parser->file->handle)) - READ_ERROR(parser, "Cannot refill buffer"); + char* readpos; // where to read the data. + size_t count; // number of bytes to read, and read in. + int is_eof; // if it is at eof. + readpos = parser->file->buffer.data + parser->file->buffer.length; + count = parser->file->buffer.size - parser->file->buffer.length; + is_eof = 0; + + if(parser->read_data_callback) { + /* Read using callback. */ + size_t retcount = 0; + int32_t ret = parser->read_data_callback(parser, readpos, count, + &retcount, parser->user_data); + is_eof = ((retcount == 0) | (retcount < count)); + if(ret < 0) + RAISE_ERROR(parser, ret, "Cannot read data"); + } else { + /* Read from file. */ + count = fread(readpos, sizeof(parser->file->buffer.data[0]), count, + parser->file->handle); + if (!count && ferror(parser->file->handle)) + READ_ERROR(parser, "Cannot refill buffer"); + is_eof = feof(parser->file->handle) != 0; + } // always null-terminate for terminating token parser->file->buffer.length += (size_t)count; parser->file->buffer.data[parser->file->buffer.length] = '\0'; - parser->file->end_of_file = feof(parser->file->handle) != 0; + parser->file->end_of_file = is_eof; /* After the file, there is padding, that is used by vector instructions, * initialise those bytes. */ diff --git a/src/zone.c b/src/zone.c index 833af7f..343488a 100644 --- a/src/zone.c +++ b/src/zone.c @@ -372,6 +372,7 @@ static int32_t initialize_parser( const size_t size = offsetof(parser_t, file); memset(parser, 0, size); parser->options = *options; + parser->read_data_callback = NULL; parser->user_data = user_data; parser->file = &parser->first; parser->buffers.size = buffers->size; @@ -460,6 +461,39 @@ int32_t zone_parse_string( return code; } +int32_t zone_parse_from_callback( + zone_parser_t *parser, + const zone_options_t *options, + zone_buffers_t *buffers, + zone_read_data_t callback, + void *user_data) +{ + int32_t code; + const size_t size = ZONE_WINDOW_SIZE + 1 + ZONE_BLOCK_SIZE; + zone_file_t *file; + + if ((code = initialize_parser(parser, options, buffers, user_data)) < 0) + return code; + parser->read_data_callback = callback; + + file = parser->file; + initialize_file(parser, file); + file->handle = NULL; + file->path = NULL; + file->name = NULL; + if (!(file->buffer.data = malloc(size))) + return (void)close_file(parser, file), ZONE_OUT_OF_MEMORY; + file->buffer.data[0] = '\0'; + file->buffer.size = ZONE_WINDOW_SIZE; + file->end_of_file = 0; + file->fields.tape[0] = &file->buffer.data[0]; + file->fields.tape[1] = &file->buffer.data[0]; + + code = parse(parser, user_data); + zone_close(parser); + return code; +} + zone_nonnull((1,5)) static void print_message( zone_parser_t *parser, From 559c1b31d066e9f9a9e5808c616803a994eca3a2 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 9 Jan 2026 14:29:44 +0100 Subject: [PATCH 12/23] - integration-unbound-changes, fix implicit int conversion warning. --- src/generic/parser.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/generic/parser.h b/src/generic/parser.h index cf5c54c..26632b4 100644 --- a/src/generic/parser.h +++ b/src/generic/parser.h @@ -305,9 +305,9 @@ static int32_t refill(parser_t *parser) parser->file->fields.head[0] = data; } - char* readpos; // where to read the data. - size_t count; // number of bytes to read, and read in. - int is_eof; // if it is at eof. + char* readpos; // where to read the data. + size_t count; // number of bytes to read, and read in. + uint8_t is_eof; // if it is at eof. readpos = parser->file->buffer.data + parser->file->buffer.length; count = parser->file->buffer.size - parser->file->buffer.length; is_eof = 0; From a6b59e6a37c670ec85f5ed641345c8f7946127fe Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 9 Jan 2026 14:37:09 +0100 Subject: [PATCH 13/23] - integration-unbound-changes, export function symbol and add zone_parse_from_callback to the api reference. --- doc/manual/api_reference.rst | 3 +++ lzonesyms.def | 1 + 2 files changed, 4 insertions(+) diff --git a/doc/manual/api_reference.rst b/doc/manual/api_reference.rst index 7f6fcda..08ef217 100644 --- a/doc/manual/api_reference.rst +++ b/doc/manual/api_reference.rst @@ -17,6 +17,9 @@ Parse functions .. doxygenfunction:: zone_parse_string :project: doxygen +.. doxygenfunction:: zone_parse_from_callback + :project: doxygen + Log priorities -------------- diff --git a/lzonesyms.def b/lzonesyms.def index 645a1d4..8bacca0 100644 --- a/lzonesyms.def +++ b/lzonesyms.def @@ -1,3 +1,4 @@ zone_log zone_parse +zone_parse_from_callback zone_parse_string From 5a09ab0bb9835d6dc1ad907672b001d94b582fc7 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 13 Jan 2026 13:27:09 +0100 Subject: [PATCH 14/23] - integration-unbound-changes, fix count read of zone parse from callback, and add test for parse from callback. --- src/generic/parser.h | 3 +- src/zone.c | 5 +- tests/CMakeLists.txt | 2 +- tests/fromcb.c | 185 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 192 insertions(+), 3 deletions(-) create mode 100644 tests/fromcb.c diff --git a/src/generic/parser.h b/src/generic/parser.h index 26632b4..c7d8b74 100644 --- a/src/generic/parser.h +++ b/src/generic/parser.h @@ -270,7 +270,7 @@ static int32_t refill(parser_t *parser) if (parser->file->end_of_file) return 0; - assert(parser->file->handle); + assert(parser->file->handle || parser->read_data_callback); // move unread data to start of buffer char *data = parser->file->buffer.data + parser->file->buffer.index; @@ -320,6 +320,7 @@ static int32_t refill(parser_t *parser) is_eof = ((retcount == 0) | (retcount < count)); if(ret < 0) RAISE_ERROR(parser, ret, "Cannot read data"); + count = retcount; } else { /* Read from file. */ count = fread(readpos, sizeof(parser->file->buffer.data[0]), count, diff --git a/src/zone.c b/src/zone.c index 343488a..7184983 100644 --- a/src/zone.c +++ b/src/zone.c @@ -152,7 +152,8 @@ static void close_file( { assert((file->name == not_a_file) == (file->path == not_a_file)); - const bool is_string = file->name == not_a_file || file->path == not_a_file; + const bool is_string = (file->name == not_a_file || + file->path == not_a_file) && !parser->read_data_callback; assert(!is_string || file == &parser->first); assert(!is_string || file->handle == NULL); @@ -485,6 +486,8 @@ int32_t zone_parse_from_callback( return (void)close_file(parser, file), ZONE_OUT_OF_MEMORY; file->buffer.data[0] = '\0'; file->buffer.size = ZONE_WINDOW_SIZE; + file->buffer.length = 0; + file->buffer.index = 0; file->end_of_file = 0; file->fields.tape[0] = &file->buffer.data[0]; file->fields.tape[1] = &file->buffer.data[0]; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 95815bd..b973a25 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -9,7 +9,7 @@ if(HAVE_HASWELL) set_source_files_properties(haswell/bits.c PROPERTIES COMPILE_FLAGS "-march=haswell") endif() -cmocka_add_tests(zone-tests types.c include.c ip4.c time.c base32.c svcb.c syntax.c semantics.c eui.c bounds.c bits.c ttl.c) +cmocka_add_tests(zone-tests types.c include.c ip4.c time.c base32.c svcb.c syntax.c semantics.c eui.c bounds.c bits.c ttl.c fromcb.c) set(xbounds ${CMAKE_CURRENT_SOURCE_DIR}/zones/xbounds.zone) set(xbounds_c "${CMAKE_CURRENT_BINARY_DIR}/xbounds.c") diff --git a/tests/fromcb.c b/tests/fromcb.c new file mode 100644 index 0000000..4bcc69c --- /dev/null +++ b/tests/fromcb.c @@ -0,0 +1,185 @@ +/* + * fromcb.c -- test parse from read data callback. + * + * Copyright (c) 2026, NLnet Labs. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ +#include +#include +#include +#include + +#include "zone.h" + +/* max number of chunks used in the test */ +#define MAX_CHUNKS 16 +/* if test should print verbosely */ +static int verb = 1; + +/* The size of the chunk, if set to this value, makes it use strlen. */ +#define CHUNK_SIZE_STRLEN -3 +/* The size of the chunk, if set to this value, makes it use strlen, and + * pad to the requested size. */ +#define CHUNK_SIZE_STRLENPAD -4 + +/* Chunk of string to return for the test. */ +struct fromcb_chunkinfo { + /* The string with content for the chunk, can be smaller than max len. */ + char* str; + /* the size of the chunk, or the defines for it. */ + int size; + /* the end of the pad, if any. Put on the end of the chunk, if padded. */ + char* padend; + /* the retvalue for the read of the chunk. 0 means success. */ + int32_t retvalue; +}; + +/* The test information for a parse from callback test. Inited to values on + * start, and contains space for counters that are updated. */ +struct fromcb_testinfo { + /* expected return value of parse */ + int32_t code; + /* list of chunks that is read, ends with NULL */ + struct fromcb_chunkinfo chunk[MAX_CHUNKS]; + /* current chunk. */ + size_t chunknum; + /* number of RRs read. */ + size_t num_rrs; + /* expected number of RRs read. */ + size_t expect_num_rrs; +}; + +static int32_t read_data_func( + zone_parser_t * parser, + char *data, + size_t len, + size_t *outlen, + void *user_data) +{ + struct fromcb_testinfo *test = (void *)user_data; + struct fromcb_chunkinfo* chunk; + int32_t retvalue; + int chunk_size = 0; + (void)parser; + chunk = &test->chunk[test->chunknum]; + if(chunk->str == NULL) { + *outlen = 0; + if(verb) + fprintf(stderr, "read_data_func(len=%d, outlen=%d) " + "chunk %d returns %d\n", (int)len, (int)*outlen, + (int)test->chunknum, 0); + return 0; + } + + chunk_size = chunk->size; + if(chunk_size == CHUNK_SIZE_STRLEN) { + chunk_size = strlen(chunk->str); + if(chunk_size != 0) + memmove(data, chunk->str, chunk_size); + } else if(chunk_size == CHUNK_SIZE_STRLENPAD) { + size_t padendlen = 0; + chunk_size = strlen(chunk->str); + if(chunk_size != 0) + memmove(data, chunk->str, chunk_size); + /* Pad it */ + if(chunk->padend) + padendlen = strlen(chunk->padend); + if((size_t)chunk_size + padendlen < len) { + size_t i, padlen = len - (size_t)chunk_size - padendlen; + char padchar = ' '; + for(i=0; ipadend) { + memmove(data+len-padendlen, chunk->padend, padendlen); + } + chunk_size = len; + } else { + if(chunk_size != 0) + memmove(data, chunk->str, chunk_size); + } + + *outlen = (size_t)chunk_size; + retvalue = chunk->retvalue; + test->chunknum++; + if(verb) + fprintf(stderr, "read_data_func(len=%d, outlen=%d) chunk %d returns %d\n", + (int)len, (int)*outlen, (int)test->chunknum-1, (int)retvalue); + return retvalue; +} + +static int32_t accept_fromcb( + zone_parser_t *parser, + const zone_name_t *owner, + uint16_t type, + uint16_t class, + uint32_t ttl, + uint16_t rdlength, + const uint8_t *rdata, + void *user_data) +{ + struct fromcb_testinfo *test = (void *)user_data; + (void)parser; + (void)owner; + (void)type; + (void)class; + (void)ttl; + (void)rdlength; + (void)rdata; + + test->num_rrs++; + if(verb) + fprintf(stderr, "accept rr %d / %d\n", + (int)test->num_rrs, (int)test->expect_num_rrs); + return 0; +} + +/*!cmocka */ +void test_fromcb(void **state) +{ + static uint8_t origin[] = + { 7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 3, 'c', 'o', 'm', 0 }; + + static struct fromcb_testinfo tests[] = { + /* fromcb test 0: two chunks */ + { 0, { + {"www.example.com. IN A 1.2.3.4\n", CHUNK_SIZE_STRLENPAD, "\n", 0}, + {"www2.example.com. IN A 1.2.3.4\n", CHUNK_SIZE_STRLEN, NULL, 0}, + {NULL, 0, NULL, 0} + }, + 0, 0, 2 // num rrs + } + }; + + (void)state; + + for (size_t i=0, n=sizeof(tests)/sizeof(tests[0]); i < n; i++) { + zone_parser_t parser; + zone_name_buffer_t name; + zone_rdata_buffer_t rdata; + zone_buffers_t buffers = { 1, &name, &rdata }; + zone_options_t options; + int32_t code; + struct fromcb_testinfo *test = &tests[i]; + + fprintf(stderr, "fromcb test %d\n", (int)i); + + memset(&options, 0, sizeof(options)); + options.accept.callback = accept_fromcb; + options.origin.octets = origin; + options.origin.length = sizeof(origin); + options.default_ttl = 3600; + options.default_class = 1; + + code = zone_parse_from_callback(&parser, &options, &buffers, + read_data_func, (void*)test); + if(verb) + fprintf(stderr, "retcode %d, num_rrs %d\n", (int)code, + (int)test->num_rrs); + assert_int_equal(code, test->code); + assert_int_equal(test->num_rrs, test->expect_num_rrs); + } +} From 3dd15ff8d140249007168f673fcb4339a323d4c7 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 13 Jan 2026 13:30:29 +0100 Subject: [PATCH 15/23] - integration-unbound-changes, fix fromcb test integer conversion warnings. --- tests/fromcb.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/fromcb.c b/tests/fromcb.c index 4bcc69c..5a48e1c 100644 --- a/tests/fromcb.c +++ b/tests/fromcb.c @@ -75,12 +75,12 @@ static int32_t read_data_func( chunk_size = chunk->size; if(chunk_size == CHUNK_SIZE_STRLEN) { - chunk_size = strlen(chunk->str); + chunk_size = (int)strlen(chunk->str); if(chunk_size != 0) memmove(data, chunk->str, chunk_size); } else if(chunk_size == CHUNK_SIZE_STRLENPAD) { size_t padendlen = 0; - chunk_size = strlen(chunk->str); + chunk_size = (int)strlen(chunk->str); if(chunk_size != 0) memmove(data, chunk->str, chunk_size); /* Pad it */ @@ -90,13 +90,13 @@ static int32_t read_data_func( size_t i, padlen = len - (size_t)chunk_size - padendlen; char padchar = ' '; for(i=0; ipadend) { memmove(data+len-padendlen, chunk->padend, padendlen); } - chunk_size = len; + chunk_size = (int)len; } else { if(chunk_size != 0) memmove(data, chunk->str, chunk_size); From 2d2e30bd02500a03a7e927f2e6c75ed4185fa07c Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 13 Jan 2026 13:36:21 +0100 Subject: [PATCH 16/23] - integration-unbound-changes, fix fromcb test with chunk type size_t. --- tests/fromcb.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/fromcb.c b/tests/fromcb.c index 5a48e1c..d5cee12 100644 --- a/tests/fromcb.c +++ b/tests/fromcb.c @@ -61,7 +61,7 @@ static int32_t read_data_func( struct fromcb_testinfo *test = (void *)user_data; struct fromcb_chunkinfo* chunk; int32_t retvalue; - int chunk_size = 0; + size_t chunk_size = 0; (void)parser; chunk = &test->chunk[test->chunknum]; if(chunk->str == NULL) { @@ -73,36 +73,36 @@ static int32_t read_data_func( return 0; } - chunk_size = chunk->size; - if(chunk_size == CHUNK_SIZE_STRLEN) { - chunk_size = (int)strlen(chunk->str); + if(chunk->size == CHUNK_SIZE_STRLEN) { + chunk_size = strlen(chunk->str); if(chunk_size != 0) memmove(data, chunk->str, chunk_size); - } else if(chunk_size == CHUNK_SIZE_STRLENPAD) { + } else if(chunk->size == CHUNK_SIZE_STRLENPAD) { size_t padendlen = 0; - chunk_size = (int)strlen(chunk->str); + chunk_size = strlen(chunk->str); if(chunk_size != 0) memmove(data, chunk->str, chunk_size); /* Pad it */ if(chunk->padend) padendlen = strlen(chunk->padend); - if((size_t)chunk_size + padendlen < len) { - size_t i, padlen = len - (size_t)chunk_size - padendlen; + if(chunk_size + padendlen < len) { + size_t i, padlen = len - chunk_size - padendlen; char padchar = ' '; for(i=0; ipadend) { memmove(data+len-padendlen, chunk->padend, padendlen); } - chunk_size = (int)len; + chunk_size = len; } else { + chunk_size = (size_t)chunk->size; if(chunk_size != 0) memmove(data, chunk->str, chunk_size); } - *outlen = (size_t)chunk_size; + *outlen = chunk_size; retvalue = chunk->retvalue; test->chunknum++; if(verb) From 67e788383903d6b2f68992b161d460213c7f6e93 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 13 Jan 2026 13:55:11 +0100 Subject: [PATCH 17/23] - integration-unbound-changes, add fromcb tests. --- tests/fromcb.c | 72 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 71 insertions(+), 1 deletion(-) diff --git a/tests/fromcb.c b/tests/fromcb.c index d5cee12..b2e0e30 100644 --- a/tests/fromcb.c +++ b/tests/fromcb.c @@ -16,7 +16,7 @@ /* max number of chunks used in the test */ #define MAX_CHUNKS 16 /* if test should print verbosely */ -static int verb = 1; +static int verb = 0; /* The size of the chunk, if set to this value, makes it use strlen. */ #define CHUNK_SIZE_STRLEN -3 @@ -151,6 +151,75 @@ void test_fromcb(void **state) {NULL, 0, NULL, 0} }, 0, 0, 2 // num rrs + }, + /* fromcb test 1: one chunk */ + { 0, { + {"www.example.com. IN A 1.2.3.4\n" + "www2.example.com. IN A 1.2.3.4\n" + , CHUNK_SIZE_STRLEN, NULL, 0}, + {NULL, 0, NULL, 0} + }, + 0, 0, 2 // num rrs + }, + /* fromcb test 2: four chunks */ + { 0, { + {"www.example.com. IN A 1.2.3.4\n", CHUNK_SIZE_STRLENPAD, "\n", 0}, + {"www2.example.com. IN A 1.2.3.4\n", CHUNK_SIZE_STRLENPAD, "\n", 0}, + {"www3.example.com. IN A 1.2.3.4\n", CHUNK_SIZE_STRLENPAD, "\n", 0}, + {"www4.example.com. IN A 1.2.3.4\n", CHUNK_SIZE_STRLEN, NULL, 0}, + {NULL, 0, NULL, 0} + }, + 0, 0, 4 // num rrs + }, + /* fromcb test 3: three chunks, it ends with a full chunk, not partial. */ + { 0, { + {"www.example.com. IN A 1.2.3.4\n", CHUNK_SIZE_STRLENPAD, "\n", 0}, + {"www2.example.com. IN A 1.2.3.4\n", CHUNK_SIZE_STRLENPAD, "\n", 0}, + {"www3.example.com. IN A 1.2.3.4\n", CHUNK_SIZE_STRLENPAD, "\n", 0}, + {NULL, 0, NULL, 0} + }, + 0, 0, 3 // num rrs + }, + /* fromcb test 4: three chunks, last has error. */ + { ZONE_READ_ERROR, { + {"www.example.com. IN A 1.2.3.4\n", CHUNK_SIZE_STRLENPAD, "\n", 0}, + {"www2.example.com. IN A 1.2.3.4\n", CHUNK_SIZE_STRLENPAD, "\n", 0}, + {"www3.example.com. IN A 1.2.3.4\n", CHUNK_SIZE_STRLEN, NULL, + ZONE_READ_ERROR}, + {NULL, 0, NULL, 0} + }, + 0, 0, 2 // num rrs + }, + /* fromcb test 5: three chunks, last has error, it is memory error. */ + { ZONE_OUT_OF_MEMORY, { + {"www.example.com. IN A 1.2.3.4\n", CHUNK_SIZE_STRLENPAD, "\n", 0}, + {"www2.example.com. IN A 1.2.3.4\n", CHUNK_SIZE_STRLENPAD, "\n", 0}, + {"www3.example.com. IN A 1.2.3.4\n", CHUNK_SIZE_STRLEN, NULL, + ZONE_OUT_OF_MEMORY}, + {NULL, 0, NULL, 0} + }, + 0, 0, 2 // num rrs + }, + /* fromcb test 6: first chunk has error. */ + { ZONE_READ_ERROR, { + {"www.example.com. IN A 1.2.3.4\n", CHUNK_SIZE_STRLENPAD, "\n", + ZONE_READ_ERROR}, + {"www2.example.com. IN A 1.2.3.4\n", CHUNK_SIZE_STRLEN, NULL, 0}, + {NULL, 0, NULL, 0} + }, + 0, 0, 0 // num rrs + }, + /* fromcb test 7: four chunks, one RR is spread over two chunks. */ + { 0, { + {"www.example.com. IN A 1.2.3.4\n", CHUNK_SIZE_STRLENPAD, "\n", 0}, + {"www2.example.com. IN A 1.2.3.4\n", CHUNK_SIZE_STRLENPAD, "\n" + "www-part.example", 0}, + {".com. IN A 1.2.3.4\n" + "www3.example.com. IN A 1.2.3.4\n", CHUNK_SIZE_STRLENPAD, "\n", 0}, + {"www4.example.com. IN A 1.2.3.4\n", CHUNK_SIZE_STRLEN, NULL, 0}, + {NULL, 0, NULL, 0} + }, + 0, 0, 5 // num rrs } }; @@ -165,6 +234,7 @@ void test_fromcb(void **state) int32_t code; struct fromcb_testinfo *test = &tests[i]; + if(verb) fprintf(stderr, "\n"); fprintf(stderr, "fromcb test %d\n", (int)i); memset(&options, 0, sizeof(options)); From 1a974d653747eae9ff2cad328cfb9bb137fe091d Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 13 Jan 2026 13:59:57 +0100 Subject: [PATCH 18/23] - integration-unbound-changes, add fromcb test with syntax error in string. --- tests/fromcb.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/fromcb.c b/tests/fromcb.c index b2e0e30..36a5c35 100644 --- a/tests/fromcb.c +++ b/tests/fromcb.c @@ -220,6 +220,16 @@ void test_fromcb(void **state) {NULL, 0, NULL, 0} }, 0, 0, 5 // num rrs + }, + /* fromcb test 8: four chunks, syntax error in string. */ + { ZONE_SYNTAX_ERROR, { + {"www.example.com. IN A 1.2.3.4\n", CHUNK_SIZE_STRLENPAD, "\n", 0}, + {"www2.example.com. syntax-error-here 1.2.3.4\n", CHUNK_SIZE_STRLENPAD, "\n", 0}, + {"www3.example.com. IN A 1.2.3.4\n", CHUNK_SIZE_STRLENPAD, "\n", 0}, + {"www4.example.com. IN A 1.2.3.4\n", CHUNK_SIZE_STRLEN, NULL, 0}, + {NULL, 0, NULL, 0} + }, + 0, 0, 1 // num rrs } }; From dcab6b137885d58046d6dfc7a1da25074930987e Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 13 Jan 2026 15:18:45 +0100 Subject: [PATCH 19/23] - integration-unbound-changes, set name and path for from callback files. --- src/zone.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/zone.c b/src/zone.c index 7184983..f106ca8 100644 --- a/src/zone.c +++ b/src/zone.c @@ -480,8 +480,6 @@ int32_t zone_parse_from_callback( file = parser->file; initialize_file(parser, file); file->handle = NULL; - file->path = NULL; - file->name = NULL; if (!(file->buffer.data = malloc(size))) return (void)close_file(parser, file), ZONE_OUT_OF_MEMORY; file->buffer.data[0] = '\0'; From f95433583384e1b7680d76be0a55f5770927aea2 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 16 Jan 2026 13:42:19 +0100 Subject: [PATCH 20/23] - integration-unbound-changes, add chrootdir to options, for the chrootdir adjustment. --- include/zone.h | 6 ++++++ src/generic/format.h | 9 +++++++++ src/zone.c | 3 ++- 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/include/zone.h b/include/zone.h index 516e153..dd5004f 100644 --- a/include/zone.h +++ b/include/zone.h @@ -459,6 +459,12 @@ typedef struct { bool no_includes; /** Maximum $INCLUDE depth. 0 for default. */ uint32_t include_limit; + /** The chroot path. If not NULL, and the $INCLUDE pathname has it as prefix, + * then it is elided from the beginning of the pathname string. Setting it + * to the chroot directory, when chrooted, makes that absolute path names + * continue to work, both before the chroot and after the chroot with + * adjustment. */ + const char* chrootdir; /** Enable 1h2m3s notations for TTLS. */ bool pretty_ttls; /** Origin in wire format. */ diff --git a/src/generic/format.h b/src/generic/format.h index 76542c2..5db3c53 100644 --- a/src/generic/format.h +++ b/src/generic/format.h @@ -254,6 +254,15 @@ static really_inline int32_t parse_dollar_include( file_t *file; if ((code = take_quoted_or_contiguous(parser, &include, &fields[0], token)) < 0) return code; + if (parser->options.chrootdir) { + size_t chrootlen = strlen(parser->options.chrootdir); + if (token->length >= chrootlen && + strncmp(token->data, parser->options.chrootdir, chrootlen) == 0) { + /* Adjust for chroot on include file. */ + token->data += chrootlen; + token->length -= chrootlen; + } + } if ((code = zone_open_file(parser, token->data, token->length, &file)) < 0) return code; diff --git a/src/zone.c b/src/zone.c index f106ca8..ff57ed1 100644 --- a/src/zone.c +++ b/src/zone.c @@ -240,7 +240,8 @@ static int32_t open_file( file->path = NULL; if (!(file->name = malloc(length + 1))) return ZONE_OUT_OF_MEMORY; - memcpy(file->name, include, length); + if(length > 0) + memcpy(file->name, include, length); file->name[length] = '\0'; if (!(file->buffer.data = malloc(size))) return (void)close_file(parser, file), ZONE_OUT_OF_MEMORY; From a9c77ddf35d7643c5f54f9ac58e81518211b6624 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 16 Jan 2026 14:08:11 +0100 Subject: [PATCH 21/23] - integration-unbound-changes, add include chrootdir test. --- tests/include.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/tests/include.c b/tests/include.c index f29b9bd..8a4077c 100644 --- a/tests/include.c +++ b/tests/include.c @@ -721,3 +721,76 @@ diagnostic_pop() free(paths[0]); free(paths[1]); } + +/*!cmocka */ +void include_with_chroot(void **state) +{ + // test parse of $INCLUDE with chrootdir option. + // the chrootdir is set to a prefix, that is then included + // at the frong of the $INCLUDE path, without actually chrooting, + // to test it. + const char* chrootdir = "/var/simdtest/"; + // an actual chrootdir may be more "/var/simdtest" and thus leave + // a string starting with '/', which is fine for the chrootdir case. + // for the test it is convenient to remove that beginning '/'. + char *paths[2] = { NULL, NULL }; + FILE *handles[2] = { NULL, NULL }; + struct file_list list = { 0, 2, paths }; + (void)state; + + static const char fmt[] = + "$INCLUDE \"%s%s\"\n" + "example A 192.0.2.1\n"; + +diagnostic_push() +msvc_diagnostic_ignored(4996) + paths[0] = get_tempnam(NULL, "zone"); + assert_non_null(paths[0]); + handles[0] = fopen(paths[0], "wb"); + assert_non_null(handles[0]); + paths[1] = get_tempnam(NULL, "zone"); + assert_non_null(paths[1]); + handles[1] = fopen(paths[1], "wb"); + assert_non_null(handles[1]); +diagnostic_pop() + + fprintf(handles[0], fmt, chrootdir, paths[1]); + (void)fflush(handles[0]); + (void)fclose(handles[0]); + fputs("example A 192.0.2.1\n", handles[1]); + (void)fflush(handles[1]); + (void)fclose(handles[1]); + + char buf[128]; + int len = snprintf(buf, sizeof(buf), fmt, chrootdir, paths[0]); + assert_true(len > 0); + + char *str = calloc((size_t)len + ZONE_BLOCK_SIZE + 1, 1); + assert_non_null(str); + (void)snprintf(str, (size_t)len+1, fmt, chrootdir, paths[0]); + + zone_parser_t parser; + zone_options_t options; + zone_name_buffer_t name; + zone_rdata_buffer_t rdata; + zone_buffers_t buffers = { 1, &name, &rdata }; + + memset(&options, 0, sizeof(options)); + options.include.callback = &track_include_cb; + options.accept.callback = &track_accept_cb; + options.origin.octets = origin; + options.origin.length = sizeof(origin); + options.default_ttl = 3600; + options.default_class = 1; + options.chrootdir = chrootdir; + + int32_t code = zone_parse_string(&parser, &options, &buffers, str, (size_t)len, &list); + remove(paths[0]); + remove(paths[1]); + assert_int_equal(code, ZONE_SUCCESS); + assert_int_equal(list.index, 2); + + free(str); + free(paths[0]); + free(paths[1]); +} From eca67807d02c74d1f01edbf7b3bc3475e56bf4d6 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 22 Jan 2026 14:45:22 +0100 Subject: [PATCH 22/23] - integration-unbound-changes, add fromcb test for eof straight away. --- tests/fromcb.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/fromcb.c b/tests/fromcb.c index 36a5c35..e4ce212 100644 --- a/tests/fromcb.c +++ b/tests/fromcb.c @@ -230,6 +230,13 @@ void test_fromcb(void **state) {NULL, 0, NULL, 0} }, 0, 0, 1 // num rrs + }, + /* fromcb test 9: eof straight away, much like an empty file. */ + { 0, { + {"", CHUNK_SIZE_STRLEN, NULL, 0}, + {NULL, 0, NULL, 0} + }, + 0, 0, 0 // num rrs } }; From 249e6f82d434dd70b337192a5be89a8cc5b3d2ed Mon Sep 17 00:00:00 2001 From: Wouter Wijngaards Date: Thu, 12 Feb 2026 11:03:07 +0100 Subject: [PATCH 23/23] Apply suggestions from code review Co-authored-by: Jannik Peters --- include/zone.h | 2 +- tests/include.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/zone.h b/include/zone.h index dd5004f..df47f80 100644 --- a/include/zone.h +++ b/include/zone.h @@ -431,7 +431,7 @@ typedef int32_t(*zone_include_t)( /** * @brief Signature of callback function invoked to read data pieces. * - * Called to read another piece of the data. Read up the number of bytes, + * Called to read another piece of the data. Read up to the number of bytes, * or less bytes up to the end, and signal end of file, or error. */ typedef int32_t(*zone_read_data_t)( diff --git a/tests/include.c b/tests/include.c index 8a4077c..27ebd8c 100644 --- a/tests/include.c +++ b/tests/include.c @@ -727,7 +727,7 @@ void include_with_chroot(void **state) { // test parse of $INCLUDE with chrootdir option. // the chrootdir is set to a prefix, that is then included - // at the frong of the $INCLUDE path, without actually chrooting, + // at the front of the $INCLUDE path, without actually chrooting, // to test it. const char* chrootdir = "/var/simdtest/"; // an actual chrootdir may be more "/var/simdtest" and thus leave